diff --git a/packages/SwingSet/src/kernel/kernel.js b/packages/SwingSet/src/kernel/kernel.js index f864374a403..463e0ec1cd8 100644 --- a/packages/SwingSet/src/kernel/kernel.js +++ b/packages/SwingSet/src/kernel/kernel.js @@ -878,6 +878,9 @@ export default function buildKernel( let useMeter = false; let deliverP = null; + // The common action should be delivering events to the vat. Any references + // in the events should no longer be the kernel's responsibility and the + // refcounts should be decremented if (message.type === 'send') { useMeter = true; const route = routeSendEvent(message); @@ -890,6 +893,8 @@ export default function buildKernel( decrementSendEventRefCount(message); deliverP = processSend(vatID, route.target, message.msg); } else { + // Message is requeued and stays the kernel's responsibility, do not + // decrement refcounts in this case kernelKeeper.addMessageToPromiseQueue(route.target, message.msg); } } @@ -1096,14 +1101,17 @@ export default function buildKernel( /** @type { PolicyInput } */ const policyInput = ['none']; + // By default we're moving events from one queue to another. Any references + // in the events remain the kernel's responsibility and the refcounts persist if (message.type === 'send') { const route = routeSendEvent(message); if (!route) { - // Message went splat + // Message went splat, no longer the kernel's responsibility decrementSendEventRefCount(message); } else { const { vatID, target } = route; if (target !== message.target) { + // Message has been re-targeted, other refcounts stay intact kernelKeeper.decrementRefCount(message.target, `deq|msg|t`); kernelKeeper.incrementRefCount(target, `enq|msg|t`); } diff --git a/packages/SwingSet/src/kernel/state/kernelKeeper.js b/packages/SwingSet/src/kernel/state/kernelKeeper.js index 468f2edc791..24e8eaeef4e 100644 --- a/packages/SwingSet/src/kernel/state/kernelKeeper.js +++ b/packages/SwingSet/src/kernel/state/kernelKeeper.js @@ -833,6 +833,23 @@ export default function makeKernelKeeper( insistKernelType('promise', kernelSlot); insistMessage(msg); + // Each message on a promise's queue maintains a refcount to the promise + // itself. This isn't strictly necessary (the promise will be kept alive + // by the deciding vat's clist, or the queued message that holds this + // promise as its result), but it matches our policy with run-queue + // messages (each holds a refcount on its target). + // + // Messages are enqueued on a promise queue in 2 cases: + // - A message routed from the acceptance queue + // - A pipelined message had a decider change while in the run-queue + // Messages are dequeued from a promise queue in 2 cases: + // - The promise is resolved + // - The promise's decider is changed to a pipelining vat + // In all cases the messages are just moved from one queue to another so + // the caller should not need to change the refcounts when moving messages + // sent to promises between queues. Only when re-targeting after resolution + // would the targets refcount be updated (but not the result or slots). + const p = getKernelPromise(kernelSlot); assert( p.state === 'unresolved',