Skip to content

Commit

Permalink
chore(swingset): RecordedVatOptions holds workerOptions, not managerType
Browse files Browse the repository at this point in the history
The internal record of vat options is stored by the vatKeeper's
`setSourceAndOptions()`, and is of type RecordedVatOptions. This is
passed to the vat loader to decide how to construct ManagerOptions to
start the worker.

This commit replaces the `.managerType` property of RecordedVatOptions
with a new WorkerOptions record. For now, this only has a `.type`
field, whose value is either `local` or `xsnap`. Soon, the xsnap
flavor will acquire new `bundleIDs` properties.

The user-facing vat-options APIs (config.vats, kernel.createTestVat,
and E(vatAdminSvc).createVat) continue to use a `managerType` of
'local' or 'xs-worker' to control the selection.

(I prefer the name `xsnap` to describe the XS-in-subprocess worker,
but the old 'xs-worker' name is used extensively by unit tests in
other packages, and is impractical to change right now).

The `makeWorkerOptions()` helper function is used to convert the
user-facing options into the internal form.
  • Loading branch information
warner committed Mar 30, 2023
1 parent 32a97e1 commit 9e7034b
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 68 deletions.
32 changes: 20 additions & 12 deletions packages/SwingSet/src/controller/initializeKernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { insistVatID } from '../lib/id.js';
import { kser, kunser } from '../lib/kmarshal.js';
import { makeVatSlot } from '../lib/parseVatSlots.js';
import { insistStorageAPI } from '../lib/storageAPI.js';
import { makeWorkerOptions } from '../lib/workerOptions.js';
import makeKernelKeeper from '../kernel/state/kernelKeeper.js';
import { exportRootObject } from '../kernel/kernel.js';
import { makeKernelQueueHandler } from '../kernel/kernelQueue.js';
Expand All @@ -16,6 +17,7 @@ function makeVatRootObjectSlot() {
}

export async function initializeKernel(config, kernelStorage, options = {}) {
await 0; // no sync prelude
const { verbose = false } = options;
const logStartup = verbose ? console.debug : () => 0;
insistStorageAPI(kernelStorage.kvStore);
Expand Down Expand Up @@ -81,22 +83,28 @@ export async function initializeKernel(config, kernelStorage, options = {}) {
'critical',
'reapInterval',
]);
creationOptions.name = name;
if (creationOptions.useTranscript === undefined) {
creationOptions.useTranscript = true;
}
if (!creationOptions.managerType) {
creationOptions.managerType = kernelKeeper.getDefaultManagerType();
}
if (!creationOptions.reapInterval) {
creationOptions.reapInterval = kernelKeeper.getDefaultReapInterval();
}
const {
managerType,
useTranscript = true,
reapInterval = kernelKeeper.getDefaultReapInterval(),
...otherOptions
} = creationOptions;
// eslint-disable-next-line @jessie.js/no-nested-await,no-await-in-loop
const workerOptions = await makeWorkerOptions(kernelKeeper, managerType);
/** @type {import('../types-internal.js').RecordedVatOptions} */
const vatOptions = harden({
name,
useTranscript,
reapInterval,
workerOptions,
...otherOptions,
});

const vatID = kernelKeeper.allocateVatIDForNameIfNeeded(name);
logStartup(`assigned VatID ${vatID} for genesis vat ${name}`);
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
vatKeeper.setSourceAndOptions({ bundleID }, creationOptions);
vatKeeper.initializeReapCountdown(creationOptions.reapInterval);
vatKeeper.setSourceAndOptions({ bundleID }, vatOptions);
vatKeeper.initializeReapCountdown(reapInterval);
kernelKeeper.addToAcceptanceQueue(
harden({ type: 'startVat', vatID, vatParameters: kser(vatParameters) }),
);
Expand Down
58 changes: 31 additions & 27 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { parseVatSlot } from '../lib/parseVatSlots.js';
import { extractSingleSlot, insistCapData } from '../lib/capdata.js';
import { insistMessage, insistVatDeliveryResult } from '../lib/message.js';
import { insistDeviceID, insistVatID } from '../lib/id.js';
import { makeWorkerOptions } from '../lib/workerOptions.js';
import { makeKernelQueueHandler } from './kernelQueue.js';
import { makeKernelSyscallHandler } from './kernelSyscall.js';
import { makeSlogger, makeDummySlogger } from './slogger.js';
Expand Down Expand Up @@ -98,7 +99,6 @@ export default function buildKernel(
deviceEndowments = { ...deviceEndowments }; // copy so we can modify
const {
verbose,
defaultManagerType = 'local',
warehousePolicy,
overrideVatManagerOptions = {},
} = kernelRuntimeOptions;
Expand Down Expand Up @@ -683,15 +683,16 @@ export default function buildKernel(
insistCapData(vatParameters);
kernelKeeper.addDynamicVatID(vatID);
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
const options = { ...dynamicOptions };
if (!dynamicOptions.managerType) {
options.managerType = kernelKeeper.getDefaultManagerType();
}
if (!dynamicOptions.reapInterval) {
options.reapInterval = kernelKeeper.getDefaultReapInterval();
}
vatKeeper.setSourceAndOptions(source, options);
vatKeeper.initializeReapCountdown(options.reapInterval);
const {
managerType,
reapInterval = kernelKeeper.getDefaultReapInterval(),
...otherOptions
} = dynamicOptions;
const workerOptions = await makeWorkerOptions(kernelKeeper, managerType);
/** @type {import('../types-internal.js').RecordedVatOptions} */
const vatOptions = harden({ workerOptions, reapInterval, ...otherOptions });
vatKeeper.setSourceAndOptions(source, vatOptions);
vatKeeper.initializeReapCountdown(reapInterval);

// createDynamicVat makes the worker, installs lockdown and
// supervisor, but does not load the vat bundle yet. It can fail
Expand Down Expand Up @@ -1425,7 +1426,6 @@ export default function buildKernel(
vatEndowments,
startXSnap,
gcTools,
defaultManagerType,
kernelSlog,
});

Expand Down Expand Up @@ -1554,34 +1554,38 @@ export default function buildKernel(
vatParameters = {},
creationOptions = {},
) {
const {
bundleID = 'b1-00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
...actualCreationOptions
} = creationOptions;
!kernelKeeper.hasVatWithName(name) || Fail`vat ${name} already exists`;
typeof setup === 'function' ||
Fail`setup is not a function, rather ${setup}`;
assertKnownOptions(actualCreationOptions, [
assertKnownOptions(creationOptions, [
'bundleID',
'enablePipelining',
'metered',
'reapInterval',
'managerType',
]);
!kernelKeeper.hasVatWithName(name) || Fail`vat ${name} already exists`;
const {
bundleID = 'b1-00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
reapInterval = 'never',
...otherOptions
} = creationOptions;

const vatID = kernelKeeper.allocateVatIDForNameIfNeeded(name);
logStartup(`assigned VatID ${vatID} for test vat ${name}`);

if (!actualCreationOptions.reapInterval) {
actualCreationOptions.reapInterval = 'never';
}
if (!actualCreationOptions.managerType) {
actualCreationOptions.managerType = 'local';
}
const workerOptions = await makeWorkerOptions(kernelKeeper, 'local');
/** @type {import('../types-internal.js').RecordedVatOptions} */
const vatOptions = harden({
name,
workerOptions,
reapInterval,
...otherOptions,
});

const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
vatKeeper.setSourceAndOptions({ bundleID }, actualCreationOptions);
vatKeeper.initializeReapCountdown(actualCreationOptions.reapInterval);
vatKeeper.setSourceAndOptions({ bundleID }, vatOptions);
vatKeeper.initializeReapCountdown(reapInterval);

await vatWarehouse.loadTestVat(vatID, setup, actualCreationOptions);
await vatWarehouse.loadTestVat(vatID, setup, vatOptions);

const vpCapData = kser(vatParameters);
/** @type { RunQueueEventStartVat } */
Expand Down
2 changes: 1 addition & 1 deletion packages/SwingSet/src/kernel/state/vatKeeper.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export function makeVatKeeper(
*/
function setSourceAndOptions(source, options) {
// take care with API change
options.managerType || Fail`vat options missing managerType`;
options.workerOptions || Fail`vat options missing workerOptions`;
assert(source);
assert(
'bundle' in source || 'bundleName' in source || 'bundleID' in source,
Expand Down
23 changes: 8 additions & 15 deletions packages/SwingSet/src/kernel/vat-loader/manager-factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export function makeVatManagerFactory({
vatEndowments,
startXSnap,
gcTools,
defaultManagerType,
kernelSlog,
}) {
const localFactory = makeLocalVatManagerFactory({
Expand All @@ -31,7 +30,7 @@ export function makeVatManagerFactory({
function validateManagerOptions(managerOptions) {
assertKnownOptions(managerOptions, [
'enablePipelining',
'managerType',
'workerOptions',
'setup',
'bundle',
'metered',
Expand Down Expand Up @@ -70,17 +69,11 @@ export function makeVatManagerFactory({
vatSyscallHandler,
) {
validateManagerOptions(managerOptions);
const {
managerType = defaultManagerType,
metered,
enableSetup,
} = managerOptions;
const { workerOptions, enableSetup } = managerOptions;
const { type } = workerOptions;

if (metered && managerType !== 'local' && managerType !== 'xs-worker') {
console.warn(`TODO: support metered with ${managerType}`);
}
if (managerType !== 'local' && 'setup' in managerOptions) {
console.warn(`TODO: stop using setup() with ${managerType}`);
if (type !== 'local' && 'setup' in managerOptions) {
console.warn(`TODO: stop using setup() with ${type}`);
}
if (enableSetup) {
if (managerOptions.setup) {
Expand All @@ -99,7 +92,7 @@ export function makeVatManagerFactory({
vatSyscallHandler,
);
}
} else if (managerType === 'local') {
} else if (type === 'local') {
return localFactory.createFromBundle(
vatID,
managerOptions.bundle,
Expand All @@ -109,7 +102,7 @@ export function makeVatManagerFactory({
);
}

if (managerType === 'xs-worker') {
if (type === 'xsnap') {
assert(managerOptions.bundle, 'xsnap requires Bundle');
return xsWorkerFactory.createFromBundle(
vatID,
Expand All @@ -120,7 +113,7 @@ export function makeVatManagerFactory({
);
}

throw Error(`unknown type ${managerType}, not 'local' or 'xs-worker'`);
throw Error(`unknown type ${type}, not 'local' or 'xsnap'`);
}

return harden(vatManagerFactory);
Expand Down
7 changes: 4 additions & 3 deletions packages/SwingSet/src/kernel/vat-loader/vat-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function makeVatLoader(stuff) {

const allowedOptions = [
'name',
'managerType', // TODO: not sure we want vats to be able to control this
'workerOptions',
'meterID',
'enableDisavow',
'enableSetup',
Expand Down Expand Up @@ -85,7 +85,7 @@ export function makeVatLoader(stuff) {
assertKnownOptions(options, allowedOptions);
const {
meterID,
managerType,
workerOptions,
enableSetup = false,
enableDisavow = false,
enablePipelining = false,
Expand All @@ -109,10 +109,11 @@ export function makeVatLoader(stuff) {
description,
name,
vatSourceBundle,
managerType,
workerOptions.type,
);

const managerOptions = {
workerOptions,
bundle: vatSourceBundle,
metered: !!meterID,
enableSetup,
Expand Down
8 changes: 4 additions & 4 deletions packages/SwingSet/src/kernel/vat-warehouse.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,19 +354,19 @@ export function makeVatWarehouse(
/**
* @param {string} vatID
* @param {unknown} setup
* @param {import('../types-external.js').StaticVatOptions} creationOptions
* @param {import('../types-internal.js').RecordedVatOptions} vatOptions
*/
async function loadTestVat(vatID, setup, creationOptions) {
async function loadTestVat(vatID, setup, vatOptions) {
const translators = provideTranslators(vatID);

const manager = await vatLoader.loadTestVat(
vatID,
setup,
translators,
creationOptions,
vatOptions,
);

const { enablePipelining = false } = creationOptions;
const { enablePipelining = false } = vatOptions;

const result = {
manager,
Expand Down
28 changes: 28 additions & 0 deletions packages/SwingSet/src/lib/workerOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @param {import("../types-external").KernelKeeper} kernelKeeper
* @param {string} [managerType]
* @returns {Promise<import("../types-internal").WorkerOptions>}
*/
export async function makeWorkerOptions(kernelKeeper, managerType) {
managerType = managerType || kernelKeeper.getDefaultManagerType();
if (managerType === 'local') {
return harden({ type: 'local' });
} else if (managerType === 'xs-worker') {
return harden({ type: 'xsnap' });
}
throw Error(`unknown managerType '${managerType}'`);
}

/**
* @param {import("../types-internal").WorkerOptions} origWorkerOptions
* @returns {Promise<import("../types-internal").WorkerOptions>}
*/
export async function updateWorkerOptions(origWorkerOptions) {
const { type } = origWorkerOptions;
if (type === 'local') {
return origWorkerOptions;
} else if (type === 'xsnap') {
return harden({ ...origWorkerOptions });
}
throw Error(`unknown worker type '${type}'`);
}
16 changes: 14 additions & 2 deletions packages/SwingSet/src/types-internal.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
export {};

/**
* The internal data that controls which worker we use (and how we use
* it) is stored in a WorkerOptions record, which comes in "local" and
* "xsnap" flavors.
*
* @typedef { { type: 'local' } } LocalWorkerOptions
* @typedef { { type: 'xsnap' } } XSnapWorkerOptions
* @typedef { { type: 'xsnap', bundleIDs: BundleID[] } } TODOXSnapWorkerOptions
* bundleIDs indicate the SES lockdown and supervisor/liveslots bundles to
* evaluate into a new xsnap worker
* @typedef { LocalWorkerOptions | XSnapWorkerOptions } WorkerOptions
*
* @typedef { string } VatID
* @typedef { string } MeterID
* @typedef { { meterID?: MeterID } } HasMeterID
* @typedef { import('./types-external.js').BaseVatOptions } BaseVatOptions
* @typedef { import('./types-external.js').HasManagerType } HasManagerType
* @typedef { { workerOptions: WorkerOptions } } HasWorkerOptions
* @typedef { import('./types-external.js').HasEnableDisavow } HasEnableDisavow
* @typedef { import('@agoric/swingset-liveslots').VatDeliveryObject } VatDeliveryObject
* @typedef { import('@agoric/swingset-liveslots').VatDeliveryResult } VatDeliveryResult
*
* // used by vatKeeper.setSourceAndOptions(source, RecordedVatOptions)
*
* @typedef { BaseVatOptions & HasMeterID & HasManagerType } InternalDynamicVatOptions
* @typedef { BaseVatOptions & HasMeterID & HasManagerType & HasEnableDisavow } RecordedVatOptions
* @typedef { BaseVatOptions & HasMeterID & HasWorkerOptions & HasEnableDisavow } RecordedVatOptions
*
* @typedef {{
* enablePipelining?: boolean,
* managerType: ManagerType,
* workerOptions: WorkerOptions,
* metered?: boolean,
* critical?: boolean,
* enableDisavow?: boolean,
Expand Down
4 changes: 2 additions & 2 deletions packages/SwingSet/test/test-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ test('XS bootstrap', async t => {
const vatID = c.vatNameToID('bootstrap');
const options = JSON.parse(kernelStorage.kvStore.get(`${vatID}.options`));
t.is(
options.managerType,
'xs-worker',
options.workerOptions.type,
'xsnap',
'managerType gets recorded for the bootstrap vat',
);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/SwingSet/test/test-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ test('vatKeeper.getOptions', async t => {
vk.setSourceAndOptions(
{ bundleID },
{
managerType: 'local',
workerOptions: { type: 'local' },
name: 'fred',
},
);
Expand Down
3 changes: 2 additions & 1 deletion packages/SwingSet/test/test-xsnap-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ test('child termination distinguished from meter exhaustion', async t => {
const fn = new URL('vat-xsnap-hang.js', import.meta.url).pathname;
const bundle = await bundleSource(fn);

const workerOptions = { type: 'xsnap' };
/** @type { ManagerOptions } */
// @ts-expect-error close enough for this test
const managerOptions = { useTranscript: true };
const managerOptions = { useTranscript: true, workerOptions };
const schandler = _vso => ['ok', null];

const m = await xsWorkerFactory.createFromBundle(
Expand Down

0 comments on commit 9e7034b

Please sign in to comment.