@agoric/swingset-vat 0.33.0-upgrade-17-dev-ec448b0.0 → 0.33.0-upgrade-18-dev-b9b8db4.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/package.json +31 -31
- package/src/controller/controller.js +6 -0
- package/src/controller/initializeSwingset.js +1 -1
- package/src/controller/upgradeSwingset.js +132 -2
- package/src/devices/mailbox/mailbox.js +76 -43
- package/src/index.js +2 -0
- package/src/kernel/deviceTranslator.js +1 -1
- package/src/kernel/gc-actions.js +2 -3
- package/src/kernel/kernel.js +59 -20
- package/src/kernel/state/kernelKeeper.js +177 -103
- package/src/kernel/state/vatKeeper.js +32 -0
- package/src/kernel/vatTranslator.js +7 -3
- package/src/types-external.js +9 -1
- package/src/types-internal.js +11 -0
- package/tools/prepare-strict-test-env-ava.js +19 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/swingset-vat",
|
|
3
|
-
"version": "0.33.0-upgrade-
|
|
3
|
+
"version": "0.33.0-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
4
4
|
"description": "Vat/Container Launcher",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -27,34 +27,34 @@
|
|
|
27
27
|
"@types/yargs-parser": "^21.0.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@agoric/internal": "0.4.0-upgrade-
|
|
31
|
-
"@agoric/kmarshal": "0.1.1-upgrade-
|
|
32
|
-
"@agoric/store": "0.9.3-upgrade-
|
|
33
|
-
"@agoric/swing-store": "0.
|
|
34
|
-
"@agoric/swingset-liveslots": "0.10.3-upgrade-
|
|
35
|
-
"@agoric/swingset-xsnap-supervisor": "0.10.3-upgrade-
|
|
36
|
-
"@agoric/time": "0.3.3-upgrade-
|
|
37
|
-
"@agoric/vat-data": "0.5.3-upgrade-
|
|
38
|
-
"@agoric/xsnap": "0.14.3-upgrade-
|
|
39
|
-
"@agoric/xsnap-lockdown": "0.14.1-upgrade-
|
|
40
|
-
"@endo/base64": "^1.0.
|
|
41
|
-
"@endo/bundle-source": "^3.4.
|
|
42
|
-
"@endo/captp": "^4.
|
|
43
|
-
"@endo/check-bundle": "^1.0.
|
|
44
|
-
"@endo/compartment-mapper": "^1.
|
|
45
|
-
"@endo/errors": "^1.2.
|
|
46
|
-
"@endo/eventual-send": "^1.2.
|
|
47
|
-
"@endo/far": "^1.1.
|
|
48
|
-
"@endo/import-bundle": "^1.
|
|
49
|
-
"@endo/init": "^1.1.
|
|
50
|
-
"@endo/marshal": "^1.
|
|
51
|
-
"@endo/nat": "^5.0.
|
|
52
|
-
"@endo/pass-style": "^1.4.
|
|
53
|
-
"@endo/patterns": "^1.4.
|
|
54
|
-
"@endo/promise-kit": "^1.1.
|
|
55
|
-
"@endo/ses-ava": "^1.2.
|
|
56
|
-
"@endo/stream": "^1.2.
|
|
57
|
-
"@endo/zip": "^1.0.
|
|
30
|
+
"@agoric/internal": "0.4.0-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
31
|
+
"@agoric/kmarshal": "0.1.1-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
32
|
+
"@agoric/store": "0.9.3-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
33
|
+
"@agoric/swing-store": "0.10.0-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
34
|
+
"@agoric/swingset-liveslots": "0.10.3-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
35
|
+
"@agoric/swingset-xsnap-supervisor": "0.10.3-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
36
|
+
"@agoric/time": "0.3.3-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
37
|
+
"@agoric/vat-data": "0.5.3-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
38
|
+
"@agoric/xsnap": "0.14.3-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
39
|
+
"@agoric/xsnap-lockdown": "0.14.1-upgrade-18-dev-b9b8db4.0+b9b8db4",
|
|
40
|
+
"@endo/base64": "^1.0.8",
|
|
41
|
+
"@endo/bundle-source": "^3.4.2",
|
|
42
|
+
"@endo/captp": "^4.4.2",
|
|
43
|
+
"@endo/check-bundle": "^1.0.11",
|
|
44
|
+
"@endo/compartment-mapper": "^1.3.1",
|
|
45
|
+
"@endo/errors": "^1.2.7",
|
|
46
|
+
"@endo/eventual-send": "^1.2.7",
|
|
47
|
+
"@endo/far": "^1.1.8",
|
|
48
|
+
"@endo/import-bundle": "^1.3.1",
|
|
49
|
+
"@endo/init": "^1.1.6",
|
|
50
|
+
"@endo/marshal": "^1.6.1",
|
|
51
|
+
"@endo/nat": "^5.0.12",
|
|
52
|
+
"@endo/pass-style": "^1.4.6",
|
|
53
|
+
"@endo/patterns": "^1.4.6",
|
|
54
|
+
"@endo/promise-kit": "^1.1.7",
|
|
55
|
+
"@endo/ses-ava": "^1.2.7",
|
|
56
|
+
"@endo/stream": "^1.2.7",
|
|
57
|
+
"@endo/zip": "^1.0.8",
|
|
58
58
|
"ansi-styles": "^6.2.1",
|
|
59
59
|
"anylogger": "^0.21.0",
|
|
60
60
|
"better-sqlite3": "^9.1.1",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"access": "public"
|
|
102
102
|
},
|
|
103
103
|
"typeCoverage": {
|
|
104
|
-
"atLeast": 75.
|
|
104
|
+
"atLeast": 75.82
|
|
105
105
|
},
|
|
106
|
-
"gitHead": "
|
|
106
|
+
"gitHead": "b9b8db4f2cc3926bb71fb9ca516328c30863f246"
|
|
107
107
|
}
|
|
@@ -366,6 +366,10 @@ export async function makeSwingsetController(
|
|
|
366
366
|
return kref;
|
|
367
367
|
},
|
|
368
368
|
|
|
369
|
+
kpRegisterInterest(kpid) {
|
|
370
|
+
return kernel.kpRegisterInterest(kpid);
|
|
371
|
+
},
|
|
372
|
+
|
|
369
373
|
kpStatus(kpid) {
|
|
370
374
|
return kernel.kpStatus(kpid);
|
|
371
375
|
},
|
|
@@ -384,6 +388,8 @@ export async function makeSwingsetController(
|
|
|
384
388
|
return kernel.deviceNameToID(deviceName);
|
|
385
389
|
},
|
|
386
390
|
|
|
391
|
+
injectQueuedUpgradeEvents: () => kernel.injectQueuedUpgradeEvents(),
|
|
392
|
+
|
|
387
393
|
/**
|
|
388
394
|
* Queue a method call into the named vat
|
|
389
395
|
*
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
import { Fail } from '@endo/errors';
|
|
1
2
|
import {
|
|
2
3
|
DEFAULT_REAP_DIRT_THRESHOLD_KEY,
|
|
3
4
|
DEFAULT_GC_KREFS_PER_BOYD,
|
|
4
5
|
getAllDynamicVats,
|
|
5
6
|
getAllStaticVats,
|
|
7
|
+
incrementReferenceCount,
|
|
8
|
+
readQueue,
|
|
6
9
|
} from '../kernel/state/kernelKeeper.js';
|
|
10
|
+
import { enumeratePrefixedKeys } from '../kernel/state/storageHelper.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @import {RunQueueEvent} from '../types-internal.js';
|
|
14
|
+
*/
|
|
7
15
|
|
|
8
16
|
const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => {
|
|
9
17
|
// This is called, once per vat, when upgradeSwingset migrates from
|
|
@@ -92,11 +100,13 @@ const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => {
|
|
|
92
100
|
* `hostStorage.commit()` afterwards.
|
|
93
101
|
*
|
|
94
102
|
* @param {SwingStoreKernelStorage} kernelStorage
|
|
95
|
-
* @returns {
|
|
103
|
+
* @returns {{ modified: boolean }}
|
|
96
104
|
*/
|
|
97
105
|
export const upgradeSwingset = kernelStorage => {
|
|
98
106
|
const { kvStore } = kernelStorage;
|
|
99
107
|
let modified = false;
|
|
108
|
+
/** @type {RunQueueEvent[]} */
|
|
109
|
+
const upgradeEvents = [];
|
|
100
110
|
let vstring = kvStore.get('version');
|
|
101
111
|
if (vstring === undefined) {
|
|
102
112
|
vstring = '0';
|
|
@@ -204,9 +214,129 @@ export const upgradeSwingset = kernelStorage => {
|
|
|
204
214
|
version = 2;
|
|
205
215
|
}
|
|
206
216
|
|
|
217
|
+
if (version < 3) {
|
|
218
|
+
// v3 means that we've completed remediation for bug #9039
|
|
219
|
+
console.log(`Starting remediation of bug #9039`);
|
|
220
|
+
|
|
221
|
+
// find all terminated vats
|
|
222
|
+
const terminated = new Set(JSON.parse(getRequired('vats.terminated')));
|
|
223
|
+
|
|
224
|
+
// find all live vats
|
|
225
|
+
const allVatIDs = [];
|
|
226
|
+
for (const [_name, vatID] of getAllStaticVats(kvStore)) {
|
|
227
|
+
if (!terminated.has(vatID)) {
|
|
228
|
+
allVatIDs.push(vatID);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for (const vatID of getAllDynamicVats(getRequired)) {
|
|
232
|
+
if (!terminated.has(vatID)) {
|
|
233
|
+
allVatIDs.push(vatID);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// find all pending notifies
|
|
238
|
+
const notifies = new Map(); // .get(kpid) = [vatIDs..];
|
|
239
|
+
for (const name of ['runQueue', 'acceptanceQueue']) {
|
|
240
|
+
for (const rq of readQueue(name, getRequired)) {
|
|
241
|
+
if (rq.type === 'notify') {
|
|
242
|
+
const { vatID, kpid } = rq;
|
|
243
|
+
assert(vatID);
|
|
244
|
+
assert(kpid);
|
|
245
|
+
let vats = notifies.get(kpid);
|
|
246
|
+
if (!vats) {
|
|
247
|
+
vats = [];
|
|
248
|
+
notifies.set(kpid, vats);
|
|
249
|
+
}
|
|
250
|
+
vats.push(vatID);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
console.log(` - pending notifies:`, notifies);
|
|
255
|
+
|
|
256
|
+
// cache of known-settled kpids: will grow to num(kpids)
|
|
257
|
+
const KPIDStatus = new Map();
|
|
258
|
+
const isSettled = kpid => {
|
|
259
|
+
if (KPIDStatus.has(kpid)) {
|
|
260
|
+
return KPIDStatus.get(kpid);
|
|
261
|
+
}
|
|
262
|
+
const state = kvStore.get(`${kpid}.state`);
|
|
263
|
+
// missing state means the kpid is deleted somehow, shouldn't happen
|
|
264
|
+
state || Fail`${kpid}.state is missing`;
|
|
265
|
+
const settled = state !== 'unresolved';
|
|
266
|
+
KPIDStatus.set(kpid, settled);
|
|
267
|
+
return settled;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// walk vNN.c.kpNN for all vats, for each one check the
|
|
271
|
+
// kpNN.state, for the settled ones check for a pending notify,
|
|
272
|
+
// record the ones without a pending notify
|
|
273
|
+
|
|
274
|
+
const buggyKPIDs = []; // [kpid, vatID]
|
|
275
|
+
for (const vatID of allVatIDs) {
|
|
276
|
+
const prefix = `${vatID}.c.`;
|
|
277
|
+
const len = prefix.length;
|
|
278
|
+
const ckpPrefix = `${vatID}.c.kp`;
|
|
279
|
+
for (const key of enumeratePrefixedKeys(kvStore, ckpPrefix)) {
|
|
280
|
+
const kpid = key.slice(len);
|
|
281
|
+
if (isSettled(kpid)) {
|
|
282
|
+
const n = notifies.get(kpid);
|
|
283
|
+
if (!n || !n.includes(vatID)) {
|
|
284
|
+
// there is no pending notify
|
|
285
|
+
buggyKPIDs.push([kpid, vatID]);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
console.log(` - found ${buggyKPIDs.length} buggy kpids, enqueueing fixes`);
|
|
291
|
+
|
|
292
|
+
// now fix it. The bug means we failed to delete the c-list entry
|
|
293
|
+
// and decref it back when the promise was rejected. That decref
|
|
294
|
+
// would have pushed the kpid onto maybeFreeKrefs, which would
|
|
295
|
+
// have triggered a refcount check at end-of-crank, which might
|
|
296
|
+
// have deleted the promise records (if nothing else was
|
|
297
|
+
// referencing the promise, like arguments in messages enqueued to
|
|
298
|
+
// unresolved promises, or something transient on the
|
|
299
|
+
// run-queue). Deleting those promise records might have decreffed
|
|
300
|
+
// krefs in the rejection data (although in general 9039 rejects
|
|
301
|
+
// those promises with non-slot-bearing DisconnectionObjects).
|
|
302
|
+
//
|
|
303
|
+
// To avoid duplicating a lot of kernel code inside this upgrade
|
|
304
|
+
// handler, we do the simplest possible thing: enqueue a notify to
|
|
305
|
+
// the upgraded vat for all these leftover promises. The new vat
|
|
306
|
+
// incarnation will ignore it (they don't recognize the vpid), but
|
|
307
|
+
// the dispatch.notify() delivery will clear the c-list and decref
|
|
308
|
+
// the kpid, and will trigger all the usual GC work. Note that
|
|
309
|
+
// these notifies will be delivered before any activity the host
|
|
310
|
+
// app might trigger for e.g. a chain upgrade, but they should not
|
|
311
|
+
// cause userspace-visible behavior (non-slot-bearing rejection
|
|
312
|
+
// data means no other vat will even get a gc-action delivery:
|
|
313
|
+
// only the upgraded vat will see anything, and those deliveries
|
|
314
|
+
// won't make it past liveslots).
|
|
315
|
+
|
|
316
|
+
let count = 0;
|
|
317
|
+
for (const [kpid, vatID] of buggyKPIDs) {
|
|
318
|
+
// account for the reference to this kpid in upgradeEvents
|
|
319
|
+
incrementReferenceCount(getRequired, kvStore, kpid, `enq|notify`);
|
|
320
|
+
upgradeEvents.push({ type: 'notify', vatID, kpid });
|
|
321
|
+
count += 1;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log(` - #9039 remediation complete, ${count} notifies to inject`);
|
|
325
|
+
modified = true;
|
|
326
|
+
version = 3;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (upgradeEvents.length) {
|
|
330
|
+
assert(modified);
|
|
331
|
+
// stash until host calls controller.injectQueuedUpgradeEvents()
|
|
332
|
+
const oldEvents = JSON.parse(kvStore.get('upgradeEvents') || '[]');
|
|
333
|
+
const events = [...oldEvents, ...upgradeEvents];
|
|
334
|
+
kvStore.set('upgradeEvents', JSON.stringify(events));
|
|
335
|
+
}
|
|
336
|
+
|
|
207
337
|
if (modified) {
|
|
208
338
|
kvStore.set('version', `${version}`);
|
|
209
339
|
}
|
|
210
|
-
return modified;
|
|
340
|
+
return harden({ modified });
|
|
211
341
|
};
|
|
212
342
|
harden(upgradeSwingset);
|
|
@@ -71,6 +71,23 @@ import { Nat } from '@endo/nat';
|
|
|
71
71
|
// replace it with one that tracks which parts of the state have been
|
|
72
72
|
// modified, to build more efficient Merkle proofs.
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {object} Mailbox
|
|
76
|
+
* @property {bigint} ack
|
|
77
|
+
* @property {Map<bigint, unknown>} outbox
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @typedef {object} MailboxExport
|
|
82
|
+
* @property {number} ack
|
|
83
|
+
* @property {Array<[number, unknown]>} outbox
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {MailboxExport} data
|
|
88
|
+
* @param {Partial<Mailbox>} [inout]
|
|
89
|
+
* @returns {Mailbox}
|
|
90
|
+
*/
|
|
74
91
|
export function importMailbox(data, inout = {}) {
|
|
75
92
|
const outbox = new Map();
|
|
76
93
|
for (const m of data.outbox) {
|
|
@@ -78,10 +95,15 @@ export function importMailbox(data, inout = {}) {
|
|
|
78
95
|
}
|
|
79
96
|
inout.ack = Nat(data.ack);
|
|
80
97
|
inout.outbox = outbox;
|
|
81
|
-
return inout;
|
|
98
|
+
return /** @type {Mailbox} */ (inout);
|
|
82
99
|
}
|
|
83
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @param {Mailbox} inout
|
|
103
|
+
* @returns {MailboxExport}
|
|
104
|
+
*/
|
|
84
105
|
export function exportMailbox(inout) {
|
|
106
|
+
/** @type {MailboxExport['outbox']} */
|
|
85
107
|
const messages = [];
|
|
86
108
|
for (const [msgnum, body] of inout.outbox) {
|
|
87
109
|
messages.push([Number(msgnum), body]);
|
|
@@ -93,64 +115,75 @@ export function exportMailbox(inout) {
|
|
|
93
115
|
};
|
|
94
116
|
}
|
|
95
117
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
/**
|
|
119
|
+
* @param {Map<string, Mailbox>} state
|
|
120
|
+
*/
|
|
121
|
+
export function exportMailboxData(state) {
|
|
122
|
+
/** @type {Record<string, {inboundAck: MailboxExport['ack'], outbox: MailboxExport['outbox']}>} */
|
|
123
|
+
const data = {};
|
|
124
|
+
for (const [peer, inout] of state.entries()) {
|
|
125
|
+
const exported = exportMailbox(inout);
|
|
126
|
+
data[peer] = {
|
|
127
|
+
inboundAck: exported.ack,
|
|
128
|
+
outbox: exported.outbox,
|
|
129
|
+
};
|
|
106
130
|
}
|
|
131
|
+
return harden(data);
|
|
132
|
+
}
|
|
107
133
|
|
|
108
|
-
|
|
109
|
-
|
|
134
|
+
function getOrCreatePeer(state, peer) {
|
|
135
|
+
if (!state.has(peer)) {
|
|
136
|
+
const inout = {
|
|
137
|
+
outbox: harden(new Map()),
|
|
138
|
+
ack: 0n,
|
|
139
|
+
};
|
|
140
|
+
state.set(peer, inout);
|
|
110
141
|
}
|
|
142
|
+
return state.get(peer);
|
|
143
|
+
}
|
|
111
144
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
/**
|
|
146
|
+
* @param {ReturnType<exportMailboxData>} data
|
|
147
|
+
* @returns {Map<string, Mailbox>}
|
|
148
|
+
*/
|
|
149
|
+
export function makeEphemeralMailboxStorage(data) {
|
|
150
|
+
const state = harden(new Map());
|
|
151
|
+
for (const peer of Object.getOwnPropertyNames(data)) {
|
|
152
|
+
const inout = getOrCreatePeer(state, peer);
|
|
153
|
+
const d = data[peer];
|
|
154
|
+
importMailbox(
|
|
155
|
+
{
|
|
156
|
+
ack: d.inboundAck,
|
|
157
|
+
outbox: d.outbox,
|
|
158
|
+
},
|
|
159
|
+
inout,
|
|
160
|
+
);
|
|
115
161
|
}
|
|
162
|
+
return state;
|
|
163
|
+
}
|
|
116
164
|
|
|
117
|
-
|
|
118
|
-
|
|
165
|
+
/**
|
|
166
|
+
* @template [T=unknown]
|
|
167
|
+
* @param {Pick<Map<string, T>, 'has' | 'get'> & {set: (key: string, value: T) => void}} [state]
|
|
168
|
+
*/
|
|
169
|
+
export function buildMailboxStateMap(state = harden(new Map())) {
|
|
170
|
+
function add(peer, msgnum, body) {
|
|
171
|
+
getOrCreatePeer(state, `${peer}`).outbox.set(Nat(msgnum), `${body}`);
|
|
119
172
|
}
|
|
120
173
|
|
|
121
|
-
function
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
const exported = exportMailbox(inout);
|
|
125
|
-
data[peer] = {
|
|
126
|
-
inboundAck: exported.ack,
|
|
127
|
-
outbox: exported.outbox,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
return harden(data);
|
|
174
|
+
function remove(peer, msgnum) {
|
|
175
|
+
const messages = getOrCreatePeer(state, `${peer}`).outbox;
|
|
176
|
+
messages.delete(Nat(msgnum));
|
|
131
177
|
}
|
|
132
178
|
|
|
133
|
-
function
|
|
134
|
-
|
|
135
|
-
for (const peer of Object.getOwnPropertyNames(data)) {
|
|
136
|
-
const inout = getOrCreatePeer(peer);
|
|
137
|
-
const d = data[peer];
|
|
138
|
-
importMailbox(
|
|
139
|
-
{
|
|
140
|
-
ack: d.inboundAck,
|
|
141
|
-
outbox: d.outbox,
|
|
142
|
-
},
|
|
143
|
-
inout,
|
|
144
|
-
);
|
|
145
|
-
}
|
|
179
|
+
function setAcknum(peer, msgnum) {
|
|
180
|
+
getOrCreatePeer(state, `${peer}`).ack = Nat(msgnum);
|
|
146
181
|
}
|
|
147
182
|
|
|
148
183
|
return harden({
|
|
149
184
|
add,
|
|
150
185
|
remove,
|
|
151
186
|
setAcknum,
|
|
152
|
-
exportToData,
|
|
153
|
-
populateFromData,
|
|
154
187
|
});
|
|
155
188
|
}
|
|
156
189
|
|
package/src/index.js
CHANGED
|
@@ -13,6 +13,8 @@ export { upgradeSwingset } from './controller/upgradeSwingset.js';
|
|
|
13
13
|
export {
|
|
14
14
|
buildMailboxStateMap,
|
|
15
15
|
buildMailbox,
|
|
16
|
+
exportMailboxData,
|
|
17
|
+
makeEphemeralMailboxStorage,
|
|
16
18
|
} from './devices/mailbox/mailbox.js';
|
|
17
19
|
export { buildTimer } from './devices/timer/timer.js';
|
|
18
20
|
export { buildBridge } from './devices/bridge/bridge.js';
|
|
@@ -69,7 +69,7 @@ function makeDRTranslator(deviceID, kernelKeeper) {
|
|
|
69
69
|
*
|
|
70
70
|
* @param {string} deviceID
|
|
71
71
|
* @param {string} deviceName
|
|
72
|
-
* @param {
|
|
72
|
+
* @param {KernelKeeper} kernelKeeper
|
|
73
73
|
* @returns {(dsc: DeviceSyscallObject) => KernelSyscallObject}
|
|
74
74
|
*/
|
|
75
75
|
export function makeDSTranslator(deviceID, deviceName, kernelKeeper) {
|
package/src/kernel/gc-actions.js
CHANGED
|
@@ -45,7 +45,7 @@ function parseAction(s) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* @param {
|
|
48
|
+
* @param {KernelKeeper} kernelKeeper
|
|
49
49
|
* @returns {import('../types-internal.js').RunQueueEvent | undefined}
|
|
50
50
|
*/
|
|
51
51
|
export function processGCActionSet(kernelKeeper) {
|
|
@@ -86,10 +86,9 @@ export function processGCActionSet(kernelKeeper) {
|
|
|
86
86
|
const hasCList = vatKeeper.hasCListEntry(kref);
|
|
87
87
|
const isReachable = hasCList ? vatKeeper.getReachableFlag(kref) : undefined;
|
|
88
88
|
const exists = kernelKeeper.kernelObjectExists(kref);
|
|
89
|
-
// @ts-expect-error xxx
|
|
90
89
|
const { reachable, recognizable } = exists
|
|
91
90
|
? kernelKeeper.getObjectRefCount(kref)
|
|
92
|
-
: {};
|
|
91
|
+
: { reachable: 0, recognizable: 0 };
|
|
93
92
|
|
|
94
93
|
if (type === 'dropExport') {
|
|
95
94
|
if (!exists) return false; // already, shouldn't happen
|
package/src/kernel/kernel.js
CHANGED
|
@@ -39,8 +39,10 @@ import { makeDeviceTranslators } from './deviceTranslator.js';
|
|
|
39
39
|
import { notifyTermination } from './notifyTermination.js';
|
|
40
40
|
import { makeVatAdminHooks } from './vat-admin-hooks.js';
|
|
41
41
|
|
|
42
|
-
/**
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* @import {MeterConsumption, VatDeliveryObject, VatDeliveryResult, VatSyscallObject, VatSyscallResult} from '@agoric/swingset-liveslots';
|
|
44
|
+
* @import {PolicyInputCleanupCounts} from '../types-external.js';
|
|
45
|
+
*/
|
|
44
46
|
|
|
45
47
|
function abbreviateReplacer(_, arg) {
|
|
46
48
|
if (typeof arg === 'bigint') {
|
|
@@ -56,7 +58,7 @@ function abbreviateReplacer(_, arg) {
|
|
|
56
58
|
/**
|
|
57
59
|
* Provide the kref of a vat's root object, as if it had been exported.
|
|
58
60
|
*
|
|
59
|
-
* @param {
|
|
61
|
+
* @param {KernelKeeper} kernelKeeper Kernel keeper managing persistent kernel state.
|
|
60
62
|
* @param {string} vatID Vat ID of the vat whose root kref is sought.
|
|
61
63
|
*
|
|
62
64
|
* @returns {string} the kref of the root object of the given vat.
|
|
@@ -213,6 +215,10 @@ export default function buildKernel(
|
|
|
213
215
|
return deviceID;
|
|
214
216
|
}
|
|
215
217
|
|
|
218
|
+
function injectQueuedUpgradeEvents() {
|
|
219
|
+
kernelKeeper.injectQueuedUpgradeEvents();
|
|
220
|
+
}
|
|
221
|
+
|
|
216
222
|
function addImport(forVatID, what) {
|
|
217
223
|
if (!started) {
|
|
218
224
|
throw Error('must do kernel.start() before addImport()');
|
|
@@ -285,15 +291,16 @@ export default function buildKernel(
|
|
|
285
291
|
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
|
|
286
292
|
critical = vatKeeper.getOptions().critical;
|
|
287
293
|
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
const deadPromises = [...kernelKeeper.enumeratePromisesByDecider(vatID)];
|
|
291
|
-
// remove vatID from the list of live vats, and mark for deletion
|
|
294
|
+
// remove vatID from the list of live vats, and mark for
|
|
295
|
+
// deletion (which will happen later, in vat-cleanup events)
|
|
292
296
|
kernelKeeper.deleteVatID(vatID);
|
|
293
297
|
kernelKeeper.markVatAsTerminated(vatID);
|
|
294
298
|
deferred.push(kernelKeeper.removeVatFromSwingStoreExports(vatID));
|
|
295
|
-
|
|
296
|
-
|
|
299
|
+
|
|
300
|
+
// Reject all promises decided by the vat
|
|
301
|
+
const errdata = makeError('vat terminated');
|
|
302
|
+
for (const [kpid, _p] of kernelKeeper.enumeratePromisesByDecider(vatID)) {
|
|
303
|
+
resolveToError(kpid, errdata, vatID);
|
|
297
304
|
}
|
|
298
305
|
}
|
|
299
306
|
if (critical) {
|
|
@@ -380,7 +387,6 @@ export default function buildKernel(
|
|
|
380
387
|
|
|
381
388
|
/**
|
|
382
389
|
*
|
|
383
|
-
* @typedef { import('@agoric/swingset-liveslots').MeterConsumption } MeterConsumption
|
|
384
390
|
* @typedef { import('../types-internal.js').MeterID } MeterID
|
|
385
391
|
* @typedef { import('../types-internal.js').Dirt } Dirt
|
|
386
392
|
*
|
|
@@ -416,7 +422,7 @@ export default function buildKernel(
|
|
|
416
422
|
*
|
|
417
423
|
* @param {VatID} vatID
|
|
418
424
|
* @param {KernelDeliveryObject} kd
|
|
419
|
-
* @param {
|
|
425
|
+
* @param {VatDeliveryObject} vd
|
|
420
426
|
*/
|
|
421
427
|
async function deliverAndLogToVat(vatID, kd, vd) {
|
|
422
428
|
vatRequestedTermination = undefined;
|
|
@@ -426,7 +432,7 @@ export default function buildKernel(
|
|
|
426
432
|
const vs = kernelSlog.provideVatSlogger(vatID).vatSlog;
|
|
427
433
|
await null;
|
|
428
434
|
try {
|
|
429
|
-
/** @type {
|
|
435
|
+
/** @type { VatDeliveryResult } */
|
|
430
436
|
const deliveryResult = await vatWarehouse.deliverToVat(vatID, kd, vd, vs);
|
|
431
437
|
insistVatDeliveryResult(deliveryResult);
|
|
432
438
|
// const [ ok, problem, usage ] = deliveryResult;
|
|
@@ -596,7 +602,9 @@ export default function buildKernel(
|
|
|
596
602
|
const p = kernelKeeper.getKernelPromise(kpid);
|
|
597
603
|
kernelKeeper.incStat('dispatchNotify');
|
|
598
604
|
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
|
|
599
|
-
p.state
|
|
605
|
+
if (p.state === 'unresolved') {
|
|
606
|
+
throw Fail`spurious notification ${kpid}`;
|
|
607
|
+
}
|
|
600
608
|
/** @type { KernelDeliveryOneNotify[] } */
|
|
601
609
|
const resolutions = [];
|
|
602
610
|
if (!vatKeeper.hasCListEntry(kpid)) {
|
|
@@ -611,7 +619,9 @@ export default function buildKernel(
|
|
|
611
619
|
return NO_DELIVERY_CRANK_RESULTS;
|
|
612
620
|
}
|
|
613
621
|
for (const toResolve of targets) {
|
|
614
|
-
const
|
|
622
|
+
const tp = kernelKeeper.getKernelPromise(toResolve);
|
|
623
|
+
assert(tp.state !== 'unresolved');
|
|
624
|
+
const { state, data } = tp;
|
|
615
625
|
resolutions.push([toResolve, { state, data }]);
|
|
616
626
|
}
|
|
617
627
|
/** @type { KernelDeliveryNotify } */
|
|
@@ -700,6 +710,7 @@ export default function buildKernel(
|
|
|
700
710
|
total:
|
|
701
711
|
work.exports +
|
|
702
712
|
work.imports +
|
|
713
|
+
work.promises +
|
|
703
714
|
work.kv +
|
|
704
715
|
work.snapshots +
|
|
705
716
|
work.transcripts,
|
|
@@ -741,6 +752,8 @@ export default function buildKernel(
|
|
|
741
752
|
kdebug(`vat ${vatID} terminated before startVat delivered`);
|
|
742
753
|
return NO_DELIVERY_CRANK_RESULTS;
|
|
743
754
|
}
|
|
755
|
+
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
|
|
756
|
+
vatKeeper.setVatParameters(vatParameters);
|
|
744
757
|
const { meterID } = vatInfo;
|
|
745
758
|
/** @type { KernelDeliveryStartVat } */
|
|
746
759
|
const kd = harden(['startVat', vatParameters]);
|
|
@@ -994,9 +1007,30 @@ export default function buildKernel(
|
|
|
994
1007
|
return results;
|
|
995
1008
|
}
|
|
996
1009
|
|
|
997
|
-
//
|
|
998
|
-
|
|
999
|
-
|
|
1010
|
+
// We are homesick for a future in which most promises are
|
|
1011
|
+
// durable, and vats do not need to subscribe to their own
|
|
1012
|
+
// promises to make promise-watchers work. In that world, vats
|
|
1013
|
+
// somehow mark the non-durable promises, which we must
|
|
1014
|
+
// reject/disconnect on their behalf during upgrade.
|
|
1015
|
+
//
|
|
1016
|
+
// To handle the present reality, without durable promises, we
|
|
1017
|
+
// pretend that all promises are so marked.
|
|
1018
|
+
|
|
1019
|
+
// reject all ephemeral promises for which the vat was decider
|
|
1020
|
+
for (const [kpid, p] of kernelKeeper.enumeratePromisesByDecider(vatID)) {
|
|
1021
|
+
const isEphemeral = true; // future vats will mark these explicitly
|
|
1022
|
+
const selfSubscribed =
|
|
1023
|
+
p.state === 'unresolved' && p.subscribers.includes(vatID);
|
|
1024
|
+
if (isEphemeral) {
|
|
1025
|
+
resolveToError(kpid, disconnectionCapData, vatID);
|
|
1026
|
+
if (!selfSubscribed) {
|
|
1027
|
+
// If the vat was subscribed to its own promise, the
|
|
1028
|
+
// resolveToError will enqueue a dispatch.notify, whose delivery
|
|
1029
|
+
// will delete the c-list entry. If it is *not* subscribed, we
|
|
1030
|
+
// should delete the c-list entry now, because nobody else will.
|
|
1031
|
+
vatKeeper.deleteCListEntriesForKernelSlots([kpid]);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1000
1034
|
}
|
|
1001
1035
|
|
|
1002
1036
|
// simulate an abandonExports syscall from the vat,
|
|
@@ -1020,6 +1054,7 @@ export default function buildKernel(
|
|
|
1020
1054
|
});
|
|
1021
1055
|
const vatOptions = harden({ ...origOptions, workerOptions });
|
|
1022
1056
|
vatKeeper.setSourceAndOptions(source, vatOptions);
|
|
1057
|
+
vatKeeper.setVatParameters(vatParameters);
|
|
1023
1058
|
// TODO: decref the bundleID once setSourceAndOptions increfs it
|
|
1024
1059
|
|
|
1025
1060
|
// pause, take a deep breath, appreciate this moment of silence
|
|
@@ -1185,6 +1220,7 @@ export default function buildKernel(
|
|
|
1185
1220
|
}
|
|
1186
1221
|
}
|
|
1187
1222
|
default:
|
|
1223
|
+
// @ts-expect-error
|
|
1188
1224
|
throw Fail`unknown promise resolution '${kp.state}'`;
|
|
1189
1225
|
}
|
|
1190
1226
|
}
|
|
@@ -1542,8 +1578,8 @@ export default function buildKernel(
|
|
|
1542
1578
|
// not
|
|
1543
1579
|
/**
|
|
1544
1580
|
*
|
|
1545
|
-
* @param {
|
|
1546
|
-
* @returns {
|
|
1581
|
+
* @param {VatSyscallObject} vatSyscallObject
|
|
1582
|
+
* @returns {VatSyscallResult}
|
|
1547
1583
|
*/
|
|
1548
1584
|
function vatSyscallHandler(vatSyscallObject) {
|
|
1549
1585
|
if (!vatWarehouse.lookup(vatID)) {
|
|
@@ -1558,7 +1594,7 @@ export default function buildKernel(
|
|
|
1558
1594
|
let ksc;
|
|
1559
1595
|
/** @type { KernelSyscallResult } */
|
|
1560
1596
|
let kres = harden(['error', 'incomplete']);
|
|
1561
|
-
/** @type {
|
|
1597
|
+
/** @type { VatSyscallResult } */
|
|
1562
1598
|
let vres = harden(['error', 'incomplete']);
|
|
1563
1599
|
|
|
1564
1600
|
try {
|
|
@@ -1819,6 +1855,7 @@ export default function buildKernel(
|
|
|
1819
1855
|
{
|
|
1820
1856
|
exports: M.number(),
|
|
1821
1857
|
imports: M.number(),
|
|
1858
|
+
promises: M.number(),
|
|
1822
1859
|
kv: M.number(),
|
|
1823
1860
|
snapshots: M.number(),
|
|
1824
1861
|
transcripts: M.number(),
|
|
@@ -2080,6 +2117,7 @@ export default function buildKernel(
|
|
|
2080
2117
|
return p.data;
|
|
2081
2118
|
}
|
|
2082
2119
|
default: {
|
|
2120
|
+
// @ts-expect-error
|
|
2083
2121
|
throw Fail`invalid state for ${kpid}: ${p.state}`;
|
|
2084
2122
|
}
|
|
2085
2123
|
}
|
|
@@ -2178,6 +2216,7 @@ export default function buildKernel(
|
|
|
2178
2216
|
pinObject,
|
|
2179
2217
|
vatNameToID,
|
|
2180
2218
|
deviceNameToID,
|
|
2219
|
+
injectQueuedUpgradeEvents,
|
|
2181
2220
|
queueToKref,
|
|
2182
2221
|
kpRegisterInterest,
|
|
2183
2222
|
kpStatus,
|
|
@@ -45,14 +45,16 @@ const enableKernelGC = true;
|
|
|
45
45
|
* @typedef { import('../../types-external.js').VatKeeper } VatKeeper
|
|
46
46
|
* @typedef { import('../../types-internal.js').InternalKernelOptions } InternalKernelOptions
|
|
47
47
|
* @typedef { import('../../types-internal.js').ReapDirtThreshold } ReapDirtThreshold
|
|
48
|
+
* @import {PromiseRecord} from '../../types-internal.js';
|
|
48
49
|
* @import {CleanupBudget, CleanupWork, PolicyOutputCleanupBudget} from '../../types-external.js';
|
|
49
50
|
* @import {RunQueueEventCleanupTerminatedVat} from '../../types-internal.js';
|
|
51
|
+
* @import {SwingStoreKernelStorage} from '@agoric/swing-store';
|
|
50
52
|
*/
|
|
51
53
|
|
|
52
54
|
export { DEFAULT_REAP_DIRT_THRESHOLD_KEY };
|
|
53
55
|
|
|
54
56
|
// most recent DB schema version
|
|
55
|
-
export const CURRENT_SCHEMA_VERSION =
|
|
57
|
+
export const CURRENT_SCHEMA_VERSION = 3;
|
|
56
58
|
|
|
57
59
|
// Kernel state lives in a key-value store supporting key retrieval by
|
|
58
60
|
// lexicographic range. All keys and values are strings.
|
|
@@ -71,9 +73,9 @@ export const CURRENT_SCHEMA_VERSION = 2;
|
|
|
71
73
|
// only modified by a call to upgradeSwingset(). See below for
|
|
72
74
|
// deltas/upgrades from one version to the next.
|
|
73
75
|
//
|
|
74
|
-
// The current ("
|
|
76
|
+
// The current ("v3") schema keys/values are:
|
|
75
77
|
//
|
|
76
|
-
// version = '
|
|
78
|
+
// version = '3'
|
|
77
79
|
// vat.names = JSON([names..])
|
|
78
80
|
// vat.dynamicIDs = JSON([vatIDs..])
|
|
79
81
|
// vat.name.$NAME = $vatID = v$NN
|
|
@@ -117,6 +119,8 @@ export const CURRENT_SCHEMA_VERSION = 2;
|
|
|
117
119
|
// old (v0): v$NN.reapCountdown = $NN or 'never'
|
|
118
120
|
// v$NN.reapDirt = JSON({ deliveries, gcKrefs, computrons }) // missing keys treated as zero
|
|
119
121
|
// (leave room for v$NN.snapshotDirt and options.snapshotDirtThreshold for #6786)
|
|
122
|
+
// v$NN.vatParameters = JSON(capdata) // missing for vats created/upgraded before #8947
|
|
123
|
+
//
|
|
120
124
|
// exclude from consensus
|
|
121
125
|
// local.*
|
|
122
126
|
|
|
@@ -141,6 +145,7 @@ export const CURRENT_SCHEMA_VERSION = 2;
|
|
|
141
145
|
// gcActions = JSON(gcActions)
|
|
142
146
|
// reapQueue = JSON([vatIDs...])
|
|
143
147
|
// pinnedObjects = ko$NN[,ko$NN..]
|
|
148
|
+
// upgradeEvents = JSON([events..])
|
|
144
149
|
|
|
145
150
|
// ko.nextID = $NN
|
|
146
151
|
// ko$NN.owner = $vatID
|
|
@@ -177,7 +182,13 @@ export const CURRENT_SCHEMA_VERSION = 2;
|
|
|
177
182
|
// v2:
|
|
178
183
|
// * change `version` to `'2'`
|
|
179
184
|
// * add `vats.terminated` with `[]` as initial value
|
|
185
|
+
// v3:
|
|
186
|
+
// * change `version` to `'3'`
|
|
187
|
+
// * perform remediation for bug #9039
|
|
188
|
+
// (after v3, does not get its own version)
|
|
189
|
+
// * `upgradeEvents` recognized, but omitted if empty
|
|
180
190
|
|
|
191
|
+
/** @type {(s: string) => string[]} s */
|
|
181
192
|
export function commaSplit(s) {
|
|
182
193
|
if (s === '') {
|
|
183
194
|
return [];
|
|
@@ -211,6 +222,77 @@ export const getAllDynamicVats = getRequired => {
|
|
|
211
222
|
return JSON.parse(getRequired('vat.dynamicIDs'));
|
|
212
223
|
};
|
|
213
224
|
|
|
225
|
+
const getObjectReferenceCount = (kvStore, kref) => {
|
|
226
|
+
const data = kvStore.get(`${kref}.refCount`);
|
|
227
|
+
if (!data) {
|
|
228
|
+
return { reachable: 0, recognizable: 0 };
|
|
229
|
+
}
|
|
230
|
+
const [reachable, recognizable] = commaSplit(data).map(Number);
|
|
231
|
+
reachable <= recognizable ||
|
|
232
|
+
Fail`refmismatch(get) ${kref} ${reachable},${recognizable}`;
|
|
233
|
+
return { reachable, recognizable };
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const setObjectReferenceCount = (kvStore, kref, counts) => {
|
|
237
|
+
const { reachable, recognizable } = counts;
|
|
238
|
+
assert.typeof(reachable, 'number');
|
|
239
|
+
assert.typeof(recognizable, 'number');
|
|
240
|
+
(reachable >= 0 && recognizable >= 0) ||
|
|
241
|
+
Fail`${kref} underflow ${reachable},${recognizable}`;
|
|
242
|
+
reachable <= recognizable ||
|
|
243
|
+
Fail`refmismatch(set) ${kref} ${reachable},${recognizable}`;
|
|
244
|
+
kvStore.set(`${kref}.refCount`, `${reachable},${recognizable}`);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Increment the reference count associated with some kernel object.
|
|
249
|
+
*
|
|
250
|
+
* We track references to promises and objects, but not devices. Promises
|
|
251
|
+
* have only a "reachable" count, whereas objects track both "reachable"
|
|
252
|
+
* and "recognizable" counts.
|
|
253
|
+
*
|
|
254
|
+
* @param { (key: string) => string} getRequired
|
|
255
|
+
* @param { import('@agoric/swing-store').KVStore } kvStore
|
|
256
|
+
* @param {string} kref The kernel slot whose refcount is to be incremented.
|
|
257
|
+
* @param {string?} tag Debugging note with rough source of the reference.
|
|
258
|
+
* @param {{ isExport?: boolean, onlyRecognizable?: boolean }} options
|
|
259
|
+
* 'isExport' means the reference comes from a clist export, which counts
|
|
260
|
+
* for promises but not objects. 'onlyRecognizable' means the reference
|
|
261
|
+
* provides only recognition, not reachability
|
|
262
|
+
*/
|
|
263
|
+
export const incrementReferenceCount = (
|
|
264
|
+
getRequired,
|
|
265
|
+
kvStore,
|
|
266
|
+
kref,
|
|
267
|
+
tag,
|
|
268
|
+
options = {},
|
|
269
|
+
) => {
|
|
270
|
+
const { isExport = false, onlyRecognizable = false } = options;
|
|
271
|
+
kref || Fail`incrementRefCount called with empty kref, tag=${tag}`;
|
|
272
|
+
const { type } = parseKernelSlot(kref);
|
|
273
|
+
if (type === 'promise') {
|
|
274
|
+
const refCount = Number(getRequired(`${kref}.refCount`)) + 1;
|
|
275
|
+
// kdebug(`++ ${kref} ${tag} ${refCount}`);
|
|
276
|
+
kvStore.set(`${kref}.refCount`, `${refCount}`);
|
|
277
|
+
}
|
|
278
|
+
if (type === 'object' && !isExport) {
|
|
279
|
+
let { reachable, recognizable } = getObjectReferenceCount(kvStore, kref);
|
|
280
|
+
if (!onlyRecognizable) {
|
|
281
|
+
reachable += 1;
|
|
282
|
+
}
|
|
283
|
+
recognizable += 1;
|
|
284
|
+
// kdebug(`++ ${kref} ${tag} ${reachable},${recognizable}`);
|
|
285
|
+
setObjectReferenceCount(kvStore, kref, { reachable, recognizable });
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
export function* readQueue(queue, getRequired) {
|
|
290
|
+
const [head, tail] = JSON.parse(getRequired(`${queue}`));
|
|
291
|
+
for (let i = head; i < tail; i += 1) {
|
|
292
|
+
yield JSON.parse(getRequired(`${queue}.${i}`));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
214
296
|
// we use different starting index values for the various vNN/koNN/kdNN/kpNN
|
|
215
297
|
// slots, to reduce confusing overlap when looking at debug messages (e.g.
|
|
216
298
|
// seeing both kp1 and ko1, which are completely unrelated despite having the
|
|
@@ -388,14 +470,7 @@ export default function makeKernelKeeper(
|
|
|
388
470
|
return tail - head;
|
|
389
471
|
}
|
|
390
472
|
|
|
391
|
-
|
|
392
|
-
const [head, tail] = JSON.parse(getRequired(`${queue}`));
|
|
393
|
-
const result = [];
|
|
394
|
-
for (let i = head; i < tail; i += 1) {
|
|
395
|
-
result.push(JSON.parse(getRequired(`${queue}.${i}`)));
|
|
396
|
-
}
|
|
397
|
-
return result;
|
|
398
|
-
}
|
|
473
|
+
const dumpQueue = queue => [...readQueue(queue, getRequired)];
|
|
399
474
|
|
|
400
475
|
/**
|
|
401
476
|
* @param {InternalKernelOptions} kernelOptions
|
|
@@ -619,26 +694,9 @@ export default function makeKernelKeeper(
|
|
|
619
694
|
return parseReachableAndVatSlot(kvStore.get(kernelKey));
|
|
620
695
|
}
|
|
621
696
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return { reachable: 0, recognizable: 0 };
|
|
626
|
-
}
|
|
627
|
-
const [reachable, recognizable] = commaSplit(data).map(Number);
|
|
628
|
-
reachable <= recognizable ||
|
|
629
|
-
Fail`refmismatch(get) ${kernelSlot} ${reachable},${recognizable}`;
|
|
630
|
-
return { reachable, recognizable };
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
function setObjectRefCount(kernelSlot, { reachable, recognizable }) {
|
|
634
|
-
assert.typeof(reachable, 'number');
|
|
635
|
-
assert.typeof(recognizable, 'number');
|
|
636
|
-
(reachable >= 0 && recognizable >= 0) ||
|
|
637
|
-
Fail`${kernelSlot} underflow ${reachable},${recognizable}`;
|
|
638
|
-
reachable <= recognizable ||
|
|
639
|
-
Fail`refmismatch(set) ${kernelSlot} ${reachable},${recognizable}`;
|
|
640
|
-
kvStore.set(`${kernelSlot}.refCount`, `${reachable},${recognizable}`);
|
|
641
|
-
}
|
|
697
|
+
const getObjectRefCount = kref => getObjectReferenceCount(kvStore, kref);
|
|
698
|
+
const setObjectRefCount = (kref, counts) =>
|
|
699
|
+
setObjectReferenceCount(kvStore, kref, counts);
|
|
642
700
|
|
|
643
701
|
/**
|
|
644
702
|
* Iterate over non-durable objects exported by a vat.
|
|
@@ -790,43 +848,41 @@ export default function makeKernelKeeper(
|
|
|
790
848
|
return kpid;
|
|
791
849
|
}
|
|
792
850
|
|
|
851
|
+
/**
|
|
852
|
+
* @param {string} kernelSlot
|
|
853
|
+
* @returns {PromiseRecord}
|
|
854
|
+
*/
|
|
793
855
|
function getKernelPromise(kernelSlot) {
|
|
794
856
|
insistKernelType('promise', kernelSlot);
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
throw Fail`unknown kernelPromise '${kernelSlot}'`;
|
|
799
|
-
}
|
|
857
|
+
const state = getRequired(`${kernelSlot}.state`);
|
|
858
|
+
const refCount = Number(kvStore.get(`${kernelSlot}.refCount`));
|
|
859
|
+
switch (state) {
|
|
800
860
|
case 'unresolved': {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
p.subscribers = commaSplit(kvStore.get(`${kernelSlot}.subscribers`));
|
|
808
|
-
p.queue = Array.from(
|
|
861
|
+
const decider = kvStore.get(`${kernelSlot}.decider`) || undefined;
|
|
862
|
+
const policy = kvStore.get(`${kernelSlot}.policy`) || 'ignore';
|
|
863
|
+
const subscribers = commaSplit(
|
|
864
|
+
kvStore.get(`${kernelSlot}.subscribers`) || '',
|
|
865
|
+
);
|
|
866
|
+
const queue = Array.from(
|
|
809
867
|
getPrefixedValues(kvStore, `${kernelSlot}.queue.`),
|
|
810
868
|
).map(s => JSON.parse(s));
|
|
811
|
-
|
|
869
|
+
return harden({ state, refCount, decider, policy, subscribers, queue });
|
|
812
870
|
}
|
|
813
871
|
case 'fulfilled':
|
|
814
872
|
case 'rejected': {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
slots: commaSplit(kvStore.get(`${kernelSlot}.data.slots`)),
|
|
873
|
+
const data = {
|
|
874
|
+
body: getRequired(`${kernelSlot}.data.body`),
|
|
875
|
+
slots: commaSplit(getRequired(`${kernelSlot}.data.slots`)),
|
|
819
876
|
};
|
|
820
|
-
for (const s of
|
|
877
|
+
for (const s of data.slots) {
|
|
821
878
|
parseKernelSlot(s);
|
|
822
879
|
}
|
|
823
|
-
|
|
880
|
+
return harden({ state, refCount, data });
|
|
824
881
|
}
|
|
825
882
|
default: {
|
|
826
|
-
throw Fail`unknown state for ${kernelSlot}: ${
|
|
883
|
+
throw Fail`unknown state for ${kernelSlot}: ${state}`;
|
|
827
884
|
}
|
|
828
885
|
}
|
|
829
|
-
return harden(p);
|
|
830
886
|
}
|
|
831
887
|
|
|
832
888
|
function getResolveablePromise(kpid, expectedDecider) {
|
|
@@ -835,7 +891,9 @@ export default function makeKernelKeeper(
|
|
|
835
891
|
insistVatID(expectedDecider);
|
|
836
892
|
}
|
|
837
893
|
const p = getKernelPromise(kpid);
|
|
838
|
-
p.state
|
|
894
|
+
if (p.state !== 'unresolved') {
|
|
895
|
+
throw Fail`${kpid} was already resolved`;
|
|
896
|
+
}
|
|
839
897
|
if (expectedDecider) {
|
|
840
898
|
p.decider === expectedDecider ||
|
|
841
899
|
Fail`${kpid} is decided by ${p.decider}, not ${expectedDecider}`;
|
|
@@ -894,6 +952,7 @@ export default function makeKernelKeeper(
|
|
|
894
952
|
// up the resolution *now* and set the correct target early. Doing that
|
|
895
953
|
// might make it easier to remove the Promise Table entry earlier.
|
|
896
954
|
const p = getKernelPromise(kernelSlot);
|
|
955
|
+
assert.equal(p.state, 'unresolved');
|
|
897
956
|
for (const msg of p.queue) {
|
|
898
957
|
const entry = harden({ type: 'send', target: kernelSlot, msg });
|
|
899
958
|
enqueue('acceptanceQueue', entry);
|
|
@@ -964,6 +1023,7 @@ export default function makeKernelKeeper(
|
|
|
964
1023
|
const work = {
|
|
965
1024
|
exports: 0,
|
|
966
1025
|
imports: 0,
|
|
1026
|
+
promises: 0,
|
|
967
1027
|
kv: 0,
|
|
968
1028
|
snapshots: 0,
|
|
969
1029
|
transcripts: 0,
|
|
@@ -988,6 +1048,7 @@ export default function makeKernelKeeper(
|
|
|
988
1048
|
const clistPrefix = `${vatID}.c.`;
|
|
989
1049
|
const exportPrefix = `${clistPrefix}o+`;
|
|
990
1050
|
const importPrefix = `${clistPrefix}o-`;
|
|
1051
|
+
const promisePrefix = `${clistPrefix}p`;
|
|
991
1052
|
|
|
992
1053
|
// Note: ASCII order is "+,-./", and we rely upon this to split the
|
|
993
1054
|
// keyspace into the various o+NN/o-NN/etc spaces. If we were using a
|
|
@@ -1040,8 +1101,22 @@ export default function makeKernelKeeper(
|
|
|
1040
1101
|
}
|
|
1041
1102
|
}
|
|
1042
1103
|
|
|
1043
|
-
//
|
|
1044
|
-
// so they already
|
|
1104
|
+
// The caller used enumeratePromisesByDecider() before calling us,
|
|
1105
|
+
// so they have already rejected the orphan promises, but those
|
|
1106
|
+
// kpids are still present in the dead vat's c-list. Clean those
|
|
1107
|
+
// up now.
|
|
1108
|
+
remaining = budget.promises ?? budget.default;
|
|
1109
|
+
for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
|
|
1110
|
+
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1111
|
+
const vref = stripPrefix(clistPrefix, k);
|
|
1112
|
+
vatKeeper.deleteCListEntry(kref, vref);
|
|
1113
|
+
// that will also delete both db keys
|
|
1114
|
+
work.promises += 1;
|
|
1115
|
+
remaining -= 1;
|
|
1116
|
+
if (remaining <= 0) {
|
|
1117
|
+
return { done: false, work };
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1045
1120
|
|
|
1046
1121
|
// now loop back through everything and delete it all
|
|
1047
1122
|
remaining = budget.kv ?? budget.default;
|
|
@@ -1139,18 +1214,22 @@ export default function makeKernelKeeper(
|
|
|
1139
1214
|
function setDecider(kpid, decider) {
|
|
1140
1215
|
insistVatID(decider);
|
|
1141
1216
|
const p = getKernelPromise(kpid);
|
|
1142
|
-
p.state
|
|
1143
|
-
!p.decider
|
|
1217
|
+
assert.equal(p.state, 'unresolved', `${kpid} was already resolved`);
|
|
1218
|
+
assert(!p.decider, `${kpid} has decider ${p.decider}, not empty`);
|
|
1144
1219
|
kvStore.set(`${kpid}.decider`, decider);
|
|
1145
1220
|
}
|
|
1146
1221
|
|
|
1147
1222
|
function clearDecider(kpid) {
|
|
1148
1223
|
const p = getKernelPromise(kpid);
|
|
1149
|
-
p.state
|
|
1150
|
-
p.decider
|
|
1224
|
+
assert.equal(p.state, 'unresolved', `${kpid} was already resolved`);
|
|
1225
|
+
assert(p.decider, `${kpid} does not have a decider`);
|
|
1151
1226
|
kvStore.set(`${kpid}.decider`, '');
|
|
1152
1227
|
}
|
|
1153
1228
|
|
|
1229
|
+
/**
|
|
1230
|
+
* @param {string} vatID
|
|
1231
|
+
* @returns {IterableIterator<[kpid: string, p: PromiseRecord]>}
|
|
1232
|
+
*/
|
|
1154
1233
|
function* enumeratePromisesByDecider(vatID) {
|
|
1155
1234
|
insistVatID(vatID);
|
|
1156
1235
|
const promisePrefix = `${vatID}.c.p`;
|
|
@@ -1164,10 +1243,10 @@ export default function makeKernelKeeper(
|
|
|
1164
1243
|
// whether the vat is the decider or not. If it is, we add the promise
|
|
1165
1244
|
// to the list of promises that must be rejected because the dead vat
|
|
1166
1245
|
// will never be able to act upon them.
|
|
1167
|
-
const kpid =
|
|
1246
|
+
const kpid = getRequired(k);
|
|
1168
1247
|
const p = getKernelPromise(kpid);
|
|
1169
1248
|
if (p.state === 'unresolved' && p.decider === vatID) {
|
|
1170
|
-
yield kpid;
|
|
1249
|
+
yield [kpid, p];
|
|
1171
1250
|
}
|
|
1172
1251
|
}
|
|
1173
1252
|
}
|
|
@@ -1177,6 +1256,7 @@ export default function makeKernelKeeper(
|
|
|
1177
1256
|
insistKernelType('promise', kernelSlot);
|
|
1178
1257
|
insistVatID(vatID);
|
|
1179
1258
|
const p = getKernelPromise(kernelSlot);
|
|
1259
|
+
assert.equal(p.state, 'unresolved');
|
|
1180
1260
|
const s = new Set(p.subscribers);
|
|
1181
1261
|
s.add(vatID);
|
|
1182
1262
|
const v = Array.from(s).sort().join(',');
|
|
@@ -1207,6 +1287,27 @@ export default function makeKernelKeeper(
|
|
|
1207
1287
|
return dequeue('acceptanceQueue');
|
|
1208
1288
|
}
|
|
1209
1289
|
|
|
1290
|
+
function injectQueuedUpgradeEvents() {
|
|
1291
|
+
// refcounts: Any krefs in `upgradeEvents` must have a refcount to
|
|
1292
|
+
// represent the list's hold on those objects. When
|
|
1293
|
+
// upgradeSwingset() creates these events, it must also
|
|
1294
|
+
// incref(kref), otherwise we run the risk of dropping the kref by
|
|
1295
|
+
// the time injectQueuedUpgradeEvents() is called. We're nominally
|
|
1296
|
+
// removing each event from upgradeEvents (decref), then pushing
|
|
1297
|
+
// it onto the run-queue (incref), but since those two cancel each
|
|
1298
|
+
// other out, we don't actually need to modify any reference
|
|
1299
|
+
// counts from within this function. Note that
|
|
1300
|
+
// addToAcceptanceQueue does not increment refcounts, just kernel
|
|
1301
|
+
// queue-length stats.
|
|
1302
|
+
|
|
1303
|
+
const events = JSON.parse(kvStore.get('upgradeEvents') || '[]');
|
|
1304
|
+
kvStore.delete('upgradeEvents');
|
|
1305
|
+
for (const e of events) {
|
|
1306
|
+
assert(e.type, `not an event`);
|
|
1307
|
+
addToAcceptanceQueue(e);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1210
1311
|
function allocateMeter(remaining, threshold) {
|
|
1211
1312
|
if (remaining !== 'unlimited') {
|
|
1212
1313
|
assert.typeof(remaining, 'bigint');
|
|
@@ -1414,40 +1515,8 @@ export default function makeKernelKeeper(
|
|
|
1414
1515
|
maybeFreeKrefs.add(kref);
|
|
1415
1516
|
}
|
|
1416
1517
|
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
*
|
|
1420
|
-
* We track references to promises and objects, but not devices. Promises
|
|
1421
|
-
* have only a "reachable" count, whereas objects track both "reachable"
|
|
1422
|
-
* and "recognizable" counts.
|
|
1423
|
-
*
|
|
1424
|
-
* @param {unknown} kernelSlot The kernel slot whose refcount is to be incremented.
|
|
1425
|
-
* @param {string?} tag Debugging note with rough source of the reference.
|
|
1426
|
-
* @param {{ isExport?: boolean, onlyRecognizable?: boolean }} options
|
|
1427
|
-
* 'isExport' means the reference comes from a clist export, which counts
|
|
1428
|
-
* for promises but not objects. 'onlyRecognizable' means the reference
|
|
1429
|
-
* provides only recognition, not reachability
|
|
1430
|
-
*/
|
|
1431
|
-
function incrementRefCount(kernelSlot, tag, options = {}) {
|
|
1432
|
-
const { isExport = false, onlyRecognizable = false } = options;
|
|
1433
|
-
kernelSlot ||
|
|
1434
|
-
Fail`incrementRefCount called with empty kernelSlot, tag=${tag}`;
|
|
1435
|
-
const { type } = parseKernelSlot(kernelSlot);
|
|
1436
|
-
if (type === 'promise') {
|
|
1437
|
-
const refCount = Nat(BigInt(getRequired(`${kernelSlot}.refCount`))) + 1n;
|
|
1438
|
-
// kdebug(`++ ${kernelSlot} ${tag} ${refCount}`);
|
|
1439
|
-
kvStore.set(`${kernelSlot}.refCount`, `${refCount}`);
|
|
1440
|
-
}
|
|
1441
|
-
if (type === 'object' && !isExport) {
|
|
1442
|
-
let { reachable, recognizable } = getObjectRefCount(kernelSlot);
|
|
1443
|
-
if (!onlyRecognizable) {
|
|
1444
|
-
reachable += 1;
|
|
1445
|
-
}
|
|
1446
|
-
recognizable += 1;
|
|
1447
|
-
// kdebug(`++ ${kernelSlot} ${tag} ${reachable},${recognizable}`);
|
|
1448
|
-
setObjectRefCount(kernelSlot, { reachable, recognizable });
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1518
|
+
const incrementRefCount = (kref, tag, options = {}) =>
|
|
1519
|
+
incrementReferenceCount(getRequired, kvStore, kref, tag, options);
|
|
1451
1520
|
|
|
1452
1521
|
/**
|
|
1453
1522
|
* Decrement the reference count associated with some kernel object.
|
|
@@ -1539,13 +1608,15 @@ export default function makeKernelKeeper(
|
|
|
1539
1608
|
const kp = getKernelPromise(kpid);
|
|
1540
1609
|
if (kp.refCount === 0) {
|
|
1541
1610
|
let idx = 0;
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1611
|
+
if (kp.state === 'fulfilled' || kp.state === 'rejected') {
|
|
1612
|
+
// #9889 don't assume promise is settled
|
|
1613
|
+
for (const slot of kp.data.slots) {
|
|
1614
|
+
// Note: the following decrement can result in an addition to the
|
|
1615
|
+
// maybeFreeKrefs set, which we are in the midst of iterating.
|
|
1616
|
+
// TC39 went to a lot of trouble to ensure that this is kosher.
|
|
1617
|
+
decrementRefCount(slot, `gc|${kpid}|s${idx}`);
|
|
1618
|
+
idx += 1;
|
|
1619
|
+
}
|
|
1549
1620
|
}
|
|
1550
1621
|
deleteKernelPromise(kpid);
|
|
1551
1622
|
}
|
|
@@ -1936,6 +2007,8 @@ export default function makeKernelKeeper(
|
|
|
1936
2007
|
getAcceptanceQueueLength,
|
|
1937
2008
|
getNextAcceptanceQueueMsg,
|
|
1938
2009
|
|
|
2010
|
+
injectQueuedUpgradeEvents,
|
|
2011
|
+
|
|
1939
2012
|
allocateMeter,
|
|
1940
2013
|
addMeterRemaining,
|
|
1941
2014
|
setMeterThreshold,
|
|
@@ -1981,3 +2054,4 @@ export default function makeKernelKeeper(
|
|
|
1981
2054
|
dump,
|
|
1982
2055
|
});
|
|
1983
2056
|
}
|
|
2057
|
+
/** @typedef {ReturnType<typeof makeKernelKeeper>} KernelKeeper */
|
|
@@ -7,6 +7,7 @@ import { isObject } from '@endo/marshal';
|
|
|
7
7
|
import { parseKernelSlot } from '../parseKernelSlots.js';
|
|
8
8
|
import { makeVatSlot, parseVatSlot } from '../../lib/parseVatSlots.js';
|
|
9
9
|
import { insistVatID } from '../../lib/id.js';
|
|
10
|
+
import { insistCapData } from '../../lib/capdata.js';
|
|
10
11
|
import { kdebug } from '../../lib/kdebug.js';
|
|
11
12
|
import {
|
|
12
13
|
parseReachableAndVatSlot,
|
|
@@ -173,6 +174,35 @@ export function makeVatKeeper(
|
|
|
173
174
|
return harden(options);
|
|
174
175
|
}
|
|
175
176
|
|
|
177
|
+
/**
|
|
178
|
+
* @param {SwingSetCapData} newVPCD
|
|
179
|
+
*/
|
|
180
|
+
function setVatParameters(newVPCD) {
|
|
181
|
+
insistCapData(newVPCD);
|
|
182
|
+
const key = `${vatID}.vatParameters`;
|
|
183
|
+
// increment-before-decrement to minimize spurious rc=0 checks
|
|
184
|
+
for (const kref of newVPCD.slots) {
|
|
185
|
+
incrementRefCount(kref, `${vatID}.vatParameters`);
|
|
186
|
+
}
|
|
187
|
+
const old = kvStore.get(key) || '{"slots":[]}';
|
|
188
|
+
for (const kref of JSON.parse(old).slots) {
|
|
189
|
+
decrementRefCount(kref, `${vatID}.vatParameters`);
|
|
190
|
+
}
|
|
191
|
+
kvStore.set(key, JSON.stringify(newVPCD));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @returns {SwingSetCapData | undefined} vpcd
|
|
196
|
+
*/
|
|
197
|
+
function getVatParameters() {
|
|
198
|
+
const key = `${vatID}.vatParameters`;
|
|
199
|
+
const old = kvStore.get(key);
|
|
200
|
+
if (old) {
|
|
201
|
+
return JSON.parse(old);
|
|
202
|
+
}
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
176
206
|
// This is named "addDirt" because it should increment all dirt
|
|
177
207
|
// counters (both for reap/BOYD and for heap snapshotting). We don't
|
|
178
208
|
// have `heapSnapshotDirt` yet, but when we do, it should get
|
|
@@ -768,6 +798,8 @@ export function makeVatKeeper(
|
|
|
768
798
|
setSourceAndOptions,
|
|
769
799
|
getSourceAndOptions,
|
|
770
800
|
getOptions,
|
|
801
|
+
setVatParameters,
|
|
802
|
+
getVatParameters,
|
|
771
803
|
addDirt,
|
|
772
804
|
getReapDirt,
|
|
773
805
|
clearReapDirt,
|
|
@@ -50,6 +50,7 @@ function makeTranslateKernelDeliveryToVatDelivery(vatID, kernelKeeper) {
|
|
|
50
50
|
parseVatSlot(targetSlot).allocatedByVat || Fail`deliver() to wrong vat`;
|
|
51
51
|
} else if (type === 'promise') {
|
|
52
52
|
const p = kernelKeeper.getKernelPromise(target);
|
|
53
|
+
assert(p.state === 'unresolved');
|
|
53
54
|
p.decider === vatID || Fail`wrong decider`;
|
|
54
55
|
}
|
|
55
56
|
const inputSlots = msg.methargs.slots.map(slot =>
|
|
@@ -59,7 +60,9 @@ function makeTranslateKernelDeliveryToVatDelivery(vatID, kernelKeeper) {
|
|
|
59
60
|
if (msg.result) {
|
|
60
61
|
insistKernelType('promise', msg.result);
|
|
61
62
|
const p = kernelKeeper.getKernelPromise(msg.result);
|
|
62
|
-
p.state
|
|
63
|
+
if (p.state !== 'unresolved') {
|
|
64
|
+
throw Fail`result ${msg.result} already resolved`;
|
|
65
|
+
}
|
|
63
66
|
!p.decider || Fail`result ${msg.result} already has decider ${p.decider}`;
|
|
64
67
|
resultSlot = vatKeeper.mapKernelSlotToVatSlot(msg.result);
|
|
65
68
|
insistVatType('promise', resultSlot);
|
|
@@ -318,8 +321,9 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) {
|
|
|
318
321
|
// In the case of non-pipelining vats these checks are redundant since
|
|
319
322
|
// we're guaranteed to have a promise newly allocated by the vat.
|
|
320
323
|
const p = kernelKeeper.getKernelPromise(result);
|
|
321
|
-
p.state
|
|
322
|
-
Fail`send() result ${result} is already resolved`;
|
|
324
|
+
if (p.state !== 'unresolved') {
|
|
325
|
+
throw Fail`send() result ${result} is already resolved`;
|
|
326
|
+
}
|
|
323
327
|
p.decider === vatID ||
|
|
324
328
|
Fail`send() result ${result} is decided by ${p.decider} not ${vatID}`;
|
|
325
329
|
kernelKeeper.clearDecider(result);
|
package/src/types-external.js
CHANGED
|
@@ -124,7 +124,7 @@ export {};
|
|
|
124
124
|
*
|
|
125
125
|
* @typedef { { transcriptCount: number } } VatStats
|
|
126
126
|
* @typedef { ReturnType<typeof import('./kernel/state/vatKeeper.js').makeVatKeeper> } VatKeeper
|
|
127
|
-
* @typedef {
|
|
127
|
+
* @typedef { import('./kernel/state/kernelKeeper.js').KernelKeeper } KernelKeeper
|
|
128
128
|
* @typedef { Awaited<ReturnType<typeof import('@agoric/xsnap').xsnap>> } XSnap
|
|
129
129
|
* @typedef { (dr: VatDeliveryResult) => void } SlogFinishDelivery
|
|
130
130
|
* @typedef { (ksr: KernelSyscallResult, vsr: VatSyscallResult) => void } SlogFinishSyscall
|
|
@@ -227,6 +227,7 @@ export {};
|
|
|
227
227
|
*
|
|
228
228
|
* @typedef { { exports: number,
|
|
229
229
|
* imports: number,
|
|
230
|
+
* promises: number,
|
|
230
231
|
* kv: number,
|
|
231
232
|
* snapshots: number,
|
|
232
233
|
* transcripts: number,
|
|
@@ -283,6 +284,13 @@ export {};
|
|
|
283
284
|
* @property { boolean } [restartWorkerOnSnapshot] Reload worker immediately upon snapshot creation
|
|
284
285
|
*/
|
|
285
286
|
|
|
287
|
+
/**
|
|
288
|
+
* @typedef { import('./devices/mailbox/mailbox.js').Mailbox } Mailbox
|
|
289
|
+
*/
|
|
290
|
+
/**
|
|
291
|
+
* @typedef { import('./devices/mailbox/mailbox.js').MailboxExport } MailboxExport
|
|
292
|
+
*/
|
|
293
|
+
|
|
286
294
|
/**
|
|
287
295
|
* Vat Creation and Management
|
|
288
296
|
*
|
package/src/types-internal.js
CHANGED
|
@@ -92,6 +92,17 @@ export {};
|
|
|
92
92
|
* @property {number} [computrons]
|
|
93
93
|
*/
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @typedef {{ state: 'unresolved', refCount: number,
|
|
97
|
+
* decider: string | undefined, policy: string,
|
|
98
|
+
* subscribers: string[], queue: string[],
|
|
99
|
+
* }} UnresolvedPromiseRecord
|
|
100
|
+
* @typedef {{ state: 'fulfilled' | 'rejected', refCount: number,
|
|
101
|
+
* data: SwingSetCapData,
|
|
102
|
+
* }} SettledPromiseRecord
|
|
103
|
+
* @typedef {UnresolvedPromiseRecord | SettledPromiseRecord} PromiseRecord
|
|
104
|
+
*/
|
|
105
|
+
|
|
95
106
|
/**
|
|
96
107
|
* @typedef {{
|
|
97
108
|
* enablePipelining: boolean,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Like prepare-strict-test-env but also sets up ses-ava and provides
|
|
3
|
+
* the ses-ava `test` function to be used as if it is the ava
|
|
4
|
+
* `test` function.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import '@agoric/swingset-liveslots/tools/prepare-strict-test-env.js';
|
|
8
|
+
|
|
9
|
+
import { wrapTest } from '@endo/ses-ava';
|
|
10
|
+
import rawTest from 'ava';
|
|
11
|
+
|
|
12
|
+
export * from '@agoric/swingset-liveslots/tools/prepare-strict-test-env.js';
|
|
13
|
+
|
|
14
|
+
export const test = wrapTest(rawTest);
|
|
15
|
+
|
|
16
|
+
// Does not import from a module because we're testing the global env
|
|
17
|
+
/* global globalThis */
|
|
18
|
+
export const VatData = globalThis.VatData;
|
|
19
|
+
assert(VatData);
|