From f6c17af1b541ffc8d33876e8275e0309ed866562 Mon Sep 17 00:00:00 2001 From: Chip Morningstar Date: Fri, 18 Dec 2020 15:23:37 -0800 Subject: [PATCH] feat: retire C-list entries for resolved promises --- packages/SwingSet/src/kernel/cleanup.js | 77 +++++++++++++--- packages/SwingSet/src/kernel/kernel.js | 11 ++- packages/SwingSet/src/kernel/liveSlots.js | 41 +++++---- .../SwingSet/src/kernel/state/vatKeeper.js | 12 +++ .../SwingSet/src/kernel/vatManager/deliver.js | 6 +- .../kernel/vatManager/nodeWorkerSupervisor.js | 6 +- .../kernel/vatManager/subprocessSupervisor.js | 6 +- packages/SwingSet/src/kernel/vatTranslator.js | 29 ++++-- packages/SwingSet/src/vats/comms/dispatch.js | 92 ++++++++++--------- packages/SwingSet/test/test-kernel.js | 46 ++++------ packages/SwingSet/test/test-liveslots.js | 45 +++------ packages/SwingSet/test/test-promises.js | 4 +- packages/SwingSet/test/test-vpid-kernel.js | 75 ++++++--------- packages/SwingSet/test/test-vpid-liveslots.js | 68 ++++---------- packages/SwingSet/test/util.js | 4 +- 15 files changed, 264 insertions(+), 258 deletions(-) diff --git a/packages/SwingSet/src/kernel/cleanup.js b/packages/SwingSet/src/kernel/cleanup.js index 1285cc2fbd5f..4ff0d81646c5 100644 --- a/packages/SwingSet/src/kernel/cleanup.js +++ b/packages/SwingSet/src/kernel/cleanup.js @@ -1,4 +1,4 @@ -import { kdebug } from './kdebug'; +// import { kdebug } from './kdebug'; import { parseKernelSlot } from './parseKernelSlots'; // XXX temporary flags to control features during development @@ -18,45 +18,94 @@ export function deleteCListEntryIfEasy( function scanKernelPromise(scanKPID, scanKernelData) { visited.add(scanKPID); - kdebug(`@@@ scan ${scanKPID}`); + // kdebug(`@@@ scan ${scanKPID} ${JSON.stringify(scanKernelData)}`); if (scanKernelData) { for (const slot of scanKernelData.slots) { const { type } = parseKernelSlot(slot); if (type === 'promise') { sawPromise = slot; if (visited.has(slot)) { - kdebug(`@@@ ${slot} previously visited`); + // kdebug(`@@@ ${slot} previously visited`); return true; } else { - const { data, state } = kernelKeeper.getKernelPromise(slot); + const { data } = kernelKeeper.getKernelPromise(slot); + // const { data, state } = kernelKeeper.getKernelPromise(slot); if (data) { if (scanKernelPromise(slot, data)) { - kdebug(`@@@ scan ${slot} detects circularity`); + // kdebug(`@@@ scan ${slot} detects circularity`); return true; } } else { - kdebug(`@@@ scan ${slot} state = ${state}`); + // kdebug(`@@@ scan ${slot} state = ${state}`); } } } } } - kdebug(`@@@ scan ${scanKPID} detects no circularity`); + // kdebug(`@@@ scan ${scanKPID} detects no circularity`); return false; } - kdebug(`@@ checking ${vatID} ${kpid} for circularity`); + // kdebug(`@@ checking ${vatID} ${kpid} for circularity`); if (scanKernelPromise(kpid, kernelData)) { - kdebug( - `Unable to delete ${vatID} clist entry ${kpid}<=>${vpid} because it is indirectly self-referential`, - ); + // kdebug( + // `Unable to delete ${vatID} clist entry ${kpid}<=>${vpid} because it is indirectly self-referential`, + // ); return; } else if (sawPromise) { - kdebug( - `Unable to delete ${vatID} clist entry ${kpid}<=>${vpid} because there was a contained promise ${sawPromise}`, - ); + // kdebug( + // `Unable to delete ${vatID} clist entry ${kpid}<=>${vpid} because there was a contained promise ${sawPromise}`, + // ); return; } } vatKeeper.deleteCListEntry(kpid, vpid); } + +export function getKpidsToRetire( + vatID, + vatKeeper, + kernelKeeper, + rootKPID, + rootKernelData, +) { + const seen = new Set(); + function scanKernelPromise(kpid, kernelData) { + // kdebug(`### scanning ${kpid} ${JSON.stringify(kernelData)}`); + if (vatKeeper.hasCListEntry(kpid)) { + // kdebug(`## adding ${kpid} to scan results`); + seen.add(kpid); + if (kernelData) { + for (const slot of kernelData.slots) { + const { type } = parseKernelSlot(slot); + // kdebug(`## examine ${kpid} slot ${slot}`); + if (type === 'promise') { + if (!seen.has(slot)) { + const kp = kernelKeeper.getKernelPromise(slot); + const { data, state } = kp; + // kdebug(`## state of ${slot} is: ${JSON.stringify(kp)}`); + if (state !== 'unresolved') { + if (data) { + scanKernelPromise(slot, data); + } + } else { + // kdebug(`## ${slot} is still unresolved`); + } + } else { + // kdebug(`## ${slot} previously seen`); + } + } else { + // kdebug(`## ${slot} is not a promise`); + } + } + } else { + // kdebug(`## ${kpid} has no data`); + } + } else { + // kdebug(`## ${kpid} has no c-list entry for ${vatID}`); + } + } + + scanKernelPromise(rootKPID, rootKernelData); + return Array.from(seen); +} diff --git a/packages/SwingSet/src/kernel/kernel.js b/packages/SwingSet/src/kernel/kernel.js index a0462e19bc1e..7d55deb8e394 100644 --- a/packages/SwingSet/src/kernel/kernel.js +++ b/packages/SwingSet/src/kernel/kernel.js @@ -472,7 +472,16 @@ export default function buildKernel( kernelKeeper.incStat(statNameForNotify(p.state)); const kd = harden(['notify', kpid, p]); const vd = vat.translators.kernelDeliveryToVatDelivery(kd); - await deliverAndLogToVat(vatID, kd, vd); + if (vd) { + await deliverAndLogToVat(vatID, kd, vd); + + const resolutions = vd[1]; + const vatKeeper = kernelKeeper.getVatKeeper(vatID); + for (const vpid of Object.keys(resolutions)) { + const kpidToDelete = vatKeeper.mapVatSlotToKernelSlot(vpid); + vatKeeper.deleteCListEntry(kpidToDelete, vpid); + } + } } } diff --git a/packages/SwingSet/src/kernel/liveSlots.js b/packages/SwingSet/src/kernel/liveSlots.js index 9abefb42466d..07c5097e0a41 100644 --- a/packages/SwingSet/src/kernel/liveSlots.js +++ b/packages/SwingSet/src/kernel/liveSlots.js @@ -509,30 +509,33 @@ function build(syscall, forVatID, cacheSize, vatPowers, vatParameters) { }; } - function notify(primaryPromiseID, resolutions) { + function notifyOnePromise(promiseID, rejected, data) { + insistCapData(data); + lsdebug( + `ls.dispatch.notify(${promiseID}, ${rejected}, ${data.body}, [${data.slots}])`, + ); + insistVatType('promise', promiseID); + // TODO: insist that we do not have decider authority for promiseID + if (!importedPromisesByPromiseID.has(promiseID)) { + throw new Error(`unknown promiseID '${promiseID}'`); + } + const pRec = importedPromisesByPromiseID.get(promiseID); + const val = m.unserialize(data); + if (rejected) { + pRec.reject(val); + } else { + pRec.resolve(val); + } + } + + function notify(resolutions) { assert(didRoot); for (const vpid of Object.keys(resolutions)) { const vp = resolutions[vpid]; - insistCapData(vp.data); - lsdebug( - `ls.dispatch.notify(${primaryPromiseID} ${vpid}, ${vp.rejected}, ${vp.data.body}, [${vp.data.slots}])`, - ); - insistVatType('promise', vpid); - // TODO: insist that we do not have decider authority for promiseID - if (!importedPromisesByPromiseID.has(vpid)) { - throw new Error(`unknown promiseID '${vpid}'`); - } - const pRec = importedPromisesByPromiseID.get(vpid); - const val = m.unserialize(vp.data); - if (vp.rejected) { - pRec.reject(val); - } else { - pRec.resolve(val); - } + notifyOnePromise(vpid, vp.rejected, vp.data); } for (const vpid of Object.keys(resolutions)) { - const vp = resolutions[vpid]; - retirePromiseIDIfEasy(vpid, vp.data); + retirePromiseID(vpid); } } diff --git a/packages/SwingSet/src/kernel/state/vatKeeper.js b/packages/SwingSet/src/kernel/state/vatKeeper.js index 6cf996e74d52..cff8737fd1b6 100644 --- a/packages/SwingSet/src/kernel/state/vatKeeper.js +++ b/packages/SwingSet/src/kernel/state/vatKeeper.js @@ -179,6 +179,17 @@ export function makeVatKeeper( return storage.get(kernelKey); } + /** + * Test if there's a c-list entry for some slot. + * + * @param {string} slot The slot of interest + * + * @returns {boolean} true iff this vat has a c-list entry mapping for `slot`. + */ + function hasCListEntry(slot) { + return storage.has(`${vatID}.c.${slot}`); + } + /** * Remove an entry from the vat's c-list. * @@ -271,6 +282,7 @@ export function makeVatKeeper( getSourceAndOptions, mapVatSlotToKernelSlot, mapKernelSlotToVatSlot, + hasCListEntry, deleteCListEntry, getTranscript, addToTranscript, diff --git a/packages/SwingSet/src/kernel/vatManager/deliver.js b/packages/SwingSet/src/kernel/vatManager/deliver.js index 3ed01c8aa709..e3b9c4296ca3 100644 --- a/packages/SwingSet/src/kernel/vatManager/deliver.js +++ b/packages/SwingSet/src/kernel/vatManager/deliver.js @@ -81,9 +81,9 @@ export function makeDeliver(tools, dispatch) { ); } - async function deliverOneNotification(primaryVpid, resolutions) { - const errmsg = `vat[${vatID}].promise[${primaryVpid}] failed`; - return doProcess(['notify', primaryVpid, resolutions], errmsg); + async function deliverOneNotification(resolutions) { + const errmsg = `vat[${vatID}].notify failed`; + return doProcess(['notify', resolutions], errmsg); } // vatDeliverObject is: diff --git a/packages/SwingSet/src/kernel/vatManager/nodeWorkerSupervisor.js b/packages/SwingSet/src/kernel/vatManager/nodeWorkerSupervisor.js index 98bf41f556e8..267d8167d802 100644 --- a/packages/SwingSet/src/kernel/vatManager/nodeWorkerSupervisor.js +++ b/packages/SwingSet/src/kernel/vatManager/nodeWorkerSupervisor.js @@ -59,9 +59,9 @@ function doMessage(targetSlot, msg) { ); } -function doNotify(primaryVpid, resolutions) { - const errmsg = `vat.promise[${primaryVpid}] failed`; - return doProcess(['notify', primaryVpid, resolutions], errmsg); +function doNotify(resolutions) { + const errmsg = `vat.notify failed`; + return doProcess(['notify', resolutions], errmsg); } parentPort.on('message', ([type, ...margs]) => { diff --git a/packages/SwingSet/src/kernel/vatManager/subprocessSupervisor.js b/packages/SwingSet/src/kernel/vatManager/subprocessSupervisor.js index ada40a8576bf..33e6c00f2fd5 100644 --- a/packages/SwingSet/src/kernel/vatManager/subprocessSupervisor.js +++ b/packages/SwingSet/src/kernel/vatManager/subprocessSupervisor.js @@ -59,9 +59,9 @@ function doMessage(targetSlot, msg) { ); } -function doNotify(primaryVpid, resolutions) { - const errmsg = `vat.promise[${primaryVpid}] failed`; - return doProcess(['notify', primaryVpid, resolutions], errmsg); +function doNotify(resolutions) { + const errmsg = `vat.notify failed`; + return doProcess(['notify', resolutions], errmsg); } const toParent = arrayEncoderStream(); diff --git a/packages/SwingSet/src/kernel/vatTranslator.js b/packages/SwingSet/src/kernel/vatTranslator.js index 31895eb223fb..84ed36e55cf1 100644 --- a/packages/SwingSet/src/kernel/vatTranslator.js +++ b/packages/SwingSet/src/kernel/vatTranslator.js @@ -4,7 +4,7 @@ import { insistKernelType, parseKernelSlot } from './parseKernelSlots'; import { insistVatType, parseVatSlot } from '../parseVatSlots'; import { insistCapData } from '../capdata'; import { kdebug, legibilizeMessageArgs, legibilizeValue } from './kdebug'; -import { deleteCListEntryIfEasy } from './cleanup'; +import { deleteCListEntryIfEasy, getKpidsToRetire } from './cleanup'; /* * Return a function that converts KernelDelivery objects into VatDelivery @@ -79,12 +79,27 @@ function makeTranslateKernelDeliveryToVatDelivery(vatID, kernelKeeper) { function translateNotify(kpid, kp) { assert(kp.state !== 'unresolved', details`spurious notification ${kpid}`); - const vpid = mapKernelSlotToVatSlot(kpid); const resolutions = {}; - resolutions[vpid] = translatePromiseDescriptor(kp); - deleteCListEntryIfEasy(vatID, vatKeeper, kernelKeeper, kpid, vpid, kp.data); - const vatDelivery = harden(['notify', vpid, resolutions]); - return vatDelivery; + kdebug(`notify ${kpid} ${JSON.stringify(kp)}`); + const targets = getKpidsToRetire( + vatID, + vatKeeper, + kernelKeeper, + kpid, + kp.data, + ); + if (targets.length > 0) { + for (const toResolve of targets) { + const p = kernelKeeper.getKernelPromise(toResolve); + const vpid = mapKernelSlotToVatSlot(toResolve); + resolutions[vpid] = translatePromiseDescriptor(p); + } + const vatDelivery = harden(['notify', resolutions]); + return vatDelivery; + } else { + kdebug(`skipping notify of ${kpid} because it's already been done`); + return null; + } } function kernelDeliveryToVatDelivery(kd) { @@ -97,7 +112,7 @@ function makeTranslateKernelDeliveryToVatDelivery(vatID, kernelKeeper) { default: throw Error(`unknown kernelDelivery.type ${type}`); } - // returns ['message', target, msg] or ['notify', vpid, vp] + // returns ['message', target, msg] or ['notify', resolutions] or null } return kernelDeliveryToVatDelivery; diff --git a/packages/SwingSet/src/vats/comms/dispatch.js b/packages/SwingSet/src/vats/comms/dispatch.js index 29f5e738f52b..f2f6de62ceaf 100644 --- a/packages/SwingSet/src/vats/comms/dispatch.js +++ b/packages/SwingSet/src/vats/comms/dispatch.js @@ -73,56 +73,60 @@ export function buildCommsDispatch(syscall) { throw Error(`unknown target ${target}`); } - function notify(primaryPromiseID, resolutions) { + function notifyOnePromise(promiseID, rejected, data) { + insistCapData(data); + // console.debug(`comms.notifyOnePromise(${promiseID}, ${rejected}, ${data})`); // dumpState(state); - for (const vpid of Object.keys(resolutions)) { - const vp = resolutions[vpid]; - insistCapData(vp.data); - // console.debug(`comms.notify(${primaryPromiseID}, ${vpid} ${vp.rejected}, ${vp.data})`); - - // I *think* we should never get here for local promises, since the - // controller only does sendOnly. But if we change that, we need to catch - // locally-generated promises and deal with them. - // if (vpid in localPromises) { - // resolveLocal(vpid, { type: 'data', vp.data }); - // } - - // todo: if we previously held resolution authority for this promise, then - // transferred it to some local vat, we'll have subscribed to the kernel - // to hear about it. If we then get the authority back again, we no longer - // want to hear about its resolution (since we're the ones doing the - // resolving), but the kernel still thinks of us as subscribing, so we'll - // get a bogus dispatch.notify. Currently we throw an error, which is - // currently ignored but might prompt a vat shutdown in the future. - - // TODO: The following goofiness, namely taking apart the capdata object - // and looking at it to see if it's a single presence reference and then - // treating it in a special way if it is, is a consequence of an impedance - // mismatch that has grown up between the comms protocol and the - // evolutionary path that the kernel/vat interface has taken. In the - // future we should clean this up and unify presences and data in the - // comms protocol the way the kernel/vat interface has. - const unser = JSON.parse(vp.data.body); - let resolution; - if (vp.rejected) { - resolution = harden({ type: 'reject', data: vp.data }); - } else if ( - Object(unser) === unser && - QCLASS in unser && - unser[QCLASS] === 'slot' - ) { - const slot = vp.data.slots[unser.index]; - insistVatType('object', slot); - resolution = harden({ type: 'object', slot }); - } else { - resolution = harden({ type: 'data', data: vp.data }); - } - resolveFromKernel(vpid, resolution); + + // I *think* we should never get here for local promises, since the + // controller only does sendOnly. But if we change that, we need to catch + // locally-generated promises and deal with them. + // if (promiseID in localPromises) { + // resolveLocal(promiseID, { type: 'data', data }); + // } + + // todo: if we previously held resolution authority for this promise, then + // transferred it to some local vat, we'll have subscribed to the kernel + // to hear about it. If we then get the authority back again, we no longer + // want to hear about its resolution (since we're the ones doing the + // resolving), but the kernel still thinks of us as subscribing, so we'll + // get a bogus dispatch.notify. Currently we throw an error, which is + // currently ignored but might prompt a vat shutdown in the future. + + // TODO: The following goofiness, namely taking apart the capdata object + // and looking at it to see if it's a single presence reference and then + // treating it in a special way if it is, is a consequence of an impedance + // mismatch that has grown up between the comms protocol and the + // evolutionary path that the kernel/vat interface has taken. In the + // future we should clean this up and unify presences and data in the + // comms protocol the way the kernel/vat interface has. + const unser = JSON.parse(data.body); + let resolution; + if (rejected) { + resolution = harden({ type: 'reject', data }); + } else if ( + Object(unser) === unser && + QCLASS in unser && + unser[QCLASS] === 'slot' + ) { + const slot = data.slots[unser.index]; + insistVatType('object', slot); + resolution = harden({ type: 'object', slot }); + } else { + resolution = harden({ type: 'data', data }); } + resolveFromKernel(promiseID, resolution); // XXX question: do we need to call retirePromiseIDIfEasy (or some special // comms vat version of it) here? } + function notify(resolutions) { + for (const vpid of Object.keys(resolutions)) { + const vp = resolutions[vpid]; + notifyOnePromise(vpid, vp.rejected, vp.data); + } + } + const dispatch = harden({ deliver, notify }); debugState.set(dispatch, { state, clistKit }); diff --git a/packages/SwingSet/test/test-kernel.js b/packages/SwingSet/test/test-kernel.js index a9d99139536e..5b212fb982c0 100644 --- a/packages/SwingSet/test/test-kernel.js +++ b/packages/SwingSet/test/test-kernel.js @@ -16,6 +16,10 @@ function capdata(body, slots = []) { return harden({ body, slots }); } +function oneResolution(promiseID, rejected, data) { + return { [promiseID]: { rejected, data } }; +} + function checkPromises(t, kernel, expected) { // extract the kernel promise table and assert that the contents match the // expected list. This sorts on the promise ID, then does a t.deepEqual @@ -724,8 +728,8 @@ test('promise resolveToData', async t => { function setupA(s) { syscallA = s; function deliver() {} - function notify(promiseID, resolutions) { - log.push(['notify', promiseID, resolutions]); + function notify(resolutions) { + log.push(['notify', resolutions]); } return { deliver, notify }; } @@ -779,13 +783,7 @@ test('promise resolveToData', async t => { // the kernelPromiseID gets mapped back to the vat PromiseID t.deepEqual(log.shift(), [ 'notify', - pForA, - { - [pForA]: { - rejected: false, - data: capdata('args', ['o-50']), - }, - }, + oneResolution(pForA, false, capdata('args', ['o-50'])), ]); t.deepEqual(log, []); // no other dispatch calls if (!RETIRE_KPIDS) { @@ -810,8 +808,8 @@ test('promise resolveToPresence', async t => { function setupA(s) { syscallA = s; function deliver() {} - function notify(promiseID, resolutions) { - log.push(['notify', promiseID, resolutions]); + function notify(resolutions) { + log.push(['notify', resolutions]); } return { deliver, notify }; } @@ -869,16 +867,10 @@ test('promise resolveToPresence', async t => { await kernel.step(); t.deepEqual(log.shift(), [ 'notify', - pForA, - { - [pForA]: { - rejected: false, - data: { - body: '{"@qclass":"slot","index":0}', - slots: [bobForA], - }, - }, - }, + oneResolution(pForA, false, { + body: '{"@qclass":"slot","index":0}', + slots: [bobForA], + }), ]); t.deepEqual(log, []); // no other dispatch calls if (!RETIRE_KPIDS) { @@ -903,8 +895,8 @@ test('promise reject', async t => { function setupA(s) { syscallA = s; function deliver() {} - function notify(promiseID, resolutions) { - log.push(['notify', promiseID, resolutions]); + function notify(resolutions) { + log.push(['notify', resolutions]); } return { deliver, notify }; } @@ -958,13 +950,7 @@ test('promise reject', async t => { // the kernelPromiseID gets mapped back to the vat PromiseID t.deepEqual(log.shift(), [ 'notify', - pForA, - { - [pForA]: { - rejected: true, - data: capdata('args', ['o-50']), - }, - }, + oneResolution(pForA, true, capdata('args', ['o-50'])), ]); t.deepEqual(log, []); // no other dispatch calls if (!RETIRE_KPIDS) { diff --git a/packages/SwingSet/test/test-liveslots.js b/packages/SwingSet/test/test-liveslots.js index 8a89d71e08d2..5ca29b8b94c6 100644 --- a/packages/SwingSet/test/test-liveslots.js +++ b/packages/SwingSet/test/test-liveslots.js @@ -13,6 +13,10 @@ function capargs(args, slots = []) { return capdata(JSON.stringify(args), slots); } +function oneResolution(promiseID, rejected, data) { + return { [promiseID]: { rejected, data } }; +} + function buildSyscall() { const log = []; @@ -89,7 +93,7 @@ test('calls', async t => { t.deepEqual(log.shift(), { type: 'subscribe', target: 'p-1' }); t.deepEqual(log.shift(), 'two true'); - dispatch.notify('p-1', { + dispatch.notify({ 'p-1': { rejected: false, data: capargs('result'), @@ -112,7 +116,7 @@ test('calls', async t => { t.deepEqual(log.shift(), { type: 'subscribe', target: 'p-2' }); t.deepEqual(log.shift(), 'two true'); - dispatch.notify('p-2', { + dispatch.notify({ 'p-2': { rejected: true, data: capargs('rejection'), @@ -228,12 +232,7 @@ test('liveslots pipeline/non-pipeline calls', async t => { t.deepEqual(log, []); // now we tell it the promise has resolved, to object 'o2' - dispatch.notify(p1, { - [p1]: { - rejected: false, - data: capargs(slot0arg, [o2]), - }, - }); + dispatch.notify(oneResolution(p1, false, capargs(slot0arg, [o2]))); await waitUntilQuiescent(); // this allows E(o2).nonpipe2() to go out, which was not pipelined t.deepEqual(log.shift(), { @@ -441,26 +440,13 @@ async function doResultPromise(t, mode) { // resolve p1 first. The one() call was already pipelined, so this // should not trigger any new syscalls. if (mode === 'to presence') { - dispatch.notify(expectedP1, { - [expectedP1]: { - rejected: false, - data: capargs(slot0arg, [target2]), - }, - }); + dispatch.notify( + oneResolution(expectedP1, false, capargs(slot0arg, [target2])), + ); } else if (mode === 'to data') { - dispatch.notify(expectedP1, { - [expectedP1]: { - rejected: false, - data: capargs(4, []), - }, - }); + dispatch.notify(oneResolution(expectedP1, false, capargs(4, []))); } else if (mode === 'reject') { - dispatch.notify(expectedP1, { - [expectedP1]: { - rejected: true, - data: capargs('error', []), - }, - }); + dispatch.notify(oneResolution(expectedP1, true, capargs('error', []))); } else { throw Error(`unknown mode ${mode}`); } @@ -468,12 +454,7 @@ async function doResultPromise(t, mode) { t.deepEqual(log, []); // Now we resolve p2, allowing the second two() to proceed - dispatch.notify(expectedP2, { - [expectedP2]: { - rejected: false, - data: capargs(4, []), - }, - }); + dispatch.notify(oneResolution(expectedP2, false, capargs(4, []))); await waitUntilQuiescent(); if (mode === 'to presence') { diff --git a/packages/SwingSet/test/test-promises.js b/packages/SwingSet/test/test-promises.js index d2a698260946..e4f04e2e31aa 100644 --- a/packages/SwingSet/test/test-promises.js +++ b/packages/SwingSet/test/test-promises.js @@ -148,7 +148,7 @@ test('circular promise resolution data', async t => { { id: 'kp41', state: 'fulfilledToData', - refCount: 3, + refCount: 2, data: { body: '[{"@qclass":"slot","index":0}]', slots: ['kp42'], @@ -157,7 +157,7 @@ test('circular promise resolution data', async t => { { id: 'kp42', state: 'fulfilledToData', - refCount: 3, + refCount: 2, data: { body: '[{"@qclass":"slot","index":0}]', slots: ['kp41'], diff --git a/packages/SwingSet/test/test-vpid-kernel.js b/packages/SwingSet/test/test-vpid-kernel.js index 94d2ba01c82e..9f5da0b5b232 100644 --- a/packages/SwingSet/test/test-vpid-kernel.js +++ b/packages/SwingSet/test/test-vpid-kernel.js @@ -20,6 +20,10 @@ function capargs(args, slots = []) { return capdata(JSON.stringify(args), slots); } +function oneResolution(promiseID, rejected, data) { + return { [promiseID]: { rejected, data } }; +} + function makeConsole(tag) { const log = anylogger(tag); const cons = {}; @@ -131,68 +135,48 @@ function resolutionOf(vpid, mode, targets) { case 'presence': return { type: 'notify', - primaryPromiseID: vpid, - resolutions: { - [vpid]: { - rejected: false, - data: capargs(slot0arg, [targets.target2]), - }, - }, + resolutions: oneResolution( + vpid, + false, + capargs(slot0arg, [targets.target2]), + ), }; case 'local-object': return { type: 'notify', - primaryPromiseID: vpid, - resolutions: { - [vpid]: { - rejected: false, - data: capargs(slot0arg, [targets.localTarget]), - }, - }, + resolutions: oneResolution( + vpid, + false, + capargs(slot0arg, [targets.localTarget]), + ), }; case 'data': return { type: 'notify', - primaryPromiseID: vpid, - resolutions: { - [vpid]: { - rejected: false, - data: capargs(4, []), - }, - }, + resolutions: oneResolution(vpid, false, capargs(4, [])), }; case 'promise-data': return { type: 'notify', - primaryPromiseID: vpid, - resolutions: { - [vpid]: { - rejected: false, - data: capargs([slot0arg], [targets.p1]), - }, - }, + resolutions: oneResolution( + vpid, + false, + capargs([slot0arg], [targets.p1]), + ), }; case 'reject': return { type: 'notify', - primaryPromiseID: vpid, - resolutions: { - [vpid]: { - rejected: true, - data: capargs('error', []), - }, - }, + resolutions: oneResolution(vpid, true, capargs('error', [])), }; case 'promise-reject': return { type: 'notify', - primaryPromiseID: vpid, - resolutions: { - [vpid]: { - rejected: true, - data: capargs([slot0arg], [targets.p1]), - }, - }, + resolutions: oneResolution( + vpid, + true, + capargs([slot0arg], [targets.p1]), + ), }; default: throw Error(`unknown mode ${mode}`); @@ -456,8 +440,7 @@ async function doTest4567(t, which, mode) { let p1VatA; let p1VatB; - const expectRetirement = - RETIRE_VPIDS && mode !== 'promise-data' && mode !== 'promise-reject'; + const expectRetirement = RETIRE_VPIDS; if (which === 4) { // 4: Alice receives a promise from Bob, which is then resolved @@ -575,7 +558,7 @@ async function doTest4567(t, which, mode) { }; onDispatchCallback = function odc1(d) { t.deepEqual(d, resolutionOf(p1VatA, mode, targetsA)); - t.is(inCList(kernel, vatA, p1kernel, p1VatA), !expectRetirement); + t.is(inCList(kernel, vatA, p1kernel, p1VatA), expectRetirement); }; const targetsB = { target2: rootAvatB, @@ -595,7 +578,7 @@ async function doTest4567(t, which, mode) { t.is(clistKernelToVat(kernel, vatA, p1kernel), undefined); t.is(clistVatToKernel(kernel, vatA, p1VatA), undefined); } else { - t.is(inCList(kernel, vatA, p1kernel, p1VatA), true); + t.is(inCList(kernel, vatA, p1kernel, p1VatA), false); t.is(clistKernelToVat(kernel, vatA, p1kernel), p1VatA); t.is(clistVatToKernel(kernel, vatA, p1VatA), p1kernel); } diff --git a/packages/SwingSet/test/test-vpid-liveslots.js b/packages/SwingSet/test/test-vpid-liveslots.js index 2f3843cc4914..cca5bfcf6b30 100644 --- a/packages/SwingSet/test/test-vpid-liveslots.js +++ b/packages/SwingSet/test/test-vpid-liveslots.js @@ -19,6 +19,10 @@ function capargs(args, slots = []) { return capdata(JSON.stringify(args), slots); } +function oneResolution(promiseID, rejected, data) { + return { [promiseID]: { rejected, data } }; +} + function buildSyscall() { const log = []; @@ -664,77 +668,38 @@ async function doVatResolveCase4(t, mode) { t.deepEqual(log, []); if (mode === 'presence') { - dispatch.notify(p1, { - [p1]: { - rejected: false, - data: capargs(slot0arg, [target2]), - }, - }); + dispatch.notify(oneResolution(p1, false, capargs(slot0arg, [target2]))); } else if (mode === 'local-object') { - dispatch.notify(p1, { - [p1]: { - rejected: false, - data: capargs(slot0arg, [rootA]), - }, - }); + dispatch.notify(oneResolution(p1, false, capargs(slot0arg, [rootA]))); } else if (mode === 'data') { - dispatch.notify(p1, { - [p1]: { - rejected: false, - data: capargs(4, []), - }, - }); + dispatch.notify(oneResolution(p1, false, capargs(4, []))); } else if (mode === 'promise-data') { - dispatch.notify(p1, { - [p1]: { - rejected: false, - data: capargs([slot0arg], [p1]), - }, - }); + dispatch.notify(oneResolution(p1, false, capargs([slot0arg], [p1]))); } else if (mode === 'reject') { - dispatch.notify(p1, { - [p1]: { - rejected: true, - data: capargs('error', []), - }, - }); + dispatch.notify(oneResolution(p1, true, capargs('error', []))); } else if (mode === 'promise-reject') { - dispatch.notify(p1, { - [p1]: { - rejected: true, - data: capargs([slot0arg], [p1]), - }, - }); + dispatch.notify(oneResolution(p1, true, capargs([slot0arg], [p1]))); } else { throw Error(`unknown mode ${mode}`); } await endOfCrank(); t.deepEqual(log, []); - const expectRetirement = - RETIRE_VPIDS && mode !== 'promise-data' && mode !== 'promise-reject'; - dispatch.deliver(rootA, 'second', capargs([slot0arg], [target1])); await endOfCrank(); const expectedP4 = nextP(); const expectedP5 = nextP(); - let expectedThreeArg = p1; - let expectedResultOfThree = expectedP4; - if (expectRetirement) { - expectedThreeArg = expectedP4; - expectedResultOfThree = expectedP5; - } t.deepEqual(log.shift(), { type: 'send', targetSlot: target1, method: 'three', - args: capargs([slot0arg], [expectedThreeArg]), - resultSlot: expectedResultOfThree, + args: capargs([slot0arg], [expectedP4]), + resultSlot: expectedP5, }); t.deepEqual(log.shift(), { type: 'subscribe', - target: expectedResultOfThree, + target: expectedP5, }); if (mode === 'presence') { @@ -748,10 +713,9 @@ async function doVatResolveCase4(t, mode) { }); t.deepEqual(log.shift(), { type: 'subscribe', target: expectedP6 }); } - if (expectRetirement) { - const targets = { target2, localTarget: rootA, p1 }; - t.deepEqual(log.shift(), resolutionOf(expectedP4, mode, targets)); - } + + const targets = { target2, localTarget: rootA, p1: expectedP4 }; + t.deepEqual(log.shift(), resolutionOf(expectedP4, mode, targets)); // if p1 rejects or resolves to data, the kernel never hears about four() t.deepEqual(log, []); diff --git a/packages/SwingSet/test/util.js b/packages/SwingSet/test/util.js index 98bf149dc6f3..32844981d157 100644 --- a/packages/SwingSet/test/util.js +++ b/packages/SwingSet/test/util.js @@ -44,8 +44,8 @@ export function buildDispatch(onDispatchCallback = undefined) { onDispatchCallback(d); } }, - notify(primaryPromiseID, resolutions) { - const d = { type: 'notify', primaryPromiseID, resolutions }; + notify(resolutions) { + const d = { type: 'notify', resolutions }; log.push(d); if (onDispatchCallback) { onDispatchCallback(d);