@agoric/swingset-vat 0.32.3-dev-5c932ce.0 → 0.32.3-dev-9c73ae4.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 +12 -12
- package/src/controller/controller.js +6 -0
- package/src/controller/upgradeSwingset.js +132 -2
- package/src/kernel/kernel.js +46 -11
- package/src/kernel/state/kernelKeeper.js +173 -105
- package/src/kernel/vatTranslator.js +7 -3
- package/src/types-external.js +1 -0
- package/src/types-internal.js +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/swingset-vat",
|
|
3
|
-
"version": "0.32.3-dev-
|
|
3
|
+
"version": "0.32.3-dev-9c73ae4.0+9c73ae4",
|
|
4
4
|
"description": "Vat/Container Launcher",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -27,16 +27,16 @@
|
|
|
27
27
|
"@types/yargs-parser": "^21.0.0"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@agoric/internal": "0.3.3-dev-
|
|
31
|
-
"@agoric/kmarshal": "0.1.1-dev-
|
|
32
|
-
"@agoric/store": "0.9.3-dev-
|
|
33
|
-
"@agoric/swing-store": "0.9.2-dev-
|
|
34
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
35
|
-
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-
|
|
36
|
-
"@agoric/time": "0.3.3-dev-
|
|
37
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
38
|
-
"@agoric/xsnap": "0.14.3-dev-
|
|
39
|
-
"@agoric/xsnap-lockdown": "0.14.1-dev-
|
|
30
|
+
"@agoric/internal": "0.3.3-dev-9c73ae4.0+9c73ae4",
|
|
31
|
+
"@agoric/kmarshal": "0.1.1-dev-9c73ae4.0+9c73ae4",
|
|
32
|
+
"@agoric/store": "0.9.3-dev-9c73ae4.0+9c73ae4",
|
|
33
|
+
"@agoric/swing-store": "0.9.2-dev-9c73ae4.0+9c73ae4",
|
|
34
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-9c73ae4.0+9c73ae4",
|
|
35
|
+
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-9c73ae4.0+9c73ae4",
|
|
36
|
+
"@agoric/time": "0.3.3-dev-9c73ae4.0+9c73ae4",
|
|
37
|
+
"@agoric/vat-data": "0.5.3-dev-9c73ae4.0+9c73ae4",
|
|
38
|
+
"@agoric/xsnap": "0.14.3-dev-9c73ae4.0+9c73ae4",
|
|
39
|
+
"@agoric/xsnap-lockdown": "0.14.1-dev-9c73ae4.0+9c73ae4",
|
|
40
40
|
"@endo/base64": "^1.0.8",
|
|
41
41
|
"@endo/bundle-source": "^3.4.2",
|
|
42
42
|
"@endo/captp": "^4.4.2",
|
|
@@ -103,5 +103,5 @@
|
|
|
103
103
|
"typeCoverage": {
|
|
104
104
|
"atLeast": 75.82
|
|
105
105
|
},
|
|
106
|
-
"gitHead": "
|
|
106
|
+
"gitHead": "9c73ae4bfa7d48114690e694b065ee0a1ce99e30"
|
|
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);
|
package/src/kernel/kernel.js
CHANGED
|
@@ -215,6 +215,10 @@ export default function buildKernel(
|
|
|
215
215
|
return deviceID;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
function injectQueuedUpgradeEvents() {
|
|
219
|
+
kernelKeeper.injectQueuedUpgradeEvents();
|
|
220
|
+
}
|
|
221
|
+
|
|
218
222
|
function addImport(forVatID, what) {
|
|
219
223
|
if (!started) {
|
|
220
224
|
throw Error('must do kernel.start() before addImport()');
|
|
@@ -287,15 +291,16 @@ export default function buildKernel(
|
|
|
287
291
|
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
|
|
288
292
|
critical = vatKeeper.getOptions().critical;
|
|
289
293
|
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
-
const deadPromises = [...kernelKeeper.enumeratePromisesByDecider(vatID)];
|
|
293
|
-
// 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)
|
|
294
296
|
kernelKeeper.deleteVatID(vatID);
|
|
295
297
|
kernelKeeper.markVatAsTerminated(vatID);
|
|
296
298
|
deferred.push(kernelKeeper.removeVatFromSwingStoreExports(vatID));
|
|
297
|
-
|
|
298
|
-
|
|
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);
|
|
299
304
|
}
|
|
300
305
|
}
|
|
301
306
|
if (critical) {
|
|
@@ -597,7 +602,9 @@ export default function buildKernel(
|
|
|
597
602
|
const p = kernelKeeper.getKernelPromise(kpid);
|
|
598
603
|
kernelKeeper.incStat('dispatchNotify');
|
|
599
604
|
const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
|
|
600
|
-
p.state
|
|
605
|
+
if (p.state === 'unresolved') {
|
|
606
|
+
throw Fail`spurious notification ${kpid}`;
|
|
607
|
+
}
|
|
601
608
|
/** @type { KernelDeliveryOneNotify[] } */
|
|
602
609
|
const resolutions = [];
|
|
603
610
|
if (!vatKeeper.hasCListEntry(kpid)) {
|
|
@@ -612,7 +619,9 @@ export default function buildKernel(
|
|
|
612
619
|
return NO_DELIVERY_CRANK_RESULTS;
|
|
613
620
|
}
|
|
614
621
|
for (const toResolve of targets) {
|
|
615
|
-
const
|
|
622
|
+
const tp = kernelKeeper.getKernelPromise(toResolve);
|
|
623
|
+
assert(tp.state !== 'unresolved');
|
|
624
|
+
const { state, data } = tp;
|
|
616
625
|
resolutions.push([toResolve, { state, data }]);
|
|
617
626
|
}
|
|
618
627
|
/** @type { KernelDeliveryNotify } */
|
|
@@ -701,6 +710,7 @@ export default function buildKernel(
|
|
|
701
710
|
total:
|
|
702
711
|
work.exports +
|
|
703
712
|
work.imports +
|
|
713
|
+
work.promises +
|
|
704
714
|
work.kv +
|
|
705
715
|
work.snapshots +
|
|
706
716
|
work.transcripts,
|
|
@@ -995,9 +1005,30 @@ export default function buildKernel(
|
|
|
995
1005
|
return results;
|
|
996
1006
|
}
|
|
997
1007
|
|
|
998
|
-
//
|
|
999
|
-
|
|
1000
|
-
|
|
1008
|
+
// We are homesick for a future in which most promises are
|
|
1009
|
+
// durable, and vats do not need to subscribe to their own
|
|
1010
|
+
// promises to make promise-watchers work. In that world, vats
|
|
1011
|
+
// somehow mark the non-durable promises, which we must
|
|
1012
|
+
// reject/disconnect on their behalf during upgrade.
|
|
1013
|
+
//
|
|
1014
|
+
// To handle the present reality, without durable promises, we
|
|
1015
|
+
// pretend that all promises are so marked.
|
|
1016
|
+
|
|
1017
|
+
// reject all ephemeral promises for which the vat was decider
|
|
1018
|
+
for (const [kpid, p] of kernelKeeper.enumeratePromisesByDecider(vatID)) {
|
|
1019
|
+
const isEphemeral = true; // future vats will mark these explicitly
|
|
1020
|
+
const selfSubscribed =
|
|
1021
|
+
p.state === 'unresolved' && p.subscribers.includes(vatID);
|
|
1022
|
+
if (isEphemeral) {
|
|
1023
|
+
resolveToError(kpid, disconnectionCapData, vatID);
|
|
1024
|
+
if (!selfSubscribed) {
|
|
1025
|
+
// If the vat was subscribed to its own promise, the
|
|
1026
|
+
// resolveToError will enqueue a dispatch.notify, whose delivery
|
|
1027
|
+
// will delete the c-list entry. If it is *not* subscribed, we
|
|
1028
|
+
// should delete the c-list entry now, because nobody else will.
|
|
1029
|
+
vatKeeper.deleteCListEntriesForKernelSlots([kpid]);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1001
1032
|
}
|
|
1002
1033
|
|
|
1003
1034
|
// simulate an abandonExports syscall from the vat,
|
|
@@ -1186,6 +1217,7 @@ export default function buildKernel(
|
|
|
1186
1217
|
}
|
|
1187
1218
|
}
|
|
1188
1219
|
default:
|
|
1220
|
+
// @ts-expect-error
|
|
1189
1221
|
throw Fail`unknown promise resolution '${kp.state}'`;
|
|
1190
1222
|
}
|
|
1191
1223
|
}
|
|
@@ -1820,6 +1852,7 @@ export default function buildKernel(
|
|
|
1820
1852
|
{
|
|
1821
1853
|
exports: M.number(),
|
|
1822
1854
|
imports: M.number(),
|
|
1855
|
+
promises: M.number(),
|
|
1823
1856
|
kv: M.number(),
|
|
1824
1857
|
snapshots: M.number(),
|
|
1825
1858
|
transcripts: M.number(),
|
|
@@ -2081,6 +2114,7 @@ export default function buildKernel(
|
|
|
2081
2114
|
return p.data;
|
|
2082
2115
|
}
|
|
2083
2116
|
default: {
|
|
2117
|
+
// @ts-expect-error
|
|
2084
2118
|
throw Fail`invalid state for ${kpid}: ${p.state}`;
|
|
2085
2119
|
}
|
|
2086
2120
|
}
|
|
@@ -2179,6 +2213,7 @@ export default function buildKernel(
|
|
|
2179
2213
|
pinObject,
|
|
2180
2214
|
vatNameToID,
|
|
2181
2215
|
deviceNameToID,
|
|
2216
|
+
injectQueuedUpgradeEvents,
|
|
2182
2217
|
queueToKref,
|
|
2183
2218
|
kpRegisterInterest,
|
|
2184
2219
|
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
|
|
@@ -141,6 +143,7 @@ export const CURRENT_SCHEMA_VERSION = 2;
|
|
|
141
143
|
// gcActions = JSON(gcActions)
|
|
142
144
|
// reapQueue = JSON([vatIDs...])
|
|
143
145
|
// pinnedObjects = ko$NN[,ko$NN..]
|
|
146
|
+
// upgradeEvents = JSON([events..])
|
|
144
147
|
|
|
145
148
|
// ko.nextID = $NN
|
|
146
149
|
// ko$NN.owner = $vatID
|
|
@@ -177,6 +180,11 @@ export const CURRENT_SCHEMA_VERSION = 2;
|
|
|
177
180
|
// v2:
|
|
178
181
|
// * change `version` to `'2'`
|
|
179
182
|
// * add `vats.terminated` with `[]` as initial value
|
|
183
|
+
// v3:
|
|
184
|
+
// * change `version` to `'3'`
|
|
185
|
+
// * perform remediation for bug #9039
|
|
186
|
+
// (after v3, does not get its own version)
|
|
187
|
+
// * `upgradeEvents` recognized, but omitted if empty
|
|
180
188
|
|
|
181
189
|
/** @type {(s: string) => string[]} s */
|
|
182
190
|
export function commaSplit(s) {
|
|
@@ -212,6 +220,77 @@ export const getAllDynamicVats = getRequired => {
|
|
|
212
220
|
return JSON.parse(getRequired('vat.dynamicIDs'));
|
|
213
221
|
};
|
|
214
222
|
|
|
223
|
+
const getObjectReferenceCount = (kvStore, kref) => {
|
|
224
|
+
const data = kvStore.get(`${kref}.refCount`);
|
|
225
|
+
if (!data) {
|
|
226
|
+
return { reachable: 0, recognizable: 0 };
|
|
227
|
+
}
|
|
228
|
+
const [reachable, recognizable] = commaSplit(data).map(Number);
|
|
229
|
+
reachable <= recognizable ||
|
|
230
|
+
Fail`refmismatch(get) ${kref} ${reachable},${recognizable}`;
|
|
231
|
+
return { reachable, recognizable };
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const setObjectReferenceCount = (kvStore, kref, counts) => {
|
|
235
|
+
const { reachable, recognizable } = counts;
|
|
236
|
+
assert.typeof(reachable, 'number');
|
|
237
|
+
assert.typeof(recognizable, 'number');
|
|
238
|
+
(reachable >= 0 && recognizable >= 0) ||
|
|
239
|
+
Fail`${kref} underflow ${reachable},${recognizable}`;
|
|
240
|
+
reachable <= recognizable ||
|
|
241
|
+
Fail`refmismatch(set) ${kref} ${reachable},${recognizable}`;
|
|
242
|
+
kvStore.set(`${kref}.refCount`, `${reachable},${recognizable}`);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Increment the reference count associated with some kernel object.
|
|
247
|
+
*
|
|
248
|
+
* We track references to promises and objects, but not devices. Promises
|
|
249
|
+
* have only a "reachable" count, whereas objects track both "reachable"
|
|
250
|
+
* and "recognizable" counts.
|
|
251
|
+
*
|
|
252
|
+
* @param { (key: string) => string} getRequired
|
|
253
|
+
* @param { import('@agoric/swing-store').KVStore } kvStore
|
|
254
|
+
* @param {string} kref The kernel slot whose refcount is to be incremented.
|
|
255
|
+
* @param {string?} tag Debugging note with rough source of the reference.
|
|
256
|
+
* @param {{ isExport?: boolean, onlyRecognizable?: boolean }} options
|
|
257
|
+
* 'isExport' means the reference comes from a clist export, which counts
|
|
258
|
+
* for promises but not objects. 'onlyRecognizable' means the reference
|
|
259
|
+
* provides only recognition, not reachability
|
|
260
|
+
*/
|
|
261
|
+
export const incrementReferenceCount = (
|
|
262
|
+
getRequired,
|
|
263
|
+
kvStore,
|
|
264
|
+
kref,
|
|
265
|
+
tag,
|
|
266
|
+
options = {},
|
|
267
|
+
) => {
|
|
268
|
+
const { isExport = false, onlyRecognizable = false } = options;
|
|
269
|
+
kref || Fail`incrementRefCount called with empty kref, tag=${tag}`;
|
|
270
|
+
const { type } = parseKernelSlot(kref);
|
|
271
|
+
if (type === 'promise') {
|
|
272
|
+
const refCount = Number(getRequired(`${kref}.refCount`)) + 1;
|
|
273
|
+
// kdebug(`++ ${kref} ${tag} ${refCount}`);
|
|
274
|
+
kvStore.set(`${kref}.refCount`, `${refCount}`);
|
|
275
|
+
}
|
|
276
|
+
if (type === 'object' && !isExport) {
|
|
277
|
+
let { reachable, recognizable } = getObjectReferenceCount(kvStore, kref);
|
|
278
|
+
if (!onlyRecognizable) {
|
|
279
|
+
reachable += 1;
|
|
280
|
+
}
|
|
281
|
+
recognizable += 1;
|
|
282
|
+
// kdebug(`++ ${kref} ${tag} ${reachable},${recognizable}`);
|
|
283
|
+
setObjectReferenceCount(kvStore, kref, { reachable, recognizable });
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
export function* readQueue(queue, getRequired) {
|
|
288
|
+
const [head, tail] = JSON.parse(getRequired(`${queue}`));
|
|
289
|
+
for (let i = head; i < tail; i += 1) {
|
|
290
|
+
yield JSON.parse(getRequired(`${queue}.${i}`));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
215
294
|
// we use different starting index values for the various vNN/koNN/kdNN/kpNN
|
|
216
295
|
// slots, to reduce confusing overlap when looking at debug messages (e.g.
|
|
217
296
|
// seeing both kp1 and ko1, which are completely unrelated despite having the
|
|
@@ -389,14 +468,7 @@ export default function makeKernelKeeper(
|
|
|
389
468
|
return tail - head;
|
|
390
469
|
}
|
|
391
470
|
|
|
392
|
-
|
|
393
|
-
const [head, tail] = JSON.parse(getRequired(`${queue}`));
|
|
394
|
-
const result = [];
|
|
395
|
-
for (let i = head; i < tail; i += 1) {
|
|
396
|
-
result.push(JSON.parse(getRequired(`${queue}.${i}`)));
|
|
397
|
-
}
|
|
398
|
-
return result;
|
|
399
|
-
}
|
|
471
|
+
const dumpQueue = queue => [...readQueue(queue, getRequired)];
|
|
400
472
|
|
|
401
473
|
/**
|
|
402
474
|
* @param {InternalKernelOptions} kernelOptions
|
|
@@ -620,26 +692,9 @@ export default function makeKernelKeeper(
|
|
|
620
692
|
return parseReachableAndVatSlot(kvStore.get(kernelKey));
|
|
621
693
|
}
|
|
622
694
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
return { reachable: 0, recognizable: 0 };
|
|
627
|
-
}
|
|
628
|
-
const [reachable, recognizable] = commaSplit(data).map(Number);
|
|
629
|
-
reachable <= recognizable ||
|
|
630
|
-
Fail`refmismatch(get) ${kernelSlot} ${reachable},${recognizable}`;
|
|
631
|
-
return { reachable, recognizable };
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
function setObjectRefCount(kernelSlot, { reachable, recognizable }) {
|
|
635
|
-
assert.typeof(reachable, 'number');
|
|
636
|
-
assert.typeof(recognizable, 'number');
|
|
637
|
-
(reachable >= 0 && recognizable >= 0) ||
|
|
638
|
-
Fail`${kernelSlot} underflow ${reachable},${recognizable}`;
|
|
639
|
-
reachable <= recognizable ||
|
|
640
|
-
Fail`refmismatch(set) ${kernelSlot} ${reachable},${recognizable}`;
|
|
641
|
-
kvStore.set(`${kernelSlot}.refCount`, `${reachable},${recognizable}`);
|
|
642
|
-
}
|
|
695
|
+
const getObjectRefCount = kref => getObjectReferenceCount(kvStore, kref);
|
|
696
|
+
const setObjectRefCount = (kref, counts) =>
|
|
697
|
+
setObjectReferenceCount(kvStore, kref, counts);
|
|
643
698
|
|
|
644
699
|
/**
|
|
645
700
|
* Iterate over non-durable objects exported by a vat.
|
|
@@ -791,45 +846,41 @@ export default function makeKernelKeeper(
|
|
|
791
846
|
return kpid;
|
|
792
847
|
}
|
|
793
848
|
|
|
849
|
+
/**
|
|
850
|
+
* @param {string} kernelSlot
|
|
851
|
+
* @returns {PromiseRecord}
|
|
852
|
+
*/
|
|
794
853
|
function getKernelPromise(kernelSlot) {
|
|
795
854
|
insistKernelType('promise', kernelSlot);
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
throw Fail`unknown kernelPromise '${kernelSlot}'`;
|
|
800
|
-
}
|
|
855
|
+
const state = getRequired(`${kernelSlot}.state`);
|
|
856
|
+
const refCount = Number(kvStore.get(`${kernelSlot}.refCount`));
|
|
857
|
+
switch (state) {
|
|
801
858
|
case 'unresolved': {
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
// @ts-expect-error get() may fail
|
|
809
|
-
p.subscribers = commaSplit(kvStore.get(`${kernelSlot}.subscribers`));
|
|
810
|
-
p.queue = Array.from(
|
|
859
|
+
const decider = kvStore.get(`${kernelSlot}.decider`) || undefined;
|
|
860
|
+
const policy = kvStore.get(`${kernelSlot}.policy`) || 'ignore';
|
|
861
|
+
const subscribers = commaSplit(
|
|
862
|
+
kvStore.get(`${kernelSlot}.subscribers`) || '',
|
|
863
|
+
);
|
|
864
|
+
const queue = Array.from(
|
|
811
865
|
getPrefixedValues(kvStore, `${kernelSlot}.queue.`),
|
|
812
866
|
).map(s => JSON.parse(s));
|
|
813
|
-
|
|
867
|
+
return harden({ state, refCount, decider, policy, subscribers, queue });
|
|
814
868
|
}
|
|
815
869
|
case 'fulfilled':
|
|
816
870
|
case 'rejected': {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
// @ts-expect-error get() may fail
|
|
821
|
-
slots: commaSplit(kvStore.get(`${kernelSlot}.data.slots`)),
|
|
871
|
+
const data = {
|
|
872
|
+
body: getRequired(`${kernelSlot}.data.body`),
|
|
873
|
+
slots: commaSplit(getRequired(`${kernelSlot}.data.slots`)),
|
|
822
874
|
};
|
|
823
|
-
for (const s of
|
|
875
|
+
for (const s of data.slots) {
|
|
824
876
|
parseKernelSlot(s);
|
|
825
877
|
}
|
|
826
|
-
|
|
878
|
+
return harden({ state, refCount, data });
|
|
827
879
|
}
|
|
828
880
|
default: {
|
|
829
|
-
throw Fail`unknown state for ${kernelSlot}: ${
|
|
881
|
+
throw Fail`unknown state for ${kernelSlot}: ${state}`;
|
|
830
882
|
}
|
|
831
883
|
}
|
|
832
|
-
return harden(p);
|
|
833
884
|
}
|
|
834
885
|
|
|
835
886
|
function getResolveablePromise(kpid, expectedDecider) {
|
|
@@ -838,7 +889,9 @@ export default function makeKernelKeeper(
|
|
|
838
889
|
insistVatID(expectedDecider);
|
|
839
890
|
}
|
|
840
891
|
const p = getKernelPromise(kpid);
|
|
841
|
-
p.state
|
|
892
|
+
if (p.state !== 'unresolved') {
|
|
893
|
+
throw Fail`${kpid} was already resolved`;
|
|
894
|
+
}
|
|
842
895
|
if (expectedDecider) {
|
|
843
896
|
p.decider === expectedDecider ||
|
|
844
897
|
Fail`${kpid} is decided by ${p.decider}, not ${expectedDecider}`;
|
|
@@ -897,6 +950,7 @@ export default function makeKernelKeeper(
|
|
|
897
950
|
// up the resolution *now* and set the correct target early. Doing that
|
|
898
951
|
// might make it easier to remove the Promise Table entry earlier.
|
|
899
952
|
const p = getKernelPromise(kernelSlot);
|
|
953
|
+
assert.equal(p.state, 'unresolved');
|
|
900
954
|
for (const msg of p.queue) {
|
|
901
955
|
const entry = harden({ type: 'send', target: kernelSlot, msg });
|
|
902
956
|
enqueue('acceptanceQueue', entry);
|
|
@@ -967,6 +1021,7 @@ export default function makeKernelKeeper(
|
|
|
967
1021
|
const work = {
|
|
968
1022
|
exports: 0,
|
|
969
1023
|
imports: 0,
|
|
1024
|
+
promises: 0,
|
|
970
1025
|
kv: 0,
|
|
971
1026
|
snapshots: 0,
|
|
972
1027
|
transcripts: 0,
|
|
@@ -991,6 +1046,7 @@ export default function makeKernelKeeper(
|
|
|
991
1046
|
const clistPrefix = `${vatID}.c.`;
|
|
992
1047
|
const exportPrefix = `${clistPrefix}o+`;
|
|
993
1048
|
const importPrefix = `${clistPrefix}o-`;
|
|
1049
|
+
const promisePrefix = `${clistPrefix}p`;
|
|
994
1050
|
|
|
995
1051
|
// Note: ASCII order is "+,-./", and we rely upon this to split the
|
|
996
1052
|
// keyspace into the various o+NN/o-NN/etc spaces. If we were using a
|
|
@@ -1043,8 +1099,22 @@ export default function makeKernelKeeper(
|
|
|
1043
1099
|
}
|
|
1044
1100
|
}
|
|
1045
1101
|
|
|
1046
|
-
//
|
|
1047
|
-
// so they already
|
|
1102
|
+
// The caller used enumeratePromisesByDecider() before calling us,
|
|
1103
|
+
// so they have already rejected the orphan promises, but those
|
|
1104
|
+
// kpids are still present in the dead vat's c-list. Clean those
|
|
1105
|
+
// up now.
|
|
1106
|
+
remaining = budget.promises ?? budget.default;
|
|
1107
|
+
for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
|
|
1108
|
+
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1109
|
+
const vref = stripPrefix(clistPrefix, k);
|
|
1110
|
+
vatKeeper.deleteCListEntry(kref, vref);
|
|
1111
|
+
// that will also delete both db keys
|
|
1112
|
+
work.promises += 1;
|
|
1113
|
+
remaining -= 1;
|
|
1114
|
+
if (remaining <= 0) {
|
|
1115
|
+
return { done: false, work };
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1048
1118
|
|
|
1049
1119
|
// now loop back through everything and delete it all
|
|
1050
1120
|
remaining = budget.kv ?? budget.default;
|
|
@@ -1142,18 +1212,22 @@ export default function makeKernelKeeper(
|
|
|
1142
1212
|
function setDecider(kpid, decider) {
|
|
1143
1213
|
insistVatID(decider);
|
|
1144
1214
|
const p = getKernelPromise(kpid);
|
|
1145
|
-
p.state
|
|
1146
|
-
!p.decider
|
|
1215
|
+
assert.equal(p.state, 'unresolved', `${kpid} was already resolved`);
|
|
1216
|
+
assert(!p.decider, `${kpid} has decider ${p.decider}, not empty`);
|
|
1147
1217
|
kvStore.set(`${kpid}.decider`, decider);
|
|
1148
1218
|
}
|
|
1149
1219
|
|
|
1150
1220
|
function clearDecider(kpid) {
|
|
1151
1221
|
const p = getKernelPromise(kpid);
|
|
1152
|
-
p.state
|
|
1153
|
-
p.decider
|
|
1222
|
+
assert.equal(p.state, 'unresolved', `${kpid} was already resolved`);
|
|
1223
|
+
assert(p.decider, `${kpid} does not have a decider`);
|
|
1154
1224
|
kvStore.set(`${kpid}.decider`, '');
|
|
1155
1225
|
}
|
|
1156
1226
|
|
|
1227
|
+
/**
|
|
1228
|
+
* @param {string} vatID
|
|
1229
|
+
* @returns {IterableIterator<[kpid: string, p: PromiseRecord]>}
|
|
1230
|
+
*/
|
|
1157
1231
|
function* enumeratePromisesByDecider(vatID) {
|
|
1158
1232
|
insistVatID(vatID);
|
|
1159
1233
|
const promisePrefix = `${vatID}.c.p`;
|
|
@@ -1167,10 +1241,10 @@ export default function makeKernelKeeper(
|
|
|
1167
1241
|
// whether the vat is the decider or not. If it is, we add the promise
|
|
1168
1242
|
// to the list of promises that must be rejected because the dead vat
|
|
1169
1243
|
// will never be able to act upon them.
|
|
1170
|
-
const kpid =
|
|
1244
|
+
const kpid = getRequired(k);
|
|
1171
1245
|
const p = getKernelPromise(kpid);
|
|
1172
1246
|
if (p.state === 'unresolved' && p.decider === vatID) {
|
|
1173
|
-
yield kpid;
|
|
1247
|
+
yield [kpid, p];
|
|
1174
1248
|
}
|
|
1175
1249
|
}
|
|
1176
1250
|
}
|
|
@@ -1180,6 +1254,7 @@ export default function makeKernelKeeper(
|
|
|
1180
1254
|
insistKernelType('promise', kernelSlot);
|
|
1181
1255
|
insistVatID(vatID);
|
|
1182
1256
|
const p = getKernelPromise(kernelSlot);
|
|
1257
|
+
assert.equal(p.state, 'unresolved');
|
|
1183
1258
|
const s = new Set(p.subscribers);
|
|
1184
1259
|
s.add(vatID);
|
|
1185
1260
|
const v = Array.from(s).sort().join(',');
|
|
@@ -1210,6 +1285,27 @@ export default function makeKernelKeeper(
|
|
|
1210
1285
|
return dequeue('acceptanceQueue');
|
|
1211
1286
|
}
|
|
1212
1287
|
|
|
1288
|
+
function injectQueuedUpgradeEvents() {
|
|
1289
|
+
// refcounts: Any krefs in `upgradeEvents` must have a refcount to
|
|
1290
|
+
// represent the list's hold on those objects. When
|
|
1291
|
+
// upgradeSwingset() creates these events, it must also
|
|
1292
|
+
// incref(kref), otherwise we run the risk of dropping the kref by
|
|
1293
|
+
// the time injectQueuedUpgradeEvents() is called. We're nominally
|
|
1294
|
+
// removing each event from upgradeEvents (decref), then pushing
|
|
1295
|
+
// it onto the run-queue (incref), but since those two cancel each
|
|
1296
|
+
// other out, we don't actually need to modify any reference
|
|
1297
|
+
// counts from within this function. Note that
|
|
1298
|
+
// addToAcceptanceQueue does not increment refcounts, just kernel
|
|
1299
|
+
// queue-length stats.
|
|
1300
|
+
|
|
1301
|
+
const events = JSON.parse(kvStore.get('upgradeEvents') || '[]');
|
|
1302
|
+
kvStore.delete('upgradeEvents');
|
|
1303
|
+
for (const e of events) {
|
|
1304
|
+
assert(e.type, `not an event`);
|
|
1305
|
+
addToAcceptanceQueue(e);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1213
1309
|
function allocateMeter(remaining, threshold) {
|
|
1214
1310
|
if (remaining !== 'unlimited') {
|
|
1215
1311
|
assert.typeof(remaining, 'bigint');
|
|
@@ -1417,40 +1513,8 @@ export default function makeKernelKeeper(
|
|
|
1417
1513
|
maybeFreeKrefs.add(kref);
|
|
1418
1514
|
}
|
|
1419
1515
|
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
*
|
|
1423
|
-
* We track references to promises and objects, but not devices. Promises
|
|
1424
|
-
* have only a "reachable" count, whereas objects track both "reachable"
|
|
1425
|
-
* and "recognizable" counts.
|
|
1426
|
-
*
|
|
1427
|
-
* @param {unknown} kernelSlot The kernel slot whose refcount is to be incremented.
|
|
1428
|
-
* @param {string?} tag Debugging note with rough source of the reference.
|
|
1429
|
-
* @param {{ isExport?: boolean, onlyRecognizable?: boolean }} options
|
|
1430
|
-
* 'isExport' means the reference comes from a clist export, which counts
|
|
1431
|
-
* for promises but not objects. 'onlyRecognizable' means the reference
|
|
1432
|
-
* provides only recognition, not reachability
|
|
1433
|
-
*/
|
|
1434
|
-
function incrementRefCount(kernelSlot, tag, options = {}) {
|
|
1435
|
-
const { isExport = false, onlyRecognizable = false } = options;
|
|
1436
|
-
kernelSlot ||
|
|
1437
|
-
Fail`incrementRefCount called with empty kernelSlot, tag=${tag}`;
|
|
1438
|
-
const { type } = parseKernelSlot(kernelSlot);
|
|
1439
|
-
if (type === 'promise') {
|
|
1440
|
-
const refCount = Nat(BigInt(getRequired(`${kernelSlot}.refCount`))) + 1n;
|
|
1441
|
-
// kdebug(`++ ${kernelSlot} ${tag} ${refCount}`);
|
|
1442
|
-
kvStore.set(`${kernelSlot}.refCount`, `${refCount}`);
|
|
1443
|
-
}
|
|
1444
|
-
if (type === 'object' && !isExport) {
|
|
1445
|
-
let { reachable, recognizable } = getObjectRefCount(kernelSlot);
|
|
1446
|
-
if (!onlyRecognizable) {
|
|
1447
|
-
reachable += 1;
|
|
1448
|
-
}
|
|
1449
|
-
recognizable += 1;
|
|
1450
|
-
// kdebug(`++ ${kernelSlot} ${tag} ${reachable},${recognizable}`);
|
|
1451
|
-
setObjectRefCount(kernelSlot, { reachable, recognizable });
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1516
|
+
const incrementRefCount = (kref, tag, options = {}) =>
|
|
1517
|
+
incrementReferenceCount(getRequired, kvStore, kref, tag, options);
|
|
1454
1518
|
|
|
1455
1519
|
/**
|
|
1456
1520
|
* Decrement the reference count associated with some kernel object.
|
|
@@ -1542,13 +1606,15 @@ export default function makeKernelKeeper(
|
|
|
1542
1606
|
const kp = getKernelPromise(kpid);
|
|
1543
1607
|
if (kp.refCount === 0) {
|
|
1544
1608
|
let idx = 0;
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1609
|
+
if (kp.state === 'fulfilled' || kp.state === 'rejected') {
|
|
1610
|
+
// #9889 don't assume promise is settled
|
|
1611
|
+
for (const slot of kp.data.slots) {
|
|
1612
|
+
// Note: the following decrement can result in an addition to the
|
|
1613
|
+
// maybeFreeKrefs set, which we are in the midst of iterating.
|
|
1614
|
+
// TC39 went to a lot of trouble to ensure that this is kosher.
|
|
1615
|
+
decrementRefCount(slot, `gc|${kpid}|s${idx}`);
|
|
1616
|
+
idx += 1;
|
|
1617
|
+
}
|
|
1552
1618
|
}
|
|
1553
1619
|
deleteKernelPromise(kpid);
|
|
1554
1620
|
}
|
|
@@ -1939,6 +2005,8 @@ export default function makeKernelKeeper(
|
|
|
1939
2005
|
getAcceptanceQueueLength,
|
|
1940
2006
|
getNextAcceptanceQueueMsg,
|
|
1941
2007
|
|
|
2008
|
+
injectQueuedUpgradeEvents,
|
|
2009
|
+
|
|
1942
2010
|
allocateMeter,
|
|
1943
2011
|
addMeterRemaining,
|
|
1944
2012
|
setMeterThreshold,
|
|
@@ -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
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,
|