Skip to content

Commit

Permalink
feat(swingset): boot xsnap workers from snapshot
Browse files Browse the repository at this point in the history
  • Loading branch information
dckc committed Apr 1, 2021
1 parent b8e4631 commit 308c4ff
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 28 deletions.
2 changes: 2 additions & 0 deletions packages/SwingSet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@
"@babel/core": "^7.5.0",
"@babel/generator": "^7.6.4",
"@endo/base64": "^0.1.0",
"@types/tmp": "^0.2.0",
"anylogger": "^0.21.0",
"esm": "^3.2.5",
"re2": "^1.10.5",
"rollup": "^1.23.1",
"rollup-plugin-node-resolve": "^5.2.0",
"semver": "^6.3.0",
"ses": "^0.12.6",
"tmp": "^0.2.1",
"yargs": "^14.2.0"
},
"files": [
Expand Down
79 changes: 61 additions & 18 deletions packages/SwingSet/src/controller.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* global require */
import fs from 'fs';
import path from 'path';
import process from 'process';
import re2 from 're2';
import { spawn } from 'child_process';
Expand All @@ -9,14 +10,15 @@ import * as babelCore from '@babel/core';
import * as babelParser from '@agoric/babel-parser';
import babelGenerate from '@babel/generator';
import anylogger from 'anylogger';
import { tmpName } from 'tmp';

import { assert, details as X } from '@agoric/assert';
import { isTamed, tameMetering } from '@agoric/tame-metering';
import { importBundle } from '@agoric/import-bundle';
import { initSwingStore } from '@agoric/swing-store-simple';
import { makeMeteringTransformer } from '@agoric/transform-metering';
import { makeTransform } from '@agoric/transform-eventual-send';
import { xsnap } from '@agoric/xsnap';
import { xsnap, makeSnapstore } from '@agoric/xsnap';

import { WeakRef, FinalizationRegistry } from './weakref';
import { startSubprocessWorker } from './spawnSubprocessWorker';
Expand All @@ -42,6 +44,56 @@ function unhandledRejectionHandler(e) {
console.error('UnhandledPromiseRejectionWarning:', e);
}

function makeStartXSnap(bundles, { snapstorePath, env }) {
const xsnapOpts = {
os: osType(),
spawn,
stdout: 'inherit',
stderr: 'inherit',
debug: !!env.XSNAP_DEBUG,
};

let snapStore;

if (snapstorePath) {
fs.mkdirSync(snapstorePath, { recursive: true });

snapStore = makeSnapstore(snapstorePath, {
tmpName,
existsSync: fs.existsSync,
createReadStream: fs.createReadStream,
createWriteStream: fs.createWriteStream,
rename: fs.promises.rename,
unlink: fs.promises.unlink,
resolve: path.resolve,
});
}

let supervisorHash = '';
return async function startXSnap(name, handleCommand) {
if (supervisorHash) {
return snapStore.load(supervisorHash, async snapshot => {
const xs = xsnap({ snapshot, name, handleCommand, ...xsnapOpts });
await xs.evaluate('null'); // ensure that spawn is done
return xs;
});
}
const worker = xsnap({ handleCommand, name, ...xsnapOpts });

for await (const bundle of bundles) {
assert(
bundle.moduleFormat === 'getExport',
X`unexpected: ${bundle.moduleFormat}`,
);
await worker.evaluate(`(${bundle.source}\n)()`.trim());
}
if (snapStore) {
supervisorHash = await snapStore.save(async fn => worker.snapshot(fn));
}
return worker;
};
}

export async function makeSwingsetController(
hostStorage = initSwingStore().storage,
deviceEndowments = {},
Expand All @@ -59,6 +111,7 @@ export async function makeSwingsetController(
slogCallbacks,
slogFile,
testTrackDecref,
snapstorePath,
} = runtimeOptions;
if (typeof Compartment === 'undefined') {
throw Error('SES must be installed before calling makeSwingsetController');
Expand Down Expand Up @@ -177,23 +230,11 @@ export async function makeSwingsetController(
return startSubprocessWorker(process.execPath, ['-r', 'esm', supercode]);
}

const startXSnap = (name, handleCommand) => {
const worker = xsnap({
os: osType(),
spawn,
handleCommand,
name,
stdout: 'inherit',
stderr: 'inherit',
debug: !!env.XSNAP_DEBUG,
});

const bundles = {
lockdown: JSON.parse(hostStorage.get('lockdownBundle')),
supervisor: JSON.parse(hostStorage.get('supervisorBundle')),
};
return harden({ worker, bundles });
};
const bundles = [
JSON.parse(hostStorage.get('lockdownBundle')),
JSON.parse(hostStorage.get('supervisorBundle')),
];
const startXSnap = makeStartXSnap(bundles, { snapstorePath, env });

const slogF =
slogFile && (await fs.createWriteStream(slogFile, { flags: 'a' })); // append
Expand Down Expand Up @@ -323,12 +364,14 @@ export async function buildVatController(
debugPrefix,
slogCallbacks,
testTrackDecref,
snapstorePath,
} = runtimeOptions;
const actualRuntimeOptions = {
verbose,
debugPrefix,
testTrackDecref,
slogCallbacks,
snapstorePath,
};
const initializationOptions = { verbose, kernelBundles };
let bootstrapResult;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const decoder = new TextDecoder();
* @param {{
* allVatPowers: VatPowers,
* kernelKeeper: KernelKeeper,
* startXSnap: (name: string, handleCommand: SyncHandler) => { worker: XSnap, bundles: Record<string, ExportBundle> },
* startXSnap: (name: string, handleCommand: SyncHandler) => Promise<XSnap>,
* testLog: (...args: unknown[]) => void,
* decref: (vatID: unknown, vref: unknown, count: number) => void,
* }} tools
Expand Down Expand Up @@ -128,15 +128,7 @@ export function makeXsSubprocessFactory({
}

// start the worker and establish a connection
const { worker, bundles } = startXSnap(`${vatID}:${name}`, handleCommand);
for await (const [it, superCode] of Object.entries(bundles)) {
parentLog(vatID, 'eval bundle', it);
assert(
superCode.moduleFormat === 'getExport',
X`${it} unexpected: ${superCode.moduleFormat}`,
);
await worker.evaluate(`(${superCode.source}\n)()`.trim());
}
const worker = await startXSnap(`${vatID}:${name}`, handleCommand);

/** @type { (item: Tagged) => Promise<CrankResults> } */
async function issueTagged(item) {
Expand Down

0 comments on commit 308c4ff

Please sign in to comment.