Skip to content

Commit

Permalink
refactor(swingset): replace queueToExport to queueToKref
Browse files Browse the repository at this point in the history
The internal `kernel.queueToExport(vatID, vref, method, args)` method will
inject a message onto the run-queue from outside of any particular vat. It is
used by `initializeKernel.js` to enqueue the bootstrap message, and by the
vatAdmin device to notify the vatAdmin vat about vats finishing creation (and
being terminated). To specify the target, you give it a `vatID` and a vref,
and it pretends that that vat had exported the vref, returning the kref to
the caller.

This change replaces `queueToExport` with `queueToKref(kref, method, args)`,
which does not perform the pretend vat export. Instead, it requires the
caller to provide a pre-translated kref. You should use it in conjunction
with `kernel.addExport`, which does the same pretend export that
`queueToExport` used to do.

The benefit to splitting this up is that we want better control over the GC
reference count of the target. `addExport` may add an artificial "reachable"
count, to keep the target alive. But `queueToKref` will not.

To support this, we must stash the kref of the vatAdminRoot in the DB during
initialization (just after the vatAdmin vat is defined), so it will be
available without the (vatNameToID, `addExport`) lookup at runtime. Instead,
the kernel loads this kref from the DB during startup, and passes it through
to the vat loader which needs it. A handful of vatLoader endowments are no
longer necessary.

(note that storing this kref is optional, in that unit tests which don't
bother to define any vats won't have it, but any real usage of swingset will
require it to be in the DB as soon as the first dynamic vat is created)

The 'policy' argument tells the kernel what to do if/when the
automatically-added result promise is resolved. I added a `none` mode to
prevent the creation of a result promise at all, which should help declutter
a few unit tests.

We're slowly migrating our terminology from "kernelSlot" and "vatSlot" to
"kref" and "vref". I updated the functions that were touched in this work.
  • Loading branch information
warner committed May 30, 2021
1 parent fbe665b commit 94c1688
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 98 deletions.
10 changes: 2 additions & 8 deletions packages/SwingSet/src/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,14 +365,8 @@ export async function makeSwingsetController(
parseVatSlot(exportID);
assert.typeof(method, 'string');
insistCapData(args);
kernel.addExport(vatID, exportID);
const kpid = kernel.queueToExport(
vatID,
exportID,
method,
args,
resultPolicy,
);
const kref = kernel.addExport(vatID, exportID);
const kpid = kernel.queueToKref(kref, method, args, resultPolicy);
kernel.kpRegisterInterest(kpid);
return kpid;
},
Expand Down
25 changes: 19 additions & 6 deletions packages/SwingSet/src/kernel/initializeKernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { makeVatSlot } from '../parseVatSlots';
import { insistStorageAPI } from '../storageAPI';
import { wrapStorage } from './state/storageWrapper';
import makeKernelKeeper from './state/kernelKeeper';
import { doQueueToExport } from './kernel';
import { doAddExport, doQueueToKref } from './kernel';

function makeVatRootObjectSlot() {
return makeVatSlot('object', true, 0);
Expand All @@ -33,6 +33,8 @@ export function initializeKernel(config, hostStorage, verbose = false) {
}
}

let gotVatAdminRootKref;

// generate the genesis vats
if (config.vats) {
for (const name of Object.keys(config.vats)) {
Expand Down Expand Up @@ -76,7 +78,18 @@ export function initializeKernel(config, hostStorage, verbose = false) {
logStartup(`assigned VatID ${vatID} for genesis vat ${name}`);
const vatKeeper = kernelKeeper.allocateVatKeeper(vatID);
vatKeeper.setSourceAndOptions({ bundle, bundleName }, creationOptions);
if (name === 'vatAdmin') {
// Create a kref for the vatAdmin root, so the kernel can tell it
// about creation/termination of dynamic vats. doAddExport will
// increment the refcount so it won't go away, despite being
// unreferenced by any other vat.
const rootVref = makeVatRootObjectSlot();
const kref = doAddExport(kernelKeeper, vatID, rootVref);
kvStore.set('vatAdminRootKref', kref);
gotVatAdminRootKref = true;
}
}
assert(gotVatAdminRootKref, X`a vat admin vat is required`);
}

// generate the devices
Expand Down Expand Up @@ -177,12 +190,12 @@ export function initializeKernel(config, hostStorage, verbose = false) {
errorIdNum: 60000,
});
const args = harden([vatObj0s, deviceObj0s]);
// queueToExport() takes kernel-refs (ko+NN, kd+NN) in s.slots
const rootSlot = makeVatRootObjectSlot();
const resultKpid = doQueueToExport(
// doQueueToKref() takes kernel-refs (ko+NN, kd+NN) in s.slots
const rootVref = makeVatRootObjectSlot();
const rootKref = doAddExport(kernelKeeper, bootstrapVatID, rootVref);
const resultKpid = doQueueToKref(
kernelKeeper,
bootstrapVatID,
rootSlot,
rootKref,
'bootstrap',
m.serialize(args),
'panic',
Expand Down
89 changes: 43 additions & 46 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,48 +40,51 @@ function makeError(message, name = 'Error') {

const VAT_TERMINATION_ERROR = makeError('vat terminated');

function doAddExport(kernelKeeper, fromVatID, vatSlot) {
/*
* Pretend that a vat just exported an object
*/
export function doAddExport(kernelKeeper, fromVatID, vref) {
insistVatID(fromVatID);
assert(parseVatSlot(vatSlot).allocatedByVat);
assert(parseVatSlot(vref).allocatedByVat);
const vatKeeper = kernelKeeper.getVatKeeper(fromVatID);
return vatKeeper.mapVatSlotToKernelSlot(vatSlot);
const kref = vatKeeper.mapVatSlotToKernelSlot(vref);
return kref;
}

/**
* Enqueue a message to some object exported by a vat, as if the message had
* been sent by some other vat.
* Enqueue a message to some kernel object, as if the message had been sent
* by some other vat. This requires a kref as a target, so use e.g.
* doAddExport to acquire one and increment its refcount to keep it alive.
*
* @param {*} kernelKeeper Kernel keeper managing persistent kernel state
* @param {string} vatID The vat to which the message is to be delivered
* @param {string} vatSlot That vat's ID for the object to deliver to
* @param {string} kref Target of the message
* @param {string} method The message verb
* @param {*} args The message arguments
* @param {string} policy How the kernel should handle an eventual resolution or
* rejection of the message's result promise. Should be one of 'ignore' (do
* nothing), 'logAlways' (log the resolution or rejection), 'logFailure' (log
* only rejections), or 'panic' (panic the kernel upon a rejection).
* @param {string} policy How the kernel should handle an eventual resolution
* or rejection of the message's result promise. Should be one of
* 'sendOnly' (don't even create a result promise), 'ignore' (do nothing),
* 'logAlways' (log the resolution or rejection), 'logFailure' (log only
* rejections), or 'panic' (panic the kernel upon a rejection).
*
* @returns {string} the kpid of the sent message's result promise
* @returns {string?} the kpid of the sent message's result promise
*/
export function doQueueToExport(
export function doQueueToKref(
kernelKeeper,
vatID,
vatSlot,
kref,
method,
args,
policy = 'ignore',
) {
// queue a message on the end of the queue, with 'absolute' kernelSlots.
// queue a message on the end of the queue, with 'absolute' krefs.
// Use 'step' or 'run' to execute it
insistVatID(vatID);
parseVatSlot(vatSlot);
insistCapData(args);
args.slots.forEach(s => parseKernelSlot(s));

const resultKPID = kernelKeeper.addKernelPromise(policy);
let resultKPID;
if (policy !== 'none') {
resultKPID = kernelKeeper.addKernelPromise(policy);
}
const msg = harden({ method, args, result: resultKPID });
const kernelSlot = doAddExport(kernelKeeper, vatID, vatSlot);
doSend(kernelKeeper, kernelSlot, msg);
doSend(kernelKeeper, kref, msg);
return resultKPID;
}

Expand Down Expand Up @@ -114,6 +117,7 @@ export default function buildKernel(
const { kvStore, streamStore } = hostStorage;
insistStorageAPI(kvStore);
const { enhancedCrankBuffer, abortCrank, commitCrank } = wrapStorage(kvStore);
const vatAdminRootKref = kvStore.get('vatAdminRootKref');

const kernelSlog = writeSlogObject
? makeSlogger(slogCallbacks, writeSlogObject)
Expand Down Expand Up @@ -153,7 +157,7 @@ export default function buildKernel(
// This is a low-level output-only string logger used by old unit tests to
// see whether vats made progress or not. The array it appends to is
// available as c.dump().log . New unit tests should instead use the
// 'result' value returned by c.queueToExport()
// 'result' value returned by c.queueToKref()
function testLog(...args) {
const rendered = args.map(arg =>
typeof arg === 'string' ? arg : JSON.stringify(arg, abbreviateReplacer),
Expand Down Expand Up @@ -254,23 +258,23 @@ export default function buildKernel(
}

/**
* Enqueue a message to some object exported by a vat, as if the message had
* been sent by some other vat.
* Enqueue a message to some kernel object, as if the message had been sent
* by some other vat.
*
* @param {string} vatID The vat to which the message is to be delivered
* @param {string} vatSlot That vat's ID for the object to deliver to
* @param {string} kref Target of the message
* @param {string} method The message verb
* @param {*} args The message arguments
* @param {string} policy How the kernel should handle an eventual resolution
* or rejection of the message's result promise. Should be one of 'ignore'
* (do nothing), 'logAlways' (log the resolution or rejection),
* 'logFailure' (log only rejections), or 'panic' (panic the kernel upon a
* @param {string} policy How the kernel should handle an eventual
* resolution or rejection of the message's result promise. Should be
* one of 'sendOnly' (don't even create a result promise), 'ignore' (do
* nothing), 'logAlways' (log the resolution or rejection), 'logFailure'
* (log only rejections), or 'panic' (panic the kernel upon a
* rejection).
*
* @returns {string} the kpid of the sent message's result promise
* @returns {string?} the kpid of the sent message's result promise, if any
*/
function queueToExport(vatID, vatSlot, method, args, policy = 'ignore') {
return doQueueToExport(kernelKeeper, vatID, vatSlot, method, args, policy);
function queueToKref(kref, method, args, policy = 'ignore') {
return doQueueToKref(kernelKeeper, kref, method, args, policy);
}

function notify(vatID, kpid) {
Expand Down Expand Up @@ -518,6 +522,7 @@ export default function buildKernel(
}

async function processCreateVat(message) {
assert(vatAdminRootKref, `initializeKernel did not set vatAdminRootKref`);
const { vatID, source, dynamicOptions } = message;
kernelKeeper.addDynamicVatID(vatID);
const vatKeeper = kernelKeeper.allocateVatKeeper(vatID);
Expand Down Expand Up @@ -550,15 +555,7 @@ export default function buildKernel(
}

function sendResponse(args) {
const vatAdminVatId = vatNameToID('vatAdmin');
const vatAdminRootObjectSlot = makeVatRootObjectSlot();
queueToExport(
vatAdminVatId,
vatAdminRootObjectSlot,
'newVatCallback',
args,
'logFailure',
);
queueToKref(vatAdminRootKref, 'newVatCallback', args, 'logFailure');
}

// eslint-disable-next-line no-use-before-define
Expand Down Expand Up @@ -754,15 +751,15 @@ export default function buildKernel(
recreateStaticVat,
loadTestVat,
} = makeVatLoader({
vatNameToID,
vatManagerFactory,
kernelSlog,
makeVatConsole,
addVatManager,
queueToExport,
queueToKref,
kernelKeeper,
panic,
buildVatSyscallHandler,
vatAdminRootKref,
});

/**
Expand Down Expand Up @@ -1069,7 +1066,7 @@ export default function buildKernel(
addExport,
vatNameToID,
deviceNameToID,
queueToExport,
queueToKref,
kpRegisterInterest,
kpStatus,
kpResolution,
Expand Down
15 changes: 4 additions & 11 deletions packages/SwingSet/src/kernel/loadVat.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ export function makeVatRootObjectSlot() {

export function makeVatLoader(stuff) {
const {
vatNameToID,
vatManagerFactory,
kernelSlog,
makeVatConsole,
addVatManager,
queueToExport,
queueToKref,
kernelKeeper,
panic,
buildVatSyscallHandler,
vatAdminRootKref,
} = stuff;

/**
Expand All @@ -32,6 +32,7 @@ export function makeVatLoader(stuff) {
* @returns {Promise<void>} The vatID of the newly created vat
*/
function createVatDynamically(vatID, source, dynamicOptions = {}) {
assert(vatAdminRootKref, `initializeKernel did not set vatAdminRootKref`);
// eslint-disable-next-line no-use-before-define
return create(vatID, source, dynamicOptions, true);
}
Expand Down Expand Up @@ -175,8 +176,6 @@ export function makeVatLoader(stuff) {
return;
}
terminated = true;
const vatAdminVatId = vatNameToID('vatAdmin');
const vatAdminRootObjectSlot = makeVatRootObjectSlot();

// Embedding the info capdata into the arguments list, taking advantage of
// the fact that neither vatID (which is a string) nor shouldReject (which
Expand All @@ -186,13 +185,7 @@ export function makeVatLoader(stuff) {
slots: info.slots,
};

queueToExport(
vatAdminVatId,
vatAdminRootObjectSlot,
'vatTerminated',
args,
'logFailure',
);
queueToKref(vatAdminRootKref, 'vatTerminated', args, 'logFailure');
}

kernelSlog.addVat(
Expand Down
19 changes: 11 additions & 8 deletions packages/SwingSet/test/test-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ async function simpleCall(t) {
{ vatID: vattpVatID, state: { transcript: [] } },
{ vatID: timerVatID, state: { transcript: [] } },
]);
t.deepEqual(data.kernelTable, []);
// the vatAdmin root is pre-registered
const vatAdminRoot = ['ko20', adminVatID, 'o+0'];
t.deepEqual(data.kernelTable, [vatAdminRoot]);

// vat1:o+1 will map to ko21
controller.queueToVatExport('vat1', 'o+1', 'foo', capdata('args'));
t.deepEqual(controller.dump().runQueue, [
{
Expand All @@ -71,7 +74,7 @@ async function simpleCall(t) {
args: capdata('args'),
result: 'kp40',
},
target: 'ko20',
target: 'ko21',
type: 'send',
},
]);
Expand Down Expand Up @@ -175,12 +178,12 @@ test('bootstrap export', async t => {
// better test, probably by sorting the list of vats by their vatID, and
// then asserting that their root objects are assigned `koNN` numbers in
// matching order.
const boot0 = 'ko20';
const comms0 = 'ko21';
const left0 = 'ko22';
const right0 = 'ko23';
const timer0 = 'ko24';
const vatAdminSvc = 'ko25';
const vatAdminSvc = 'ko20';
const boot0 = 'ko21';
const comms0 = 'ko22';
const left0 = 'ko23';
const right0 = 'ko24';
const timer0 = 'ko25';
const vattp0 = 'ko26';
const adminDev = 'kd30';
const kt = [
Expand Down
Loading

0 comments on commit 94c1688

Please sign in to comment.