Skip to content

Commit

Permalink
Merge branch 'master' into 6641-auctioneer
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Mar 17, 2023
2 parents b39fe18 + 9d47dd0 commit a2bc45a
Show file tree
Hide file tree
Showing 58 changed files with 3,327 additions and 1,044 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ if (!dirPath) {
if (!isSwingStore(dirPath)) {
throw Error(`${dirPath} does not appear to be a swingstore (no ./data.mdb)`);
}
const { kvStore, streamStore } = openSwingStore(dirPath).kernelStorage;
const { kvStore, transcriptStore } = openSwingStore(dirPath).kernelStorage;
function get(key) {
return kvStore.get(key);
}
Expand Down Expand Up @@ -98,7 +98,7 @@ if (!vatName) {
fs.writeSync(fd, JSON.stringify(first));
fs.writeSync(fd, '\n');

// The streamStore holds concatenated transcripts from all upgraded
// The transcriptStore holds concatenated transcripts from all upgraded
// versions. For each old version, it holds every delivery from
// `startVat` through `stopVat`. For the current version, it holds
// every delivery from `startVat` up through the last delivery
Expand All @@ -123,9 +123,8 @@ if (!vatName) {
console.log(`${transcriptLength} transcript entries`);

let deliveryNum = 0;
const transcriptStream = `transcript-${vatID}`;
const stream = streamStore.readStream(transcriptStream, startPos, endPos);
for (const entry of stream) {
const transcript = transcriptStore.readSpan(vatID, startPos, endPos);
for (const entry of transcript) {
// entry is JSON.stringify({ d, syscalls }), syscall is { d, response }
const t = { transcriptNum, ...JSON.parse(entry) };
// console.log(`t.${deliveryNum} : ${t}`);
Expand Down
2 changes: 1 addition & 1 deletion packages/SwingSet/misc-tools/replay-transcript.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ async function replay(transcriptFile) {
return loadRaw(snapFile);
},
}
: makeSnapStore(process.cwd(), makeSnapStoreIO());
: makeSnapStore(process.cwd(), () => {}, makeSnapStoreIO());
const testLog = undefined;
const meterControl = makeDummyMeterControl();
const gcTools = harden({
Expand Down
101 changes: 68 additions & 33 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,11 @@ export default function buildKernel(
// check will report 'false'. That's fine, there's no state to
// clean up.
if (kernelKeeper.vatIsAlive(vatID)) {
const promisesToReject = kernelKeeper.cleanupAfterTerminatedVat(vatID);
for (const kpid of promisesToReject) {
// Reject all promises decided by the vat, making sure to capture the list
// of kpids before that data is deleted.
const deadPromises = [...kernelKeeper.enumeratePromisesByDecider(vatID)];
kernelKeeper.cleanupAfterTerminatedVat(vatID);
for (const kpid of deadPromises) {
resolveToError(kpid, makeError('vat terminated'), vatID);
}
}
Expand Down Expand Up @@ -818,52 +821,58 @@ export default function buildKernel(
upgradeMessage,
incarnationNumber: vatKeeper.getIncarnationNumber(),
};
const disconnectionCapData = kser(disconnectObject);
/** @type { import('../types-external.js').KernelDeliveryStopVat } */
const kd1 = harden(['stopVat', kser(disconnectObject)]);
const vd1 = vatWarehouse.kernelDeliveryToVatDelivery(vatID, kd1);
const status1 = await deliverAndLogToVat(vatID, kd1, vd1);
const stopVatKD = harden(['stopVat', disconnectionCapData]);
const stopVatVD = vatWarehouse.kernelDeliveryToVatDelivery(
vatID,
stopVatKD,
);
const stopVatStatus = await deliverAndLogToVat(vatID, stopVatKD, stopVatVD);
const stopVatResults = deliveryCrankResults(vatID, stopVatStatus, false);

// We don't meter stopVat, since no user code is running, but we
// still report computrons to the runPolicy
let { computrons } = stopVatResults; // BigInt or undefined
if (computrons !== undefined) {
assert.typeof(computrons, 'bigint');
}

// make arguments for vat-vat-admin.js vatUpgradeCallback()
/**
* @param {SwingSetCapData} _errorCD
* Make a method-arguments structure representing failure
* for vat-vat-admin.js vatUpgradeCallback().
*
* @param {SwingSetCapData} _errorCapData
* @returns {RawMethargs}
*/
function makeFailure(_errorCD) {
insistCapData(_errorCD); // kser(Error)
const makeFailureMethargs = _errorCapData => {
insistCapData(_errorCapData); // kser(Error)
// const error = kunser(_errorCD)
// actually we shouldn't reveal the details, so instead we do:
const error = Error('vat-upgrade failure');
return ['vatUpgradeCallback', [upgradeID, false, error]];
}

// We use deliveryCrankResults to parse the stopVat status.
const results1 = deliveryCrankResults(vatID, status1, false);

// We don't meter stopVat, since no user code is running, but we
// still report computrons to the runPolicy
let { computrons } = results1; // BigInt or undefined
if (computrons !== undefined) {
assert.typeof(computrons, 'bigint');
}
};

// TODO: if/when we implement vat pause/suspend, and if
// deliveryCrankResults changes to not use .terminate to indicate
// a problem, this should change to match: where we would normally
// pause/suspend a vat for a delivery error, here we want to
// unwind the upgrade.

if (results1.terminate) {
if (stopVatResults.terminate) {
// get rid of the worker, so the next delivery to this vat will
// re-create one from the previous state
// eslint-disable-next-line @jessie.js/no-nested-await
await vatWarehouse.stopWorker(vatID);

// notify vat-admin of the failed upgrade
const vatAdminMethargs = makeFailure(results1.terminate.info);
const vatAdminMethargs = makeFailureMethargs(
stopVatResults.terminate.info,
);

// we still report computrons to the runPolicy
const results = harden({
...results1,
...stopVatResults,
computrons,
abort: true, // always unwind
consumeMessage: true, // don't repeat the upgrade
Expand All @@ -873,8 +882,25 @@ export default function buildKernel(
return results;
}

// stopVat succeeded, so now we stop the worker, delete the
// transcript and any snapshot
// stopVat succeeded. finish cleanup on behalf of the worker.

// TODO: send BOYD to the vat, to give it one last chance to clean
// up, drop imports, and delete durable data. If we ever have a
// vat that is so broken it can't do BOYD, we can make that
// optional. #7001

// walk c-list for all decided promises, reject them all
for (const kpid of kernelKeeper.enumeratePromisesByDecider(vatID)) {
resolveToError(kpid, disconnectionCapData, vatID);
}

// TODO: getNonDurableObjectExports, synthesize abandonVSO,
// execute it as if it were a syscall. (maybe distinguish between
// reachable/recognizable exports, abandon the reachable, retire
// the recognizable) #6696

// cleanup done, now we stop the worker, delete the transcript and
// any snapshot

await vatWarehouse.resetWorker(vatID);
const source = { bundleID };
Expand All @@ -888,23 +914,32 @@ export default function buildKernel(

// deliver a startVat with the new vatParameters
/** @type { import('../types-external.js').KernelDeliveryStartVat } */
const kd2 = harden(['startVat', vatParameters]);
const vd2 = vatWarehouse.kernelDeliveryToVatDelivery(vatID, kd2);
const startVatKD = harden(['startVat', vatParameters]);
const startVatVD = vatWarehouse.kernelDeliveryToVatDelivery(
vatID,
startVatKD,
);
// decref vatParameters now that translation did incref
for (const kref of vatParameters.slots) {
kernelKeeper.decrementRefCount(kref, 'upgrade-vat-event');
}
const status2 = await deliverAndLogToVat(vatID, kd2, vd2);
const results2 = deliveryCrankResults(vatID, status2, false);
computrons = addComputrons(computrons, results2.computrons);
const startVatStatus = await deliverAndLogToVat(
vatID,
startVatKD,
startVatVD,
);
const startVatResults = deliveryCrankResults(vatID, startVatStatus, false);
computrons = addComputrons(computrons, startVatResults.computrons);

if (results2.terminate) {
if (startVatResults.terminate) {
// unwind just like above
// eslint-disable-next-line @jessie.js/no-nested-await
await vatWarehouse.stopWorker(vatID);
const vatAdminMethargs = makeFailure(results2.terminate.info);
const vatAdminMethargs = makeFailureMethargs(
startVatResults.terminate.info,
);
const results = harden({
...results2,
...startVatResults,
computrons,
abort: true, // always unwind
consumeMessage: true, // don't repeat the upgrade
Expand Down
4 changes: 1 addition & 3 deletions packages/SwingSet/src/kernel/kernelQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ export function makeKernelQueueHandler(tools) {
const p = kernelKeeper.getResolveablePromise(kpid, vatID);
const { subscribers } = p;
for (const subscriber of subscribers) {
if (subscriber !== vatID) {
notify(subscriber, kpid);
}
notify(subscriber, kpid);
}
kernelKeeper.resolveKernelPromise(kpid, rejected, data);
const tag = rejected ? 'rejected' : 'fulfilled';
Expand Down
74 changes: 29 additions & 45 deletions packages/SwingSet/src/kernel/state/kernelKeeper.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ const enableKernelGC = true;
* @typedef { import('../../types-external.js').KernelSlog } KernelSlog
* @typedef { import('../../types-external.js').ManagerType } ManagerType
* @typedef { import('../../types-external.js').SnapStore } SnapStore
* @typedef { import('../../types-external.js').StreamPosition } StreamPosition
* @typedef { import('../../types-external.js').StreamStore } StreamStore
* @typedef { import('../../types-external.js').TranscriptStore } TranscriptStore
* @typedef { import('../../types-external.js').VatKeeper } VatKeeper
* @typedef { import('../../types-external.js').VatManager } VatManager
*/
Expand Down Expand Up @@ -86,8 +85,6 @@ const enableKernelGC = true;
// $vatSlot is one of: o+$NN/o-$NN/p+$NN/p-$NN/d+$NN/d-$NN
// v$NN.c.$vatSlot = $kernelSlot = ko$NN/kp$NN/kd$NN
// v$NN.nextDeliveryNum = $NN
// v$NN.t.startPosition = $NN // inclusive
// v$NN.t.endPosition = $NN // exclusive
// v$NN.vs.$key = string
// v$NN.meter = m$NN // XXX does this exist?
// v$NN.reapInterval = $NN or 'never'
Expand Down Expand Up @@ -174,7 +171,7 @@ const FIRST_METER_ID = 1n;
* @param {KernelSlog|null} kernelSlog
*/
export default function makeKernelKeeper(kernelStorage, kernelSlog) {
const { kvStore, streamStore, snapStore } = kernelStorage;
const { kvStore, transcriptStore, snapStore } = kernelStorage;

insistStorageAPI(kvStore);

Expand Down Expand Up @@ -784,10 +781,8 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
const vatKeeper = provideVatKeeper(vatID);
const exportPrefix = `${vatID}.c.o+`;
const importPrefix = `${vatID}.c.o-`;
const promisePrefix = `${vatID}.c.p`;
const kernelPromisesToReject = [];

vatKeeper.deleteSnapshots();
vatKeeper.deleteSnapshotsAndTranscript();

// Note: ASCII order is "+,-./", and we rely upon this to split the
// keyspace into the various o+NN/o-NN/etc spaces. If we were using a
Expand Down Expand Up @@ -825,23 +820,8 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
// that will also delete both db keys
}

// now find all orphaned promises, which must be rejected
for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
// The vpid for a promise imported or exported by a vat (and thus
// potentially a promise for which the vat *might* be the decider) will
// always be of the form `p+NN` or `p-NN`. The corresponding vpid->kpid
// c-list entry will thus always begin with `vMM.c.p`. Decider-ship is
// independent of whether the promise was imported or exported, so we
// have to look up the corresponding kernel promise table entry to see
// whether the vat is the decider or not. If it is, we add the promise
// to the list of promises that must be rejected because the dead vat
// will never be able to act upon them.
const kpid = kvStore.get(k);
const p = getKernelPromise(kpid);
if (p.state === 'unresolved' && p.decider === vatID) {
kernelPromisesToReject.push(kpid);
}
}
// the caller used enumeratePromisesByDecider() before calling us,
// so they already know the orphaned promises to reject

// now loop back through everything and delete it all
for (const k of enumeratePrefixedKeys(kvStore, `${vatID}.`)) {
Expand Down Expand Up @@ -873,8 +853,6 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
}
decStat('vats');
}

return kernelPromisesToReject;
}

function addMessageToPromiseQueue(kernelSlot, msg) {
Expand Down Expand Up @@ -928,6 +906,27 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
kvStore.set(`${kpid}.decider`, '');
}

function* enumeratePromisesByDecider(vatID) {
insistVatID(vatID);
const promisePrefix = `${vatID}.c.p`;
for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
// The vpid for a promise imported or exported by a vat (and thus
// potentially a promise for which the vat *might* be the decider) will
// always be of the form `p+NN` or `p-NN`. The corresponding vpid->kpid
// c-list entry will thus always begin with `vMM.c.p`. Decider-ship is
// independent of whether the promise was imported or exported, so we
// have to look up the corresponding kernel promise table entry to see
// whether the vat is the decider or not. If it is, we add the promise
// to the list of promises that must be rejected because the dead vat
// will never be able to act upon them.
const kpid = kvStore.get(k);
const p = getKernelPromise(kpid);
if (p.state === 'unresolved' && p.decider === vatID) {
yield kpid;
}
}
}

function addSubscriberToPromise(kernelSlot, vatID) {
insistKernelType('promise', kernelSlot);
insistVatID(vatID);
Expand Down Expand Up @@ -1297,11 +1296,11 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
return found;
}
if (!kvStore.has(`${vatID}.o.nextID`)) {
initializeVatState(kvStore, streamStore, vatID);
initializeVatState(kvStore, transcriptStore, vatID);
}
const vk = makeVatKeeper(
kvStore,
streamStore,
transcriptStore,
kernelSlog,
vatID,
addKernelObject,
Expand All @@ -1328,20 +1327,6 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
}

/**
* Cease writing to the vat's transcript.
*
* @param {string} vatID
*/
function closeVatTranscript(vatID) {
insistVatID(vatID);
const transcriptStream = `transcript-${vatID}`;
streamStore.closeStream(transcriptStream);
}

/**
* NOTE: caller is responsible to closeVatTranscript()
* before evicting a VatKeeper.
*
* @param {string} vatID
*/
function evictVatKeeper(vatID) {
Expand Down Expand Up @@ -1448,7 +1433,6 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
if (vk) {
// TODO: find some way to expose the liveSlots internal tables, the
// kernel doesn't see them
closeVatTranscript(vatID);
const vatTable = {
vatID,
state: { transcript: Array.from(vk.getTranscript()) },
Expand Down Expand Up @@ -1586,6 +1570,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
addSubscriberToPromise,
setDecider,
clearDecider,
enumeratePromisesByDecider,
incrementRefCount,
decrementRefCount,
getObjectRefCount,
Expand Down Expand Up @@ -1615,7 +1600,6 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
provideVatKeeper,
vatIsAlive,
evictVatKeeper,
closeVatTranscript,
cleanupAfterTerminatedVat,
addDynamicVatID,
getDynamicVats,
Expand Down
Loading

0 comments on commit a2bc45a

Please sign in to comment.