@agoric/swingset-liveslots 0.10.3-other-dev-8f8782b.0 → 0.10.3-other-dev-3eb1a1d.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +27 -19
- package/src/boyd-gc.js +598 -0
- package/src/cache.js +3 -2
- package/src/capdata.js +1 -2
- package/src/collectionManager.js +219 -103
- package/src/facetiousness.js +1 -1
- package/src/index.js +4 -2
- package/src/liveslots.js +131 -301
- package/src/message.js +5 -5
- package/src/parseVatSlots.js +1 -1
- package/src/types-index.d.ts +4 -0
- package/src/types-index.js +2 -0
- package/src/types.js +8 -2
- package/src/vatstore-iterators.js +2 -0
- package/src/virtualObjectManager.js +185 -71
- package/src/virtualReferences.js +135 -26
- package/src/watchedPromises.js +67 -17
- package/test/{test-baggage.js → baggage.test.js} +1 -2
- package/test/{test-cache.js → cache.test.js} +0 -1
- package/test/clear-collection.test.js +586 -0
- package/test/{test-collection-schema-refcount.js → collection-schema-refcount.test.js} +1 -2
- package/test/{test-collection-upgrade.js → collection-upgrade.test.js} +1 -3
- package/test/{test-collections.js → collections.test.js} +158 -14
- package/test/{test-dropped-collection-weakrefs.js → dropped-collection-weakrefs.test.js} +1 -2
- package/test/dropped-weakset-9939.test.js +80 -0
- package/test/dummyMeterControl.js +1 -1
- package/test/{test-durabilityChecks.js → durabilityChecks.test.js} +4 -4
- package/test/exo-utils.js +70 -0
- package/test/{test-facetiousness.js → facetiousness.test.js} +1 -2
- package/test/gc-and-finalize.js +30 -1
- package/test/gc-before-finalizer.test.js +230 -0
- package/test/gc-helpers.js +2 -3
- package/test/{test-gc-sensitivity.js → gc-sensitivity.test.js} +2 -2
- package/test/handled-promises.test.js +506 -0
- package/test/{test-initial-vrefs.js → initial-vrefs.test.js} +2 -3
- package/test/liveslots-helpers.js +12 -7
- package/test/{test-liveslots-mock-gc.js → liveslots-mock-gc.test.js} +101 -2
- package/test/{test-liveslots-real-gc.js → liveslots-real-gc.test.js} +62 -37
- package/test/{test-liveslots.js → liveslots.test.js} +14 -15
- package/test/mock-gc.js +1 -0
- package/test/storeGC/{test-lifecycle.js → lifecycle.test.js} +2 -2
- package/test/storeGC/{test-refcount-management.js → refcount-management.test.js} +1 -2
- package/test/storeGC/{test-scalar-store-kind.js → scalar-store-kind.test.js} +0 -1
- package/test/storeGC/{test-weak-key.js → weak-key.test.js} +1 -2
- package/test/strict-test-env-upgrade.test.js +94 -0
- package/test/util.js +2 -2
- package/test/vat-environment.test.js +65 -0
- package/test/vat-util.js +2 -2
- package/test/virtual-objects/{test-cease-recognition.js → cease-recognition.test.js} +2 -2
- package/test/virtual-objects/{test-cross-facet.js → cross-facet.test.js} +5 -4
- package/test/virtual-objects/{test-empty-data.js → empty-data.test.js} +1 -2
- package/test/virtual-objects/{test-facets.js → facets.test.js} +1 -2
- package/test/virtual-objects/{test-kind-changes.js → kind-changes.test.js} +2 -2
- package/test/virtual-objects/{test-reachable-vrefs.js → reachable-vrefs.test.js} +2 -2
- package/test/virtual-objects/{test-rep-tostring.js → rep-tostring.test.js} +3 -5
- package/test/virtual-objects/{test-retain-remotable.js → retain-remotable.test.js} +25 -24
- package/test/virtual-objects/set-debug-label-instances.js +1 -1
- package/test/virtual-objects/{test-state-shape.js → state-shape.test.js} +2 -2
- package/test/virtual-objects/{test-virtualObjectGC.js → virtualObjectGC.test.js} +2 -2
- package/test/virtual-objects/{test-virtualObjectManager.js → virtualObjectManager.test.js} +126 -8
- package/test/virtual-objects/{test-vo-real-gc.js → vo-real-gc.test.js} +8 -8
- package/test/virtual-objects/{test-weakcollections-vref-handling.js → weakcollections-vref-handling.test.js} +1 -2
- package/test/{test-vo-test-harness.js → vo-test-harness.test.js} +0 -1
- package/test/{test-vpid-liveslots.js → vpid-liveslots.test.js} +105 -5
- package/test/waitUntilQuiescent.js +2 -1
- package/test/weakset-dropped-remotable.test.js +50 -0
- package/tools/fakeCollectionManager.js +44 -0
- package/tools/fakeVirtualObjectManager.js +62 -0
- package/tools/fakeVirtualSupport.js +389 -0
- package/tools/prepare-strict-test-env.js +124 -0
- package/tools/prepare-test-env.js +13 -0
- package/tools/setup-vat-data.js +96 -0
- package/tools/vo-test-harness.js +143 -0
- package/CHANGELOG.md +0 -61
- package/test/kmarshal.js +0 -79
- package/test/test-handled-promises.js +0 -360
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { Far } from '@endo/marshal';
|
|
3
|
+
import { kser, kslot } from '@agoric/kmarshal';
|
|
4
|
+
import { makeLiveSlots } from '../src/liveslots.js';
|
|
5
|
+
import { buildSyscall } from './liveslots-helpers.js';
|
|
6
|
+
import { makeStartVat, makeMessage, makeBringOutYourDead } from './util.js';
|
|
7
|
+
import { makeMockGC } from './mock-gc.js';
|
|
8
|
+
|
|
9
|
+
const justGC = log =>
|
|
10
|
+
log.filter(
|
|
11
|
+
l =>
|
|
12
|
+
l.type === 'dropImports' ||
|
|
13
|
+
l.type === 'retireImports' ||
|
|
14
|
+
l.type === 'retireExports',
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
test('presence in COLLECTED state is not dropped yet', async t => {
|
|
18
|
+
const { syscall, log } = buildSyscall();
|
|
19
|
+
const gcTools = makeMockGC();
|
|
20
|
+
|
|
21
|
+
// our GC terminology (see boyd-gc.js for notes):
|
|
22
|
+
// * REACHABLE means userspace can reach a Presence
|
|
23
|
+
// * UNREACHABLE means it cannot
|
|
24
|
+
// * COLLECTED means the engine GC has noticed, so the
|
|
25
|
+
// slotToVal.get(vref) WeakRef is empty, even though
|
|
26
|
+
// slotToVal.has(vref) is still true. A finalizer
|
|
27
|
+
// callback has been queued.
|
|
28
|
+
// * FINALIZED means the callback has been run. Our callback does
|
|
29
|
+
// slotToVal.delete(vref) before adding the vref to
|
|
30
|
+
// possiblyDeadSet
|
|
31
|
+
|
|
32
|
+
// slotToVal.has() returns true for REACHABLE / UNREACHABLE /
|
|
33
|
+
// COLLECTED, and false for FINALIZED. getValForSlot() is another
|
|
34
|
+
// way to probe for reachability, but it also looks to see if the
|
|
35
|
+
// WeakRef is full, so it returns false for both COLLECTED and
|
|
36
|
+
// FINALIZED.
|
|
37
|
+
|
|
38
|
+
// We use slotToVal.has(vref) as the "is it still reachable" probe
|
|
39
|
+
// in scanForDeadObjects(), because if we have a Presence in the
|
|
40
|
+
// COLLECTED state, we must not dropImports it during this BOYD. The
|
|
41
|
+
// finalizer is still enqueued, so some future turn will run it, and
|
|
42
|
+
// the vref will be added to possiblyDeadSet again. We want the drop
|
|
43
|
+
// to happen exactly once, and we don't remove the finalizer from
|
|
44
|
+
// the queue, which means the drop must happen in the future BOYD.
|
|
45
|
+
|
|
46
|
+
// We can get into this situation with the following sequence:
|
|
47
|
+
// 1: vref is held by both Presence and vdata
|
|
48
|
+
// 2: Presence is dropped by userspace (->UNREACHABLE)
|
|
49
|
+
// 3: userspace store.delete(), drops vdata refcount, addToPossiblyDeadSet
|
|
50
|
+
// 4: organic GC happens (->COLLECTED, WeakRef is empty)
|
|
51
|
+
// 5: BOYD is called (finalizer has not run yet)
|
|
52
|
+
|
|
53
|
+
// The order of steps 3 and 4 is not important. What matters is that
|
|
54
|
+
// the WeakRef is empty, but the finalizer has not yet run, at the
|
|
55
|
+
// time that BOYD happens. And that the vref is in possiblyDeadSet
|
|
56
|
+
// (because of the vdata drop, not the finalizer), so this BOYD will
|
|
57
|
+
// examine the vref.
|
|
58
|
+
|
|
59
|
+
// This test simulates this case with our mockGC tools. It would
|
|
60
|
+
// fail if boyd-gc.js used getValForSlot instead of slotToVal.has.
|
|
61
|
+
|
|
62
|
+
// The GC code used to call getValForSlot() instead of
|
|
63
|
+
// slotToVal.has(), in the possiblyRetiredSet. The second test
|
|
64
|
+
// exercises this case, and would fail if it still used
|
|
65
|
+
// getValForSlot
|
|
66
|
+
|
|
67
|
+
let myPresence;
|
|
68
|
+
function buildRootObject(vatPowers) {
|
|
69
|
+
const { VatData } = vatPowers;
|
|
70
|
+
const { makeScalarBigMapStore } = VatData;
|
|
71
|
+
const store = makeScalarBigMapStore();
|
|
72
|
+
return Far('root', {
|
|
73
|
+
store: p => {
|
|
74
|
+
myPresence = p;
|
|
75
|
+
store.init('presence', p);
|
|
76
|
+
},
|
|
77
|
+
drop: () => store.delete('presence'),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const makeNS = () => ({ buildRootObject });
|
|
82
|
+
const ls = makeLiveSlots(syscall, 'vatA', {}, {}, gcTools, undefined, makeNS);
|
|
83
|
+
const { dispatch } = ls;
|
|
84
|
+
await dispatch(makeStartVat(kser()));
|
|
85
|
+
|
|
86
|
+
await dispatch(makeMessage('o+0', 'store', [kslot('o-1')]));
|
|
87
|
+
t.truthy(myPresence);
|
|
88
|
+
|
|
89
|
+
// clear out everything before our check
|
|
90
|
+
await dispatch(makeBringOutYourDead());
|
|
91
|
+
log.length = 0;
|
|
92
|
+
|
|
93
|
+
// drop the vdata reference
|
|
94
|
+
await dispatch(makeMessage('o+0', 'drop', []));
|
|
95
|
+
log.length = 0;
|
|
96
|
+
|
|
97
|
+
// and, before BOYD can happen, collect (but do not finalize) the Presence
|
|
98
|
+
gcTools.kill(myPresence);
|
|
99
|
+
|
|
100
|
+
// now BOYD
|
|
101
|
+
await dispatch(makeBringOutYourDead());
|
|
102
|
+
|
|
103
|
+
// the BOYD must not have done dropImports or retireImports
|
|
104
|
+
t.deepEqual(log, []);
|
|
105
|
+
|
|
106
|
+
// eventually, the finalizer runs
|
|
107
|
+
gcTools.flushAllFRs();
|
|
108
|
+
|
|
109
|
+
// *now* a BOYD will drop+retire
|
|
110
|
+
await dispatch(makeBringOutYourDead());
|
|
111
|
+
t.deepEqual(justGC(log), [
|
|
112
|
+
{ type: 'dropImports', slots: ['o-1'] },
|
|
113
|
+
{ type: 'retireImports', slots: ['o-1'] },
|
|
114
|
+
]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('presence in COLLECTED state is not retired early', async t => {
|
|
118
|
+
const { syscall, log } = buildSyscall();
|
|
119
|
+
const gcTools = makeMockGC();
|
|
120
|
+
|
|
121
|
+
// The GC code used to call getValForSlot() instead of
|
|
122
|
+
// slotToVal.has(), in the possiblyRetiredSet. This test would fail
|
|
123
|
+
// if it still used getValForSlot.
|
|
124
|
+
|
|
125
|
+
// The setup is a Presence in the COLLECTED state as the only
|
|
126
|
+
// pillar, but not in possiblyDeadSet, whose vref also appears in
|
|
127
|
+
// possiblyRetiredSet (because a recognizer was just deleted). On
|
|
128
|
+
// the BOYD that handles the possiblyRetiredSet, the "reachability
|
|
129
|
+
// inhibits retirement" check should treat the COLLECTED state as
|
|
130
|
+
// reachable, so the retirement is deferred for a later BOYD (which
|
|
131
|
+
// would drop the vref first)
|
|
132
|
+
//
|
|
133
|
+
// To build this, we have an anchored (virtual) MapStore msA holding
|
|
134
|
+
// the only reference (vdata) to a (virtual) WeakSetStore wssB. wssB
|
|
135
|
+
// has one (weak) key, o-1, for which there is a Presence P.
|
|
136
|
+
//
|
|
137
|
+
// 1: Construct everything, kill the wssB Representative, BOYD. That
|
|
138
|
+
// will process wssB, but leave it alive because of the vdata in
|
|
139
|
+
// msA.
|
|
140
|
+
|
|
141
|
+
// 2: Use msA.delete(key) to drop its vdata ref to wssB (adding wssB
|
|
142
|
+
// to possiblyDeadSet), and use gcTools.kill(P) to mock-mark it
|
|
143
|
+
// as COLLECTED (but not finalized)
|
|
144
|
+
|
|
145
|
+
// 3: Do a BOYD. The first pass will see wssB is unreachable, and
|
|
146
|
+
// delete it. The collection deleter will put o-1 in
|
|
147
|
+
// possiblyRetiredSet. There might be a second pass (doMore=1),
|
|
148
|
+
// but it won't have anything in possiblyDeadSet and will do
|
|
149
|
+
// nothing. Then the post-deadSet loop will process
|
|
150
|
+
// possiblyRetiredSet, which will contain our o-1. That
|
|
151
|
+
// processing step contains the valToSlot.has (or the
|
|
152
|
+
// would-be-incorrect getValForSlot) that we want to exercise.
|
|
153
|
+
|
|
154
|
+
let myPresence;
|
|
155
|
+
let myWeakStore;
|
|
156
|
+
|
|
157
|
+
function buildRootObject(vatPowers) {
|
|
158
|
+
const { VatData } = vatPowers;
|
|
159
|
+
const { makeScalarBigMapStore, makeScalarBigWeakSetStore } = VatData;
|
|
160
|
+
const store = makeScalarBigMapStore();
|
|
161
|
+
myWeakStore = makeScalarBigWeakSetStore();
|
|
162
|
+
return Far('root', {
|
|
163
|
+
store: p => {
|
|
164
|
+
myPresence = p;
|
|
165
|
+
myWeakStore.add(p);
|
|
166
|
+
t.truthy(myWeakStore.has(p));
|
|
167
|
+
store.init('weakstore', myWeakStore);
|
|
168
|
+
},
|
|
169
|
+
dropWeakStore: () => store.delete('weakstore'),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const makeNS = () => ({ buildRootObject });
|
|
174
|
+
const ls = makeLiveSlots(syscall, 'vatA', {}, {}, gcTools, undefined, makeNS);
|
|
175
|
+
const { dispatch, testHooks } = ls;
|
|
176
|
+
const { possiblyDeadSet, possiblyRetiredSet, slotToVal } = testHooks;
|
|
177
|
+
await dispatch(makeStartVat(kser()));
|
|
178
|
+
|
|
179
|
+
// step 1 (setup): store, kill WeakSetStore representative, BOYD
|
|
180
|
+
await dispatch(makeMessage('o+0', 'store', [kslot('o-1')]));
|
|
181
|
+
t.truthy(myPresence);
|
|
182
|
+
gcTools.kill(myWeakStore);
|
|
183
|
+
gcTools.flushAllFRs();
|
|
184
|
+
await dispatch(makeBringOutYourDead());
|
|
185
|
+
log.length = 0;
|
|
186
|
+
|
|
187
|
+
// myPresence vref is held by the Presence, and recognized by myWeakStore
|
|
188
|
+
t.is(possiblyDeadSet.size, 0);
|
|
189
|
+
t.is(possiblyRetiredSet.size, 0);
|
|
190
|
+
|
|
191
|
+
// step 2: delete vdata ref to weakstore, make myPresence COLLECTED
|
|
192
|
+
await dispatch(makeMessage('o+0', 'dropWeakStore', []));
|
|
193
|
+
gcTools.kill(myPresence);
|
|
194
|
+
log.length = 0;
|
|
195
|
+
// weakstore is possiblyDead (NARRATORS VOICE: it's dead). Presence
|
|
196
|
+
// is not, because the finalizer hasn't run.
|
|
197
|
+
t.is(possiblyDeadSet.size, 1);
|
|
198
|
+
t.is(possiblyRetiredSet.size, 0);
|
|
199
|
+
t.not([...possiblyDeadSet][0], 'o-1');
|
|
200
|
+
// the empty weakref is still there
|
|
201
|
+
t.true(slotToVal.has('o-1'));
|
|
202
|
+
|
|
203
|
+
// step 3: BOYD. It will collect myWeakStore on the first pass,
|
|
204
|
+
// whose deleter should clear all entries, which will add its
|
|
205
|
+
// recognized vrefs to possiblyRetiredSet. The post-pass will check
|
|
206
|
+
// o-1 for reachability with slotToVal.has, and because that says it
|
|
207
|
+
// is reachable, it will not be retired (even though it has no
|
|
208
|
+
// recognizer by now).
|
|
209
|
+
//
|
|
210
|
+
// *If* scanForDeadObjects() were mistakenly using getValForSlot()
|
|
211
|
+
// *instead of slotToVal.has(), we would see a retireImports here,
|
|
212
|
+
// *which would be a vat-fatal error, because we haven't seen a
|
|
213
|
+
// *dropImports yet.
|
|
214
|
+
await dispatch(makeBringOutYourDead());
|
|
215
|
+
// I tested this manually, by modifying boyd-gc.js *to use
|
|
216
|
+
// getValForSlot, and observed that this deepEqual(justGC(log), [])
|
|
217
|
+
// failed: it had an unexpected retireImports
|
|
218
|
+
t.deepEqual(justGC(log), []);
|
|
219
|
+
log.length = 0;
|
|
220
|
+
|
|
221
|
+
// eventually, the finalizer runs
|
|
222
|
+
gcTools.flushAllFRs();
|
|
223
|
+
|
|
224
|
+
// *now* a BOYD will drop+retire
|
|
225
|
+
await dispatch(makeBringOutYourDead());
|
|
226
|
+
t.deepEqual(justGC(log), [
|
|
227
|
+
{ type: 'dropImports', slots: ['o-1'] },
|
|
228
|
+
{ type: 'retireImports', slots: ['o-1'] },
|
|
229
|
+
]);
|
|
230
|
+
});
|
package/test/gc-helpers.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
// eslint-disable-next-line import/order
|
|
2
|
-
|
|
3
1
|
import { Far } from '@endo/marshal';
|
|
4
2
|
import { M } from '@agoric/store';
|
|
5
|
-
import { kslot, kser } from '
|
|
3
|
+
import { kslot, kser } from '@agoric/kmarshal';
|
|
6
4
|
import { parseVatSlot } from '../src/parseVatSlots.js';
|
|
7
5
|
|
|
8
6
|
// These tests follow the model described in
|
|
@@ -168,6 +166,7 @@ export function* enumerateKeysWithPrefix(fakestore, prefix) {
|
|
|
168
166
|
}
|
|
169
167
|
}
|
|
170
168
|
}
|
|
169
|
+
harden(enumerateKeysWithPrefix);
|
|
171
170
|
|
|
172
171
|
export function recognizersOf(v, baseref) {
|
|
173
172
|
// the | is followed by the collectionID that can recognize baseref
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import test from 'ava';
|
|
2
|
-
import '@endo/init/debug.js';
|
|
3
3
|
import { Far } from '@endo/marshal';
|
|
4
|
+
import { kser } from '@agoric/kmarshal';
|
|
4
5
|
import { buildSyscall } from './liveslots-helpers.js';
|
|
5
6
|
import { makeLiveSlots } from '../src/liveslots.js';
|
|
6
|
-
import { kser } from './kmarshal.js';
|
|
7
7
|
import { makeMockGC } from './mock-gc.js';
|
|
8
8
|
import { makeMessage, makeStartVat } from './util.js';
|
|
9
9
|
|