Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a reapAllVats method to the controller #8634

Merged
merged 1 commit into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/SwingSet/src/controller/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ export async function makeSwingsetController(
return kernel.shutdown();
},

reapAllVats() {
kernel.reapAllVats();
},

changeKernelOptions(options) {
kernel.changeKernelOptions(options);
},
Expand Down
10 changes: 10 additions & 0 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -1750,6 +1750,15 @@ export default function buildKernel(
}
}

function reapAllVats() {
for (const [_, vatID] of kernelKeeper.getStaticVats()) {
kernelKeeper.scheduleReap(vatID);
}
for (const vatID of kernelKeeper.getDynamicVats()) {
kernelKeeper.scheduleReap(vatID);
}
}

async function step() {
if (kernelPanic) {
throw kernelPanic;
Expand Down Expand Up @@ -1935,6 +1944,7 @@ export default function buildKernel(
step,
run,
shutdown,
reapAllVats,
changeKernelOptions,

// the rest are for testing and debugging
Expand Down
5 changes: 4 additions & 1 deletion packages/SwingSet/src/types-external.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ export {};
* @typedef {{
* bundle: Bundle
* }} BundleRef
* @typedef {(SourceSpec | BundleSpec | BundleRef ) & {
* @typedef {{
* bundleName: string
* }} BundleName
* @typedef {(SourceSpec | BundleSpec | BundleRef | BundleName ) & {
* creationOptions?: Record<string, any>,
* parameters?: Record<string, any>,
* }} SwingSetConfigProperties
Expand Down
23 changes: 23 additions & 0 deletions packages/SwingSet/test/reap-all/bootstrap-reap-all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Far, E } from '@endo/far';

export function buildRootObject() {
let vatAdmin;
let bcap;
const roots = [];

return Far('root', {
async bootstrap(vats, devices) {
vatAdmin = await E(vats.vatAdmin).createVatAdminService(devices.vatAdmin);
bcap = await E(vatAdmin).getNamedBundleCap('dumbo');
console.log('end of bootstrap, vatAdmin', vatAdmin);
},

async createDynamicVats() {
for (let i = 0; i < 3; i += 1) {
const res = await E(vatAdmin).createVat(bcap);
roots.push(res.root);
}
return roots;
},
});
}
83 changes: 83 additions & 0 deletions packages/SwingSet/test/reap-all/test-reap-all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// eslint-disable-next-line import/order
import { test } from '../../tools/prepare-test-env-ava.js';

import { initSwingStore } from '@agoric/swing-store';
import { kunser } from '@agoric/kmarshal';

import { initializeSwingset, makeSwingsetController } from '../../src/index.js';

function bfile(name) {
return new URL(name, import.meta.url).pathname;
}

test('reap all vats', async t => {
/** @type {SwingSetConfig} */
const config = {
defaultManagerType: 'local',
defaultReapInterval: 4,
bootstrap: 'bootstrap',
vats: {
bootstrap: { sourceSpec: bfile('bootstrap-reap-all.js') },
staticDumbo1: { bundleName: 'dumbo' },
staticDumbo2: { bundleName: 'dumbo' },
staticDumbo3: { bundleName: 'dumbo' },
},
bundles: {
dumbo: { sourceSpec: bfile('vat-dumbo.js') },
},
};

const kernelStorage = initSwingStore().kernelStorage;
await initializeSwingset(config, [], kernelStorage);
const c = await makeSwingsetController(kernelStorage);
t.teardown(c.shutdown);
c.pinVatRoot('bootstrap');
c.pinVatRoot('staticDumbo1');
c.pinVatRoot('staticDumbo2');
c.pinVatRoot('staticDumbo3');
await c.run();

const kpid = c.queueToVatRoot('bootstrap', 'createDynamicVats');
await c.run();

const dynamicRoots = kunser(c.kpResolution(kpid));
for (let i = 0; i < 3; i += 1) {
for (let j = 0; j < i + 1; j += 1) {
c.queueToVatRoot(`staticDumbo${i + 1}`, 'doSomething', [
`staticDumbo${i + 1} #${j + 1}`,
]);
}
}
for (let i = 0; i < 3; i += 1) {
for (let j = 0; j < i + 1; j += 1) {
c.queueToVatObject(dynamicRoots[i], 'doSomething', [
`dynamicDumbo${i + 1} #${j + 1}`,
]);
}
}
// Note: no call to c.run() here, so all the above messages are still enqueued

const dumpBefore = c.dump();
t.is(dumpBefore.acceptanceQueue.length, 12);
t.is(dumpBefore.reapQueue.length, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, neat, it didn't occur to me to use reapQueue for measuring this.. very clever.

Let's see, the kernel servicing code:

  function getNextMessageAndProcessor() {
    const acceptanceMessage = kernelKeeper.getNextAcceptanceQueueMsg();
    if (acceptanceMessage) {
      return {
        message: acceptanceMessage,
        processor: processAcceptanceMessage,
      };
    }
    const message =
      processGCActionSet(kernelKeeper) ||
      kernelKeeper.nextReapAction() ||
      kernelKeeper.getNextRunQueueMsg();
    return { message, processor: processDeliveryMessage };
  }

services the acceptanceQueue first (transferring items to the run-queue), then takes any GC action, then reapQueue, then finally runQueue. So when we use this feature for GC remediation, it's important to drain a controller.run() first (leaving all queues empty), then submit the c.reapAllVats(), then drain a second controller.run().

(if we submitted the reapAllVats() while there were still things in the acceptance or run queues, the reaps would happen first, then the real deliveries, which would leave GC stuff from the real deliveries still hanging out for an unknown period of time before the "organic" BOYDs finally happened)

t.is(dumpBefore.runQueue.length, 0);

c.reapAllVats();
const dumpPreReap = c.dump();
t.is(dumpPreReap.acceptanceQueue.length, 12);
t.is(dumpPreReap.reapQueue.length, 11);
t.is(dumpBefore.runQueue.length, 0);
// prettier-ignore
const reapQueueReference =
new Set(['v1', 'v3', 'v6', 'v7', 'v8', 'v5', 'v2', 'v4', 'v9', 'v10', 'v11'])
const reapQueueActual = new Set(dumpPreReap.reapQueue);
t.deepEqual(reapQueueActual, reapQueueReference);

await c.run();
const dumpPostReap = c.dump();
t.is(dumpPostReap.acceptanceQueue.length, 0);
FUDCo marked this conversation as resolved.
Show resolved Hide resolved
t.is(dumpPostReap.reapQueue.length, 0);
t.is(dumpBefore.runQueue.length, 0);

t.pass();
});
9 changes: 9 additions & 0 deletions packages/SwingSet/test/reap-all/vat-dumbo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Far } from '@endo/far';

export function buildRootObject() {
return Far('root', {
doSomething(msg) {
console.log(`doSomething: ${msg}`);
},
});
}
Loading