diff --git a/packages/SwingSet/src/kernel/kernel.js b/packages/SwingSet/src/kernel/kernel.js index d8e5ee9bc32..324f0c3b59d 100644 --- a/packages/SwingSet/src/kernel/kernel.js +++ b/packages/SwingSet/src/kernel/kernel.js @@ -808,6 +808,10 @@ export default function buildKernel(kernelEndowments) { const vatKeeper = kernelKeeper.allocateVatKeeperIfNeeded(vatID); if (!vatKeeper.isDead()) { vatKeeper.markAsDead(); + const err = makeError('vat terminated'); + for (const kpid of kernelKeeper.findPromisesFromDeadVat(vatID)) { + deliverToError(kpid, err, vatID); + } removeVatManager(vatID).then( () => kdebug(`terminated vat ${vatID}`), e => console.error(`problem terminating vat ${vatID}`, e), diff --git a/packages/SwingSet/src/kernel/state/kernelKeeper.js b/packages/SwingSet/src/kernel/state/kernelKeeper.js index f704e8f9e4e..6de65a8d803 100644 --- a/packages/SwingSet/src/kernel/state/kernelKeeper.js +++ b/packages/SwingSet/src/kernel/state/kernelKeeper.js @@ -213,7 +213,7 @@ export default function makeKernelKeeper(storage) { } function setInitialized() { - storage.set('initialized', true); + storage.set('initialized', 'true'); } function getCrankNumber() { @@ -432,6 +432,20 @@ export default function makeKernelKeeper(storage) { storage.set(`${kernelSlot}.data.slots`, capdata.slots.join(',')); } + function findPromisesFromDeadVat(vatID) { + const prefixKey = `${vatID}.c.p`; + const endKey = `${vatID}.c.q`; + const result = []; + for (const k of storage.getKeys(prefixKey, endKey)) { + const kpid = storage.get(k); + const p = getKernelPromise(kpid); + if (p.state === 'unresolved' && p.decider === vatID) { + result.push(kpid); + } + } + return result; + } + function addMessageToPromiseQueue(kernelSlot, msg) { insistKernelType('promise', kernelSlot); @@ -793,6 +807,7 @@ export default function makeKernelKeeper(storage) { fulfillKernelPromiseToPresence, fulfillKernelPromiseToData, rejectKernelPromise, + findPromisesFromDeadVat, addMessageToPromiseQueue, addSubscriberToPromise, setDecider, diff --git a/packages/SwingSet/src/kernel/state/vatKeeper.js b/packages/SwingSet/src/kernel/state/vatKeeper.js index e90be8e0bc2..b69ddf2768f 100644 --- a/packages/SwingSet/src/kernel/state/vatKeeper.js +++ b/packages/SwingSet/src/kernel/state/vatKeeper.js @@ -180,11 +180,11 @@ export function makeVatKeeper( } function markAsDead() { - storage.set(`${vatID}.dead`, true); + storage.set(`${vatID}.dead`, 'true'); } function isDead() { - return storage.get(`${vatID}.dead`); + return !!storage.get(`${vatID}.dead`); } /** diff --git a/packages/SwingSet/test/vat-admin/terminate/bootstrap-terminate.js b/packages/SwingSet/test/vat-admin/terminate/bootstrap-terminate.js index 0f477be314f..481495ef345 100644 --- a/packages/SwingSet/test/vat-admin/terminate/bootstrap-terminate.js +++ b/packages/SwingSet/test/vat-admin/terminate/bootstrap-terminate.js @@ -45,8 +45,8 @@ export function buildRootObject(vatPowers) { // the first will trigger another outgoing query .. const query3P = E(dude.root).elsewhere(self, 3); query3P.then( - answer => testLog(`3P.then ${answer}`), - err => testLog(`3P.catch ${err}`), + answer => testLog(`query3P.then ${answer}`), + err => testLog(`query3P.catch ${err}`), ); // .. but it will be killed .. E(dude.adminNode).terminate(); @@ -70,53 +70,40 @@ export function buildRootObject(vatPowers) { // [adminNode.terminate, dude.foo(4), adminNode.terminate, self.query(3)] // then terminate() is delivered, and the vat is killed. The kernel pushes a - // message to vatAdmin to let the done() promise resolve. (PHASE 2) The kernel also + // message to vatAdmin to let the done() promise resolve. The kernel also // looks for the unresolved promises decided by the late vat, and rejects them, // which pushes notify events on the queue - // (PHASE 1) run-queue is: - // [dude.foo(4), adminNode.terminate, self.query(3), vatAdmin.fireDone] - // (PHASE 2) run-queue is: + // run-queue is: // [dude.foo(4), adminNode.terminate, self.query(3), vatAdmin.fireDone, // self.notify(foreverP), self.notify(afterForeverP)] // now dude.foo(4) comes up for delivery, and deliverToVat notices the // target is dead, so the kernel rejects the result, pushing another // notify - // (PHASE 1) run-queue is: - // [adminNode.terminate, self.query(3), vatAdmin.fireDone, self.notify(foo4P)] - // (PHASE 2) run-queue is: + // run-queue is: // [adminNode.terminate, self.query(3), vatAdmin.fireDone, // self.notify(foreverP), self.notify(afterForeverP), self.notify(foo4P)] // now the duplicate terminate() comes up, and vatAdminVat should ignore it - // (PHASE 1) run-queue is: - // [self.query(3), vatAdmin.fireDone, self.notify(foo4P)] - // (PHASE 2) run-queue is: + // run-queue is: // [self.query(3), vatAdmin.fireDone, self.notify(foreverP), // self.notify(afterForeverP), self.notify(foo4P)] // now the self.query(3) gets delivered, which pushes 'GOT QUERY 3' onto testLog, and // resolves the result promise. The dead vat is the only subscriber, so the kernel // pushes a notify event to vat-dude for it (which will never be delivered) - // (PHASE 1) run-queue is: - // [vatAdmin.fireDone, self.notify(foo4P), dude.notify(answerP)] - // (PHASE 2) run-queue is: + // run-queue is: // [vatAdmin.fireDone, self.notify(foreverP), self.notify(afterForeverP), // self.notify(foo4P), dude.notify(answerP)] // now vatAdmin gets fireDone, which resolves the 'done' promise we've been awaiting for, // which pushes a notify - // (PHASE 1) run-queue is: - // [self.notify(foo4P), dude.notify(answerP), self.notify(doneP)] - // (PHASE 2) run-queue is: + // run-queue is: // [self.notify(foreverP), self.notify(afterForeverP), self.notify(foo4P), // dude.notify(answerP), self.notify(doneP)] - // PHASE 2: we receive the rejection of foreverP, pushing - // 'foreverP.catch (err)' to testLog - // PHASE 2: we receive the rejection of afterForeverP, pushing - // 'afterForeverP.catch (err)' to testLog - + // We receive the rejection of foreverP, pushing 'foreverP.catch (err)' to testLog + // We receive the rejection of afterForeverP, pushing 'afterForeverP.catch (err)' to testLog // run-queue is: // [self.notify(foo4P), dude.notify(answerP), self.notify(doneP)] @@ -130,8 +117,6 @@ export function buildRootObject(vatPowers) { // We finally hear about doneP resolving, allowing the bootstrap to // proceed to the end of the test. We push the 'done' message to testLog - // CHIP TODO PHASE1: (or defer to phase1.5): uncomment, wire up - // queueToExport(vatAdminVat) to make it fire 'done' await doneP; testLog('done'); diff --git a/packages/SwingSet/test/vat-admin/terminate/test-terminate.js b/packages/SwingSet/test/vat-admin/terminate/test-terminate.js index 448915cef55..da28f92e253 100644 --- a/packages/SwingSet/test/vat-admin/terminate/test-terminate.js +++ b/packages/SwingSet/test/vat-admin/terminate/test-terminate.js @@ -32,10 +32,10 @@ test('terminate', async t => { 'query2 2', 'QUERY 3', 'GOT QUERY 3', - // these two will be added in phase 2 - // 'foreverP.catch (err??)', - // 'afterForeverP.catch (err??)', + 'foreverP.catch vat terminated', + 'query3P.catch vat terminated', 'foo4P.catch vat is dead', + 'afterForeverP.catch vat terminated', 'done', ]); t.end();