Skip to content

Commit d18ddf5

Browse files
committed
fix(swingset): do not record GC syscalls in the transcript
Consensus mode will depend upon GC being deterministic, but solo mode does not. Solo mode requires GC be "sufficiently deterministic", which means a finalizer may or may not run in any given crank. To support this, we must not record the GC-related syscalls (dropImport, retireImport, retireExport) in the transcript. When replaying a transcript, we ignore these syscalls as well. closes #3146 refs #2615 refs #2660 refs #2724
1 parent 3b0662e commit d18ddf5

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

packages/SwingSet/src/kernel/vatManager/transcript.js

+10
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ export function makeTranscriptManager(
2626
};
2727
}
2828

29+
const gcSyscalls = new Set(['dropImports', 'retireImports', 'retireExports']);
30+
2931
function addSyscall(d, response) {
32+
const type = d[0];
33+
if (gcSyscalls.has(type)) {
34+
return;
35+
}
3036
if (currentEntry) {
3137
currentEntry.syscalls.push({ d, response });
3238
}
@@ -59,6 +65,10 @@ export function makeTranscriptManager(
5965
let replayError;
6066

6167
function simulateSyscall(newSyscall) {
68+
const type = newSyscall[0];
69+
if (gcSyscalls.has(type)) {
70+
return undefined;
71+
}
6272
const s = playbackSyscalls.shift();
6373
const newReplayError = compareSyscalls(vatID, s.d, newSyscall);
6474
if (newReplayError) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// eslint-disable-next-line import/order
2+
import { test } from '../tools/prepare-test-env-ava';
3+
4+
import { makeDummySlogger } from '../src/kernel/slogger';
5+
import { makeManagerKit } from '../src/kernel/vatManager/manager-helper';
6+
7+
const m1 = ['message', { method: 'foo', args: { body: '', slots: [] } }];
8+
9+
function setup(storedTranscript = []) {
10+
const vatID = 'vatID';
11+
const slog = makeDummySlogger({}, () => console);
12+
const transcript = [];
13+
const vatKeeper = {
14+
addToTranscript(entry) {
15+
transcript.push(entry);
16+
},
17+
vatStats() {
18+
return { transcriptCount: storedTranscript.length };
19+
},
20+
getTranscript() {
21+
return storedTranscript;
22+
},
23+
};
24+
const kernelKeeper = {
25+
getVatKeeper() {
26+
return vatKeeper;
27+
},
28+
};
29+
function vatSyscallHandler(_vso) {
30+
return ['ok', null];
31+
}
32+
const workerCanBlock = false;
33+
const mk = makeManagerKit(
34+
vatID,
35+
slog,
36+
kernelKeeper,
37+
vatSyscallHandler,
38+
workerCanBlock,
39+
);
40+
const { syscallFromWorker } = mk;
41+
function deliver(_delivery) {
42+
// a syscall.subscribe is included in the transcript
43+
syscallFromWorker(['subscribe', 'p-1']);
44+
// but GC syscalls are not
45+
syscallFromWorker(['dropImports', ['o-1']]);
46+
syscallFromWorker(['retireImports', ['o-1']]);
47+
syscallFromWorker(['retireExports', ['o+2']]);
48+
syscallFromWorker(['subscribe', 'p-2']);
49+
return Promise.resolve(['ok', null, { usage: 0 }]);
50+
}
51+
mk.setDeliverToWorker(deliver);
52+
function shutdown() {}
53+
const manager = mk.getManager(shutdown);
54+
return { manager, transcript };
55+
}
56+
57+
test('gc syscalls are not included in transcript', async t => {
58+
const { manager, transcript } = setup();
59+
await manager.deliver(m1);
60+
61+
t.is(transcript.length, 1);
62+
t.deepEqual(transcript[0], {
63+
d: m1,
64+
syscalls: [
65+
{ d: ['subscribe', 'p-1'], response: null },
66+
{ d: ['subscribe', 'p-2'], response: null },
67+
],
68+
});
69+
});
70+
71+
test('gc syscalls are ignored during replay', async t => {
72+
const storedTranscript = [
73+
{
74+
d: m1,
75+
syscalls: [
76+
{ d: ['subscribe', 'p-1'], response: null },
77+
{ d: ['subscribe', 'p-2'], response: null },
78+
],
79+
},
80+
];
81+
const { manager } = setup(storedTranscript);
82+
await manager.replayTranscript();
83+
// success is that replayTranscript didn't throw anachrophobia error
84+
t.pass();
85+
});

0 commit comments

Comments
 (0)