Skip to content

Commit

Permalink
fix(swingset): add GC stubs: syscall/dispatch retireImports/Exports
Browse files Browse the repository at this point in the history
This add `syscall.retireImports` and `syscall.retireExports` to the
pre-existing `syscall.dropImports`. All three are no-ops, but vats should be
able to call them without ill effects.

It also adds `dispatch.retireExports` and `dispatch.retireImports` to the
pre-existing `dispatch.dropExports` stub. These are tolerated but ignored by
both liveslots and the comms vat

Together, this should allow kernel and liveslots GC development to proceed in
parallel.

refs #2724
  • Loading branch information
warner committed Apr 26, 2021
1 parent 5e659e6 commit 113da2d
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 21 deletions.
19 changes: 16 additions & 3 deletions packages/SwingSet/src/kernel/kernelSyscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,18 @@ export function makeKernelSyscallHandler(tools) {
return OKNULL;
}

function retireImports(koids) {
assert(Array.isArray(koids), X`retireImports given non-Array ${koids}`);
console.log(`-- kernel ignoring retireImports ${koids.join(',')}`);
return OKNULL;
}

function retireExports(koids) {
assert(Array.isArray(koids), X`retireExports given non-Array ${koids}`);
console.log(`-- kernel ignoring retireExports ${koids.join(',')}`);
return OKNULL;
}

function doKernelSyscall(ksc) {
const [type, ...args] = ksc;
switch (type) {
Expand All @@ -140,16 +152,17 @@ export function makeKernelSyscallHandler(tools) {
return vatstoreDelete(...args);
case 'dropImports':
return dropImports(...args);
case 'retireImports':
return retireImports(...args);
case 'retireExports':
return retireExports(...args);
default:
assert.fail(X`unknown vatSyscall type ${type}`);
}
}

const kernelSyscallHandler = harden({
send, // TODO remove these individual ones
invoke,
subscribe,
resolve,
doKernelSyscall,
});
return kernelSyscallHandler;
Expand Down
24 changes: 24 additions & 0 deletions packages/SwingSet/src/kernel/liveSlots.js
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,20 @@ function build(
console.log(`-- liveslots ignoring dropExports`);
}

function retireExports(vrefs) {
assert(Array.isArray(vrefs));
vrefs.map(vref => insistVatType('object', vref));
vrefs.map(vref => assert(parseVatSlot(vref).allocatedByVat));
console.log(`-- liveslots ignoring retireExports`);
}

function retireImports(vrefs) {
assert(Array.isArray(vrefs));
vrefs.map(vref => insistVatType('object', vref));
vrefs.map(vref => assert(!parseVatSlot(vref).allocatedByVat));
console.log(`-- liveslots ignoring retireImports`);
}

// TODO: when we add notifyForward, guard against cycles

function exitVat(completion) {
Expand Down Expand Up @@ -821,6 +835,16 @@ function build(
dropExports(vrefs);
break;
}
case 'retireExports': {
const [vrefs] = args;
retireExports(vrefs);
break;
}
case 'retireImports': {
const [vrefs] = args;
retireImports(vrefs);
break;
}
default:
assert.fail(X`unknown delivery type ${type}`);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/SwingSet/src/kernel/vatManager/supervisor-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ function makeSupervisorSyscall(syscallToManager, workerCanBlock) {
resolve: resolutions => doSyscall(['resolve', resolutions]),
exit: (isFailure, data) => doSyscall(['exit', isFailure, data]),
dropImports: vrefs => doSyscall(['dropImports', vrefs]),
retireImports: vrefs => doSyscall(['retireImports', vrefs]),
retireExports: vrefs => doSyscall(['retireExports', vrefs]),

// These syscalls should be omitted if the worker cannot get a
// synchronous return value back from the kernel, such as when the worker
Expand Down
41 changes: 34 additions & 7 deletions packages/SwingSet/src/kernel/vatTranslator.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,20 +185,43 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) {
}

function translateDropImports(vrefs) {
assert(Array.isArray(vrefs), X`dropImport() given non-Array ${vrefs}`);
// We delete clist entries as we translate, which will (TODO) decref the
// krefs. When we're done with that loop, we hand the set of krefs to
// kernelSyscall so it can (TODO) check newly-decremented refcounts
// against zero, and maybe delete even more.
assert(Array.isArray(vrefs), X`dropImports() given non-Array ${vrefs}`);
// TODO: dropImports does not affect the c-list, but makes the kernel
// calculate the refcount of a kref, possibly causing further action.
const krefs = vrefs.map(vref => {
insistVatType('object', vref);
insistVatType('object', vref); // TODO: probably device nodes too
const kref = mapVatSlotToKernelSlot(vref);
vatKeeper.deleteCListEntry(kref, vref);
return kref;
});
return harden(['dropImports', krefs]);
}

function translateRetireImports(vrefs) {
assert(Array.isArray(vrefs), X`retireImports() given non-Array ${vrefs}`);
// TODO: retireImports will delete clist entries as we translate, which
// will (TODO) decref the krefs. When we're done with that loop, we hand
// the set of krefs to kernelSyscall so it can (TODO) check
// newly-decremented refcounts against zero, and maybe delete even more.
const krefs = vrefs.map(vref => {
insistVatType('object', vref); // TODO: probably device nodes too
const kref = mapVatSlotToKernelSlot(vref);
// vatKeeper.deleteCListEntry(kref, vref);
return kref;
});
return harden(['retireImports', krefs]);
}

function translateRetireExports(vrefs) {
assert(Array.isArray(vrefs), X`retireExports() given non-Array ${vrefs}`);
// TODO: not sure how the kernel should react to this yet
const krefs = vrefs.map(vref => {
insistVatType('object', vref); // TODO: probably device nodes too
const kref = mapVatSlotToKernelSlot(vref);
return kref;
});
return harden(['retireExports', krefs]);
}

function translateCallNow(target, method, args) {
insistCapData(args);
const dev = mapVatSlotToKernelSlot(target);
Expand Down Expand Up @@ -276,6 +299,10 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) {
return translateVatstoreDelete(...args);
case 'dropImports':
return translateDropImports(...args);
case 'retireImports':
return translateRetireImports(...args);
case 'retireExports':
return translateRetireExports(...args);
default:
assert.fail(X`unknown vatSyscall type ${type}`);
}
Expand Down
8 changes: 6 additions & 2 deletions packages/SwingSet/src/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ export function insistVatDeliveryObject(vdo) {
}
break;
}
case 'dropExports': {
case 'dropExports':
case 'retireExports':
case 'retireImports': {
const [slots] = rest;
assert(Array.isArray(slots));
for (const slot of slots) {
Expand Down Expand Up @@ -150,7 +152,9 @@ export function insistVatSyscallObject(vso) {
assert.typeof(key, 'string');
break;
}
case 'dropImports': {
case 'dropImports':
case 'retireImports':
case 'retireExports': {
const [slots] = rest;
assert(Array.isArray(slots));
for (const slot of slots) {
Expand Down
9 changes: 8 additions & 1 deletion packages/SwingSet/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@
* @typedef { [tag: 'message', target: string, msg: Message]} VatDeliveryMessage
* @typedef { [tag: 'notify', resolutions: string[] ]} VatDeliveryNotify
* @typedef { [tag: 'dropExports', vrefs: string[] ]} VatDeliveryDropExports
* @typedef { VatDeliveryMessage | VatDeliveryNotify | VatDeliveryDropExports } VatDeliveryObject
* @typedef { [tag: 'retireExports', vrefs: string[] ]} VatDeliveryRetireExports
* @typedef { [tag: 'retireImports', vrefs: string[] ]} VatDeliveryRetireImports
* @typedef { VatDeliveryMessage | VatDeliveryNotify | VatDeliveryDropExports
* | VatDeliveryRetireExports | VatDeliveryRetireImports
* } VatDeliveryObject
* @typedef { [tag: 'ok', message: null, usage: unknown] | [tag: 'error', message: string, usage: unknown | null] } VatDeliveryResult
*
* @typedef { [tag: 'send', target: string, msg: Message] } VatSyscallSend
Expand All @@ -82,10 +86,13 @@
* @typedef { [tag: 'vatstoreSet', key: string, data: string ]} VatSyscallVatstoreSet
* @typedef { [tag: 'vatstoreDelete', key: string ]} VatSyscallVatstoreDelete
* @typedef { [tag: 'dropImports', slots: string[] ]} VatSyscallDropImports
* @typedef { [tag: 'retireImports', slots: string[] ]} VatSyscallRetireImports
* @typedef { [tag: 'retireExports', slots: string[] ]} VatSyscallRetireExports
*
* @typedef { VatSyscallSend | VatSyscallCallNow | VatSyscallSubscribe
* | VatSyscallResolve | VatSyscallVatstoreGet | VatSyscallVatstoreSet
* | VatSyscallVatstoreDelete | VatSyscallDropImports
* | VatSyscallRetireImports | VatSyscallRetireExports
* } VatSyscallObject
*
* @typedef { [tag: 'ok', data: SwingSetCapData | string | null ]} VatSyscallResultOk
Expand Down
24 changes: 24 additions & 0 deletions packages/SwingSet/src/vats/comms/dispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@ export function buildCommsDispatch(
console.log(`-- comms ignoring dropExports`);
}

function retireExports(vrefs) {
assert(Array.isArray(vrefs));
vrefs.map(vref => insistVatType('object', vref));
vrefs.map(vref => assert(parseVatSlot(vref).allocatedByVat));
console.log(`-- comms ignoring retireExports`);
}

function retireImports(vrefs) {
assert(Array.isArray(vrefs));
vrefs.map(vref => insistVatType('object', vref));
vrefs.map(vref => assert(!parseVatSlot(vref).allocatedByVat));
console.log(`-- comms ignoring retireImports`);
}

function dispatch(vatDeliveryObject) {
const [type, ...args] = vatDeliveryObject;
switch (type) {
Expand All @@ -147,6 +161,16 @@ export function buildCommsDispatch(
dropExports(vrefs);
return;
}
case 'retireExports': {
const [vrefs] = args;
retireExports(vrefs);
break;
}
case 'retireImports': {
const [vrefs] = args;
retireImports(vrefs);
break;
}
default:
assert.fail(X`unknown delivery type ${type}`);
}
Expand Down
6 changes: 6 additions & 0 deletions packages/SwingSet/test/liveslots-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export function buildSyscall() {
dropImports(slots) {
log.push({ type: 'dropImports', slots });
},
retireImports(slots) {
log.push({ type: 'retireImports', slots });
},
retireExports(slots) {
log.push({ type: 'retireExports', slots });
},
exit(isFailure, info) {
log.push({ type: 'exit', isFailure, info });
},
Expand Down
19 changes: 17 additions & 2 deletions packages/SwingSet/test/test-comms.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { flipRemoteSlot } from '../src/vats/comms/parseRemoteSlot';
import { makeState } from '../src/vats/comms/state';
import { makeCListKit } from '../src/vats/comms/clist';
import { debugState } from '../src/vats/comms/dispatch';
import { makeMessage, makeDropExports } from './util';
import {
makeMessage,
makeDropExports,
makeRetireExports,
makeRetireImports,
} from './util';
import { commsVatDriver } from './commsVatDriver';

test('provideRemoteForLocal', t => {
Expand Down Expand Up @@ -229,8 +234,18 @@ test('receive', t => {
{ message: /unexpected recv seqNum .*/ },
);

// make sure comms can tolerate dropExports, even if it's a no-op
// make sure comms can tolerate GC operations, even if they're a no-op
dispatch(makeDropExports(expectedAliceKernel, expectedAyanaKernel));
dispatch(makeRetireExports(expectedAliceKernel, expectedAyanaKernel));
// Sending retireImport into a vat that hasn't yet emitted dropImport is
// rude, and would only happen if the exporter unilaterally revoked the
// object's identity. Normally the kernel would only send retireImport
// after receiving dropImport (and sending a dropExport into the exporter,
// and getting a retireExport from the exporter, gracefully terminating the
// object's identity). We do it the rude way because it's good enough to
// test the comms vat can tolerate it, but we may have to update this when
// we implement retireImport for real.
dispatch(makeRetireImports(bobKernel));
});

// This tests the various pathways through the comms vat driver. This has the
Expand Down
28 changes: 22 additions & 6 deletions packages/SwingSet/test/test-liveslots.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
makeResolve,
makeReject,
makeDropExports,
makeRetireExports,
makeRetireImports,
} from './util';

test('calls', async t => {
Expand Down Expand Up @@ -630,13 +632,13 @@ test('disavow', async t => {
t.deepEqual(log, []);
});

test('dropExports', async t => {
test('GC operations', async t => {
const { log, syscall } = buildSyscall();

function build(_vatPowers) {
const ex1 = Far('export', {});
const root = Far('root', {
one() {
one(_arg) {
return ex1;
},
});
Expand All @@ -645,11 +647,12 @@ test('dropExports', async t => {
const dispatch = makeDispatch(syscall, build, 'vatA', true);
t.deepEqual(log, []);
const rootA = 'o+0';
const arg = 'o-1';

// rp1 = root~.one()
// rp1 = root~.one(arg)
// ex1 = await rp1
const rp1 = 'p-1';
dispatch(makeMessage(rootA, 'one', capargs([]), rp1));
dispatch(makeMessage(rootA, 'one', capargsOneSlot(arg), rp1));
await waitUntilQuiescent();
const l1 = log.shift();
const ex1 = l1.resolutions[0][2].slots[0];
Expand All @@ -659,9 +662,22 @@ test('dropExports', async t => {
});
t.deepEqual(log, []);

// now tell the vat to drop that export
dispatch(makeDropExports(ex1));
// now tell the vat we don't need a strong reference to that export
// for now, all that we care about is that liveslots doesn't crash
dispatch(makeDropExports(ex1));

// and release its identity too
dispatch(makeRetireExports(ex1));

// Sending retireImport into a vat that hasn't yet emitted dropImport is
// rude, and would only happen if the exporter unilaterally revoked the
// object's identity. Normally the kernel would only send retireImport
// after receiving dropImport (and sending a dropExport into the exporter,
// and getting a retireExport from the exporter, gracefully terminating the
// object's identity). We do it the rude way because it's good enough to
// test that liveslots can tolerate it, but we may have to update this when
// we implement retireImport for real.
dispatch(makeRetireImports(arg));
});

// Create a WeakRef/FinalizationRegistry pair that can be manipulated for
Expand Down
10 changes: 10 additions & 0 deletions packages/SwingSet/test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,13 @@ export function makeDropExports(...vrefs) {
const vatDeliverObject = harden(['dropExports', vrefs]);
return vatDeliverObject;
}

export function makeRetireExports(...vrefs) {
const vatDeliverObject = harden(['retireExports', vrefs]);
return vatDeliverObject;
}

export function makeRetireImports(...vrefs) {
const vatDeliverObject = harden(['retireImports', vrefs]);
return vatDeliverObject;
}

0 comments on commit 113da2d

Please sign in to comment.