diff --git a/packages/SwingSet/src/controller.js b/packages/SwingSet/src/controller.js index 653061c19ee..95043c50d43 100644 --- a/packages/SwingSet/src/controller.js +++ b/packages/SwingSet/src/controller.js @@ -46,7 +46,12 @@ export async function makeSwingsetController( insistStorageAPI(hostStorage); // build console early so we can add console.log to diagnose early problems - const { verbose, debugPrefix = '', slogFile } = runtimeOptions; + const { + verbose, + debugPrefix = '', + slogFile, + testTrackDecref, + } = runtimeOptions; if (typeof Compartment === 'undefined') { throw Error('SES must be installed before calling makeSwingsetController'); } @@ -188,7 +193,7 @@ export async function makeSwingsetController( FinalizationRegistry, }; - const kernelOptions = { verbose }; + const kernelOptions = { verbose, testTrackDecref }; const kernel = buildKernel(kernelEndowments, deviceEndowments, kernelOptions); if (runtimeOptions.verbose) { @@ -280,8 +285,9 @@ export async function buildVatController( verbose, kernelBundles, debugPrefix, + testTrackDecref, } = runtimeOptions; - const actualRuntimeOptions = { verbose, debugPrefix }; + const actualRuntimeOptions = { verbose, debugPrefix, testTrackDecref }; const initializationOptions = { verbose, kernelBundles }; let bootstrapResult; if (!swingsetIsInitialized(hostStorage)) { diff --git a/packages/SwingSet/src/kernel/kernel.js b/packages/SwingSet/src/kernel/kernel.js index 97e427382bd..7cc205fa2b3 100644 --- a/packages/SwingSet/src/kernel/kernel.js +++ b/packages/SwingSet/src/kernel/kernel.js @@ -103,7 +103,7 @@ export default function buildKernel( FinalizationRegistry, } = kernelEndowments; deviceEndowments = { ...deviceEndowments }; // copy so we can modify - const { verbose } = kernelOptions; + const { verbose, testTrackDecref = false } = kernelOptions; const logStartup = verbose ? console.debug : () => 0; insistStorageAPI(hostStorage); @@ -144,6 +144,17 @@ export default function buildKernel( return kernelSlog.vatConsole(vatID, origConsole); } + const pendingDecrefs = []; + function decref(vatID, vref, count) { + assert(ephemeral.vats.has(vatID), `unknown vatID ${vatID}`); + assert(count > 0, `bad count ${count}`); + // TODO: decrement the clist import counter by 'count', then GC if zero + if (testTrackDecref) { + console.log(`kernel decref [${vatID}].${vref} -= ${count}`); + pendingDecrefs.push({ vatID, vref, count }); + } + } + // runQueue entries are {type, vatID, more..}. 'more' depends on type: // * deliver: target, msg // * notifyFulfillToData/notifyFulfillToPresence/notifyReject: @@ -531,7 +542,7 @@ export default function buildKernel( } } - const gcTools = harden({ WeakRef, FinalizationRegistry }); + const gcTools = harden({ WeakRef, FinalizationRegistry, decref }); const vatManagerFactory = makeVatManagerFactory({ allVatPowers, kernelKeeper, @@ -955,7 +966,7 @@ export default function buildKernel( // a time, so any log() calls that were interleaved during their // original execution will be sorted by vat in the replace). Logs are // not kept in the persistent state, only in ephemeral state. - return { log: ephemeral.log, ...kernelKeeper.dump() }; + return { log: ephemeral.log, pendingDecrefs, ...kernelKeeper.dump() }; }, kdebugEnable, diff --git a/packages/SwingSet/src/kernel/vatManager/factory.js b/packages/SwingSet/src/kernel/vatManager/factory.js index fa4a207e9fb..e712ebc1b54 100644 --- a/packages/SwingSet/src/kernel/vatManager/factory.js +++ b/packages/SwingSet/src/kernel/vatManager/factory.js @@ -30,18 +30,21 @@ export function makeVatManagerFactory({ makeNodeWorker, kernelKeeper, testLog: allVatPowers.testLog, + decref: gcTools.decref, }); const nodeSubprocessFactory = makeNodeSubprocessFactory({ startSubprocessWorker: startSubprocessWorkerNode, kernelKeeper, testLog: allVatPowers.testLog, + decref: gcTools.decref, }); const xsWorkerFactory = makeNodeSubprocessFactory({ startSubprocessWorker: startSubprocessWorkerXS, kernelKeeper, testLog: allVatPowers.testLog, + decref: gcTools.decref, }); function validateManagerOptions(managerOptions) { diff --git a/packages/SwingSet/src/kernel/vatManager/localVatManager.js b/packages/SwingSet/src/kernel/vatManager/localVatManager.js index 8434470bf0a..2450bf7fee5 100644 --- a/packages/SwingSet/src/kernel/vatManager/localVatManager.js +++ b/packages/SwingSet/src/kernel/vatManager/localVatManager.js @@ -17,6 +17,7 @@ export function makeLocalVatManagerFactory(tools) { } = tools; const { makeGetMeter, refillAllMeters, stopGlobalMeter } = meterManager; + const { WeakRef, FinalizationRegistry } = gcTools; const baseVP = { Remotable: allVatPowers.Remotable, getInterfaceOf: allVatPowers.getInterfaceOf, @@ -105,10 +106,14 @@ export function makeLocalVatManagerFactory(tools) { vatParameters, testLog: allVatPowers.testLog, }); + function vatDecref(vref, count) { + gcTools.decref(vatID, vref, count); + } + const lsgc = harden({ WeakRef, FinalizationRegistry, vatDecref }); // we might or might not use this, depending upon whether the vat exports // 'buildRootObject' or a default 'setup' function - const ls = makeLiveSlots(syscall, vatID, vatPowers, vatParameters, gcTools); + const ls = makeLiveSlots(syscall, vatID, vatPowers, vatParameters, lsgc); let meterRecord = null; if (metered) { diff --git a/packages/SwingSet/src/kernel/vatManager/nodeWorker.js b/packages/SwingSet/src/kernel/vatManager/nodeWorker.js index c2cc59e1d39..a8fe9c4ba08 100644 --- a/packages/SwingSet/src/kernel/vatManager/nodeWorker.js +++ b/packages/SwingSet/src/kernel/vatManager/nodeWorker.js @@ -23,7 +23,7 @@ function parentLog(first, ...args) { } export function makeNodeWorkerVatManagerFactory(tools) { - const { makeNodeWorker, kernelKeeper, testLog } = tools; + const { makeNodeWorker, kernelKeeper, testLog, decref } = tools; function createFromBundle(vatID, bundle, managerOptions) { const { vatParameters } = managerOptions; @@ -68,6 +68,10 @@ export function makeNodeWorkerVatManagerFactory(tools) { doSyscall(vatSyscallObject); } + function vatDecref(vref, count) { + decref(vatID, vref, count); + } + // start the worker and establish a connection const { promise: workerP, resolve: gotWorker } = makePromiseKit(); @@ -107,6 +111,9 @@ export function makeNodeWorkerVatManagerFactory(tools) { const deliveryResult = args; resolve(deliveryResult); } + } else if (type === 'decref') { + const [vref, count] = args; + vatDecref(vref, count); } else { parentLog(`unrecognized uplink message ${type}`); } diff --git a/packages/SwingSet/src/kernel/vatManager/worker-subprocess-node.js b/packages/SwingSet/src/kernel/vatManager/worker-subprocess-node.js index 7c4f45db8c4..9ad89e0904c 100644 --- a/packages/SwingSet/src/kernel/vatManager/worker-subprocess-node.js +++ b/packages/SwingSet/src/kernel/vatManager/worker-subprocess-node.js @@ -24,7 +24,7 @@ function parentLog(first, ...args) { } export function makeNodeSubprocessFactory(tools) { - const { startSubprocessWorker, kernelKeeper, testLog } = tools; + const { startSubprocessWorker, kernelKeeper, testLog, decref } = tools; function createFromBundle(vatID, bundle, managerOptions) { const { vatParameters } = managerOptions; @@ -71,6 +71,10 @@ export function makeNodeSubprocessFactory(tools) { doSyscall(vatSyscallObject); } + function vatDecref(vref, count) { + decref(vatID, vref, count); + } + // start the worker and establish a connection const { fromChild, toChild, terminate, done } = startSubprocessWorker(); @@ -109,6 +113,9 @@ export function makeNodeSubprocessFactory(tools) { const deliveryResult = args; resolve(deliveryResult); } + } else if (type === 'decref') { + const [vref, count] = args; + vatDecref(vref, count); } else { parentLog(`unrecognized uplink message ${type}`); } diff --git a/packages/SwingSet/test/test-vpid-liveslots.js b/packages/SwingSet/test/test-vpid-liveslots.js index 4133a75995b..182960ed88e 100644 --- a/packages/SwingSet/test/test-vpid-liveslots.js +++ b/packages/SwingSet/test/test-vpid-liveslots.js @@ -191,7 +191,8 @@ function resolutionOf(vpid, mode, targets) { } function makeDispatch(syscall, build) { - const gcTools = harden({ WeakRef, FinalizationRegistry }); + function vatDecref() {} + const gcTools = harden({ WeakRef, FinalizationRegistry, vatDecref }); const { setBuildRootObject, dispatch } = makeLiveSlots( syscall, 'vatA',