Skip to content

Commit 655773e

Browse files
committed
add failing tests: syscalls GC sensitivity via reanimation
This exercises three cases in which syscalls are sensitive to GC activity: * reanimateDurableKindID, when regenerating a KindHandle (#7142) * reanimate, when regenerating a plain Representative (#7142) * reanimateCollection, when regenerating a virtual collection (#6360)
1 parent 7e74cfd commit 655773e

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import test from 'ava';
2+
import '@endo/init/debug.js';
3+
import { Far } from '@endo/marshal';
4+
import { buildSyscall } from './liveslots-helpers.js';
5+
import { makeLiveSlots } from '../src/liveslots.js';
6+
import { kser } from './kmarshal.js';
7+
import { makeMockGC } from './mock-gc.js';
8+
import { makeMessage, makeStartVat } from './util.js';
9+
10+
test.failing('kind handle reanimation', async t => {
11+
const { syscall, log } = buildSyscall();
12+
const gcTools = makeMockGC();
13+
14+
function buildRootObject(vatPowers, vatParameters, baggage) {
15+
const { VatData } = vatPowers;
16+
const kh0 = VatData.makeKindHandle('kh');
17+
VatData.defineDurableKind(kh0, () => ({}), {});
18+
baggage.init('kh', kh0);
19+
20+
const root = Far('root', {
21+
fetch1() {
22+
// console.log(`--fetch1`);
23+
baggage.get('kh');
24+
},
25+
gc() {
26+
// console.log(`--gc`);
27+
gcTools.kill(kh0);
28+
gcTools.flushAllFRs();
29+
},
30+
fetch2() {
31+
// console.log(`--fetch2`);
32+
baggage.get('kh');
33+
},
34+
});
35+
return root;
36+
}
37+
38+
const rootA = 'o+0';
39+
const ls = makeLiveSlots(syscall, 'vatA', {}, {}, gcTools, undefined, () => ({
40+
buildRootObject,
41+
}));
42+
const { dispatch } = ls;
43+
await dispatch(makeStartVat(kser()));
44+
45+
// Imagine a vat which allocates a KindID for 'kh', and stores it in
46+
// baggage, and then drops the Representative (the Handle). Then
47+
// time passes, during which GC may or may not happen, and then the
48+
// vat pulls 'kh' out of baggage.
49+
50+
log.length = 0;
51+
// this simulates the GC-did-not-happen case: the kh0 Representative
52+
// is still around from buildRootObject (liveslots has not seen the
53+
// FinalizationRegistry fire, and the WeakRef is still populated)
54+
await dispatch(makeMessage(rootA, 'fetch1', []));
55+
const noGCLog = [...log];
56+
log.length = 0;
57+
58+
// this simulates the GC-did-happen case: liveslots has seen kh0
59+
// die, so it must reanimate a new one
60+
await dispatch(makeMessage(rootA, 'gc', []));
61+
log.length = 0;
62+
await dispatch(makeMessage(rootA, 'fetch2', []));
63+
const yesGCLog = [...log];
64+
log.length = 0;
65+
66+
// we need the syscall behavior of both cases to be the same
67+
t.deepEqual(noGCLog, yesGCLog);
68+
});
69+
70+
test.failing('representative reanimation', async t => {
71+
const { syscall, log } = buildSyscall();
72+
const gcTools = makeMockGC();
73+
74+
function buildRootObject(vatPowers, vatParameters, baggage) {
75+
const { VatData } = vatPowers;
76+
const kh0 = VatData.makeKindHandle('kh');
77+
const behavior = { get: ({ state }) => state.data };
78+
const initState = { data: 0 };
79+
const make = VatData.defineDurableKind(kh0, () => initState, behavior);
80+
const r0 = make();
81+
baggage.init('k', r0);
82+
const r1 = make();
83+
// knock r0.innerSelf out of the cache, leave only r1
84+
make();
85+
r1.get();
86+
87+
const root = Far('root', {
88+
fetch1() {
89+
// console.log(`--fetch1`);
90+
baggage.get('k');
91+
},
92+
gc() {
93+
// console.log(`--gc`);
94+
gcTools.kill(r0);
95+
gcTools.flushAllFRs();
96+
// knock r0.innerSelf out of the cache, leave only r1
97+
make();
98+
r1.get();
99+
},
100+
fetch2() {
101+
// console.log(`--fetch2`);
102+
baggage.get('k');
103+
},
104+
});
105+
return root;
106+
}
107+
108+
const rootA = 'o+0';
109+
const opts = { virtualObjectCacheSize: 0 };
110+
const ls = makeLiveSlots(
111+
syscall,
112+
'vatA',
113+
{},
114+
opts,
115+
gcTools,
116+
undefined,
117+
() => ({
118+
buildRootObject,
119+
}),
120+
);
121+
const { dispatch } = ls;
122+
await dispatch(makeStartVat(kser()));
123+
124+
// Imagine a vat which creates an initial Representative of some
125+
// Kind and stores it in baggage, then drops the Representative (the
126+
// Handle). Then time passes, during which GC may or may not happen,
127+
// and then the vat pulls it back out of baggage.
128+
129+
log.length = 0;
130+
// this simulates the GC-did-not-happen case: the r0 Representative
131+
// is still around from buildRootObject (liveslots has not seen the
132+
// FinalizationRegistry fire, and the WeakRef is still populated)
133+
await dispatch(makeMessage(rootA, 'fetch1', []));
134+
const noGCLog = [...log];
135+
log.length = 0;
136+
137+
// this simulates the GC-did-happen case: liveslots has seen r0 die,
138+
// so it must reanimate a new one
139+
await dispatch(makeMessage(rootA, 'gc', []));
140+
log.length = 0;
141+
await dispatch(makeMessage(rootA, 'fetch2', []));
142+
const yesGCLog = [...log];
143+
log.length = 0;
144+
145+
// we need the syscall behavior of both cases to be the same
146+
t.deepEqual(noGCLog, yesGCLog);
147+
});
148+
149+
test.failing('collection reanimation', async t => {
150+
const { syscall, log } = buildSyscall();
151+
const gcTools = makeMockGC();
152+
153+
function buildRootObject(vatPowers, vatParameters, baggage) {
154+
const { VatData } = vatPowers;
155+
const c0 = VatData.makeScalarBigMapStore('c', { durable: true });
156+
baggage.init('c', c0);
157+
158+
const root = Far('root', {
159+
fetch1() {
160+
// console.log(`--fetch1`);
161+
baggage.get('c');
162+
},
163+
gc() {
164+
// console.log(`--gc`);
165+
gcTools.kill(c0);
166+
gcTools.flushAllFRs();
167+
},
168+
fetch2() {
169+
// console.log(`--fetch2`);
170+
baggage.get('c');
171+
},
172+
});
173+
return root;
174+
}
175+
176+
const rootA = 'o+0';
177+
const ls = makeLiveSlots(syscall, 'vatA', {}, {}, gcTools, undefined, () => ({
178+
buildRootObject,
179+
}));
180+
const { dispatch } = ls;
181+
await dispatch(makeStartVat(kser()));
182+
183+
// Imagine a vat which creates a durable collection 'c0' and stores
184+
// it in baggage, and then drops the Representative. Then time
185+
// passes, during which GC may or may not happen, and then the vat
186+
// pulls 'c' out of baggage.
187+
188+
log.length = 0;
189+
// this simulates the GC-did-not-happen case: the c0 Representative
190+
// is still around from buildRootObject (liveslots has not seen the
191+
// FinalizationRegistry fire, and the WeakRef is still populated)
192+
await dispatch(makeMessage(rootA, 'fetch1', []));
193+
const noGCLog = [...log];
194+
log.length = 0;
195+
196+
// this simulates the GC-did-happen case: liveslots has seen c0
197+
// die, so it must reanimate a new one
198+
await dispatch(makeMessage(rootA, 'gc', []));
199+
log.length = 0;
200+
await dispatch(makeMessage(rootA, 'fetch2', []));
201+
const yesGCLog = [...log];
202+
log.length = 0;
203+
204+
// we need the syscall behavior of both cases to be the same
205+
t.deepEqual(noGCLog, yesGCLog);
206+
});

0 commit comments

Comments
 (0)