Skip to content

Commit 9941a08

Browse files
committed
feat: refactor object pinning
1 parent 7edfa24 commit 9941a08

File tree

5 files changed

+58
-19
lines changed

5 files changed

+58
-19
lines changed

packages/SwingSet/src/controller.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import { waitUntilQuiescent } from './waitUntilQuiescent.js';
2222
import { makeGcAndFinalize } from './gc-and-finalize.js';
2323
import { insistStorageAPI } from './storageAPI.js';
2424
import { insistCapData } from './capdata.js';
25-
import { parseVatSlot } from './parseVatSlots.js';
2625
import { provideHostStorage } from './hostStorage.js';
2726
import {
2827
swingsetIsInitialized,
@@ -361,6 +360,12 @@ export async function makeSwingsetController(
361360
return defensiveCopy(kernel.getStatus());
362361
},
363362

363+
pinVatRoot(vatName) {
364+
const vatID = kernel.vatNameToID(vatName);
365+
const kref = kernel.getRootObject(vatID);
366+
kernel.pinObject(kref);
367+
},
368+
364369
// these are for tests
365370

366371
kpStatus(kpid) {
@@ -379,17 +384,15 @@ export async function makeSwingsetController(
379384

380385
/**
381386
* @param {string} vatName
382-
* @param {string} exportID
383387
* @param {string} method
384388
* @param {CapData<unknown>} args
385389
* @param {ResolutionPolicy} resultPolicy
386390
*/
387-
queueToVatExport(vatName, exportID, method, args, resultPolicy = 'ignore') {
391+
queueToVatRoot(vatName, method, args, resultPolicy = 'ignore') {
388392
const vatID = kernel.vatNameToID(vatName);
389-
parseVatSlot(exportID);
390393
assert.typeof(method, 'string');
391394
insistCapData(args);
392-
const kref = kernel.addExport(vatID, exportID);
395+
const kref = kernel.getRootObject(vatID);
393396
const kpid = kernel.queueToKref(kref, method, args, resultPolicy);
394397
if (kpid) {
395398
kernel.kpRegisterInterest(kpid);

packages/SwingSet/src/kernel/initializeKernel.js

+6-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { makeVatSlot } from '../parseVatSlots';
88
import { insistStorageAPI } from '../storageAPI';
99
import { wrapStorage } from './state/storageWrapper';
1010
import makeKernelKeeper from './state/kernelKeeper';
11-
import { doAddExport, doQueueToKref } from './kernel';
11+
import { exportRootObject, doQueueToKref } from './kernel';
1212

1313
function makeVatRootObjectSlot() {
1414
return makeVatSlot('object', true, 0);
@@ -82,11 +82,10 @@ export function initializeKernel(config, hostStorage, verbose = false) {
8282
vatKeeper.setSourceAndOptions({ bundle, bundleName }, creationOptions);
8383
if (name === 'vatAdmin') {
8484
// Create a kref for the vatAdmin root, so the kernel can tell it
85-
// about creation/termination of dynamic vats. doAddExport will
86-
// increment the refcount so it won't go away, despite being
87-
// unreferenced by any other vat.
88-
const rootVref = makeVatRootObjectSlot();
89-
const kref = doAddExport(kernelKeeper, vatID, rootVref);
85+
// about creation/termination of dynamic vats.
86+
const kref = exportRootObject(kernelKeeper, vatID);
87+
// Pin, to prevent it being GC'd when only the kvStore points to it
88+
kernelKeeper.pinObject(kref);
9089
kvStore.set('vatAdminRootKref', kref);
9190
gotVatAdminRootKref = true;
9291
}
@@ -193,8 +192,7 @@ export function initializeKernel(config, hostStorage, verbose = false) {
193192
});
194193
const args = harden([vatObj0s, deviceObj0s]);
195194
// doQueueToKref() takes kernel-refs (ko+NN, kd+NN) in s.slots
196-
const rootVref = makeVatRootObjectSlot();
197-
const rootKref = doAddExport(kernelKeeper, bootstrapVatID, rootVref);
195+
const rootKref = exportRootObject(kernelKeeper, bootstrapVatID);
198196
const resultKpid = doQueueToKref(
199197
kernelKeeper,
200198
rootKref,

packages/SwingSet/src/kernel/kernel.js

+25-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { makeSlogger, makeDummySlogger } from './slogger.js';
2020
import { getKpidsToRetire } from './cleanup.js';
2121
import { processNextGCAction } from './gc-actions.js';
2222

23-
import { makeVatRootObjectSlot, makeVatLoader } from './loadVat.js';
23+
import { makeVatLoader } from './loadVat.js';
2424
import { makeDeviceTranslators } from './deviceTranslator.js';
2525
import { notifyTermination } from './notifyTermination.js';
2626

@@ -43,6 +43,20 @@ function makeError(message, name = 'Error') {
4343

4444
const VAT_TERMINATION_ERROR = makeError('vat terminated');
4545

46+
/**
47+
* Provide the kref of a vat's root object, as if it had been exported.
48+
*
49+
* @param {*} kernelKeeper Kernel keeper managing persistent kernel state.
50+
* @param {string} vatID Vat ID of the vat whose root kref is sought.
51+
*
52+
* @returns {string} the kref of the root object of the given vat.
53+
*/
54+
export function exportRootObject(kernelKeeper, vatID) {
55+
insistVatID(vatID);
56+
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
57+
return vatKeeper.mapVatSlotToKernelSlot('o+0');
58+
}
59+
4660
/*
4761
* Pretend that a vat just exported an object, and increment the refcount on
4862
* the resulting kref so nothing tries to delete it for being unreferenced.
@@ -60,8 +74,7 @@ export function doAddExport(kernelKeeper, fromVatID, vref) {
6074

6175
/**
6276
* Enqueue a message to some kernel object, as if the message had been sent
63-
* by some other vat. This requires a kref as a target, so use e.g.
64-
* doAddExport to acquire one and increment its refcount to keep it alive.
77+
* by some other vat. This requires a kref as a target.
6578
*
6679
* @param {*} kernelKeeper Kernel keeper managing persistent kernel state
6780
* @param {string} kref Target of the message
@@ -236,6 +249,10 @@ export default function buildKernel(
236249
return vatKeeper.mapKernelSlotToVatSlot(kernelSlot);
237250
}
238251

252+
function pinObject(kref) {
253+
kernelKeeper.pinObject(kref);
254+
}
255+
239256
function addExport(fromVatID, vatSlot) {
240257
if (!started) {
241258
throw new Error('must do kernel.start() before addExport()');
@@ -576,7 +593,7 @@ export default function buildKernel(
576593
function makeSuccessResponse() {
577594
// build success message, giving admin vat access to the new vat's root
578595
// object
579-
const kernelRootObjSlot = addExport(vatID, makeVatRootObjectSlot());
596+
const kernelRootObjSlot = exportRootObject(kernelKeeper, vatID);
580597
return {
581598
body: JSON.stringify([
582599
vatID,
@@ -1068,6 +1085,10 @@ export default function buildKernel(
10681085

10691086
addImport,
10701087
addExport,
1088+
getRootObject(vatID) {
1089+
return exportRootObject(kernelKeeper, vatID);
1090+
},
1091+
pinObject,
10711092
vatNameToID,
10721093
deviceNameToID,
10731094
queueToKref,

packages/SwingSet/src/kernel/state/kernelKeeper.js

+12
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const enableKernelGC = true;
6363

6464
// runQueue = JSON(runQueue) // usually empty on disk
6565
// gcActions = JSON(gcActions) // usually empty on disk
66+
// pinnedObjects = ko$NN[,ko$NN..]
6667

6768
// ko.nextID = $NN
6869
// ko$NN.owner = $vatID
@@ -1055,6 +1056,16 @@ export default function makeKernelKeeper(
10551056
return importers;
10561057
}
10571058

1059+
function pinObject(kref) {
1060+
const pinList = kvStore.get('pinnedObjects') || '';
1061+
const pinned = new Set(commaSplit(pinList));
1062+
if (!pinned.has(kref)) {
1063+
incrementRefCount(kref, 'pin');
1064+
pinned.add(kref);
1065+
kvStore.set('pinnedObjects', [...pinned].sort().join(','));
1066+
}
1067+
}
1068+
10581069
// used for debugging, and tests. This returns a JSON-serializable object.
10591070
// It includes references to live (mutable) kernel state, so don't mutate
10601071
// the pieces, and be sure to serialize/deserialize before passing it
@@ -1173,6 +1184,7 @@ export default function makeKernelKeeper(
11731184
kernelObjectExists,
11741185
getImporters,
11751186
deleteKernelObject,
1187+
pinObject,
11761188

11771189
addKernelPromise,
11781190
addKernelPromiseForVat,

packages/swingset-runner/src/main.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ export async function main() {
411411
config,
412412
bootstrapArgv,
413413
hostStorage,
414+
{ verbose },
414415
runtimeOptions,
415416
);
416417
swingStore.commit();
@@ -424,6 +425,11 @@ export async function main() {
424425
deviceEndowments,
425426
runtimeOptions,
426427
);
428+
if (benchmarkRounds > 0) {
429+
// Pin the vat root that will run benchmark rounds so it'll still be there
430+
// when it comes time to run them.
431+
controller.pinVatRoot(launchIndirectly ? 'launcher' : 'bootstrap');
432+
}
427433

428434
let blockNumber = 0;
429435
let statLogger = null;
@@ -560,9 +566,8 @@ export async function main() {
560566
let totalSteps = 0;
561567
let totalDeltaT = 0n;
562568
for (let i = 0; i < rounds; i += 1) {
563-
const roundResult = controller.queueToVatExport(
569+
const roundResult = controller.queueToVatRoot(
564570
launchIndirectly ? 'launcher' : 'bootstrap',
565-
'o+0',
566571
'runBenchmarkRound',
567572
args,
568573
'ignore',

0 commit comments

Comments
 (0)