diff --git a/packages/SwingSet/src/kernel/state/kernelKeeper.js b/packages/SwingSet/src/kernel/state/kernelKeeper.js index 1d64b225f1c..976e8d919f1 100644 --- a/packages/SwingSet/src/kernel/state/kernelKeeper.js +++ b/packages/SwingSet/src/kernel/state/kernelKeeper.js @@ -43,6 +43,7 @@ const enableKernelGC = true; * @typedef { import('../../types-external.js').SnapStore } SnapStore * @typedef { import('../../types-external.js').TranscriptStore } TranscriptStore * @typedef { import('../../types-external.js').VatKeeper } VatKeeper + * @typedef { Pick } VatUndertaker * @typedef { import('../../types-internal.js').InternalKernelOptions } InternalKernelOptions * @typedef { import('../../types-internal.js').ReapDirtThreshold } ReapDirtThreshold * @import {PromiseRecord} from '../../types-internal.js'; @@ -422,6 +423,8 @@ export default function makeKernelKeeper( const ephemeral = harden({ /** @type { Map } */ vatKeepers: new Map(), + /** @type { Map } */ + vatUndertakers: new Map(), deviceKeepers: new Map(), // deviceID -> deviceKeeper }); @@ -1044,7 +1047,7 @@ export default function makeKernelKeeper( // first or vref first), and delete the other one in the same // call, so we don't wind up with half an entry. - const vatKeeper = provideVatKeeper(vatID); + const undertaker = provideVatUndertaker(vatID); const clistPrefix = `${vatID}.c.`; const exportPrefix = `${clistPrefix}o+`; const importPrefix = `${clistPrefix}o-`; @@ -1092,7 +1095,7 @@ export default function makeKernelKeeper( // drop+retire const kref = kvStore.get(k) || Fail`getNextKey ensures get`; const vref = stripPrefix(clistPrefix, k); - vatKeeper.deleteCListEntry(kref, vref); + undertaker.deleteCListEntry(kref, vref); // that will also delete both db keys work.imports += 1; remaining -= 1; @@ -1109,7 +1112,7 @@ export default function makeKernelKeeper( for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) { const kref = kvStore.get(k) || Fail`getNextKey ensures get`; const vref = stripPrefix(clistPrefix, k); - vatKeeper.deleteCListEntry(kref, vref); + undertaker.deleteCListEntry(kref, vref); // that will also delete both db keys work.promises += 1; remaining -= 1; @@ -1131,7 +1134,7 @@ export default function makeKernelKeeper( // this will internally loop through 'budget' deletions remaining = budget.snapshots ?? budget.default; - const dsc = vatKeeper.deleteSnapshots(remaining); + const dsc = undertaker.deleteSnapshots(remaining); work.snapshots += dsc.cleanups; remaining -= dsc.cleanups; if (remaining <= 0) { @@ -1140,7 +1143,7 @@ export default function makeKernelKeeper( // same remaining = budget.transcripts ?? budget.default; - const dts = vatKeeper.deleteTranscripts(remaining); + const dts = undertaker.deleteTranscripts(remaining); work.transcripts += dts.cleanups; remaining -= dts.cleanups; // last task, so increment cleanups, but dc.done is authoritative @@ -1697,6 +1700,26 @@ export default function makeKernelKeeper( initializeVatState(kvStore, transcriptStore, vatID, source, options); } + /** @type {import('./vatKeeper.js').VatKeeperPowers} */ + const vatKeeperPowers = { + transcriptStore, + kernelSlog, + addKernelObject, + addKernelPromiseForVat, + kernelObjectExists, + incrementRefCount, + decrementRefCount, + getObjectRefCount, + setObjectRefCount, + getReachableAndVatSlot, + addMaybeFreeKref, + incStat, + decStat, + getCrankNumber, + scheduleReap, + snapStore, + }; + function provideVatKeeper(vatID) { insistVatID(vatID); const found = ephemeral.vatKeepers.get(vatID); @@ -1704,30 +1727,36 @@ export default function makeKernelKeeper( return found; } assert(kvStore.has(`${vatID}.o.nextID`), `${vatID} was not initialized`); - const vk = makeVatKeeper( - kvStore, - transcriptStore, - kernelSlog, - vatID, - addKernelObject, - addKernelPromiseForVat, - kernelObjectExists, - incrementRefCount, - decrementRefCount, - getObjectRefCount, - setObjectRefCount, - getReachableAndVatSlot, - addMaybeFreeKref, - incStat, - decStat, - getCrankNumber, - scheduleReap, - snapStore, - ); + const vk = makeVatKeeper(vatID, kvStore, vatKeeperPowers); ephemeral.vatKeepers.set(vatID, vk); return vk; } + /** + * Produce an attenuated vatKeeper for slow vat termination (and that + * therefore does not insist on liveness, unlike provideVatKeeper). + * + * @param {string} vatID + */ + function provideVatUndertaker(vatID) { + insistVatID(vatID); + const found = ephemeral.vatUndertakers.get(vatID); + if (found !== undefined) { + return found; + } + const { deleteCListEntry, deleteSnapshots, deleteTranscripts } = + ephemeral.vatKeepers.get(vatID) || + makeVatKeeper(vatID, kvStore, vatKeeperPowers); + /** @type {VatUndertaker} */ + const undertaker = harden({ + deleteCListEntry, + deleteSnapshots, + deleteTranscripts, + }); + ephemeral.vatUndertakers.set(vatID, undertaker); + return undertaker; + } + function vatIsAlive(vatID) { insistVatID(vatID); return kvStore.has(`${vatID}.o.nextID`) && !terminatedVats.includes(vatID); diff --git a/packages/SwingSet/src/kernel/state/vatKeeper.js b/packages/SwingSet/src/kernel/state/vatKeeper.js index 1d4d6b1f12f..d307bc77e7b 100644 --- a/packages/SwingSet/src/kernel/state/vatKeeper.js +++ b/packages/SwingSet/src/kernel/state/vatKeeper.js @@ -84,49 +84,53 @@ export function initializeVatState( } /** - * Produce a vat keeper for a vat. + * @typedef {object} VatKeeperPowers + * @property {TranscriptStore} transcriptStore Accompanying transcript store, for the transcripts + * @property {*} kernelSlog + * @property {*} addKernelObject Kernel function to add a new object to the kernel's mapping tables. + * @property {*} addKernelPromiseForVat Kernel function to add a new promise to the kernel's mapping tables. + * @property {(kernelSlot: string) => boolean} kernelObjectExists + * @property {*} incrementRefCount + * @property {*} decrementRefCount + * @property {(kernelSlot: string) => {reachable: number, recognizable: number}} getObjectRefCount + * @property {(kernelSlot: string, o: { reachable: number, recognizable: number }) => void} setObjectRefCount + * @property {(vatID: string, kernelSlot: string) => {isReachable: boolean, vatSlot: string}} getReachableAndVatSlot + * @property {(kernelSlot: string) => void} addMaybeFreeKref + * @property {*} incStat + * @property {*} decStat + * @property {*} getCrankNumber + * @property {*} scheduleReap + * @property {SnapStore} snapStore + */ + +/** + * Produce a "vat keeper" for the kernel state of a vat. * - * @param {KVStore} kvStore The keyValue store in which the persistent state will be kept - * @param {TranscriptStore} transcriptStore Accompanying transcript store, for the transcripts - * @param {*} kernelSlog * @param {string} vatID The vat ID string of the vat in question - * @param {*} addKernelObject Kernel function to add a new object to the kernel's - * mapping tables. - * @param {*} addKernelPromiseForVat Kernel function to add a new promise to the - * kernel's mapping tables. - * @param {(kernelSlot: string) => boolean} kernelObjectExists - * @param {*} incrementRefCount - * @param {*} decrementRefCount - * @param {(kernelSlot: string) => {reachable: number, recognizable: number}} getObjectRefCount - * @param {(kernelSlot: string, o: { reachable: number, recognizable: number }) => void} setObjectRefCount - * @param {(vatID: string, kernelSlot: string) => {isReachable: boolean, vatSlot: string}} getReachableAndVatSlot - * @param {(kernelSlot: string) => void} addMaybeFreeKref - * @param {*} incStat - * @param {*} decStat - * @param {*} getCrankNumber - * @param {*} scheduleReap - * @param {SnapStore} [snapStore] - * returns an object to hold and access the kernel's state for the given vat + * @param {KVStore} kvStore The keyValue store in which the persistent state will be kept + * @param {VatKeeperPowers} powers */ export function makeVatKeeper( - kvStore, - transcriptStore, - kernelSlog, vatID, - addKernelObject, - addKernelPromiseForVat, - kernelObjectExists, - incrementRefCount, - decrementRefCount, - getObjectRefCount, - setObjectRefCount, - getReachableAndVatSlot, - addMaybeFreeKref, - incStat, - decStat, - getCrankNumber, - scheduleReap, - snapStore = undefined, + kvStore, + { + transcriptStore, + kernelSlog, + addKernelObject, + addKernelPromiseForVat, + kernelObjectExists, + incrementRefCount, + decrementRefCount, + getObjectRefCount, + setObjectRefCount, + getReachableAndVatSlot, + addMaybeFreeKref, + incStat, + decStat, + getCrankNumber, + scheduleReap, + snapStore, + }, ) { insistVatID(vatID);