@agoric/swingset-vat 0.33.0-u17.1 → 0.33.0-u18.1
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 +10 -10
- package/src/controller/initializeKernel.js +0 -2
- package/src/controller/initializeSwingset.js +72 -63
- package/src/controller/upgradeSwingset.js +179 -36
- package/src/devices/bridge/device-bridge.js +3 -2
- package/src/devices/lib/deviceTools.js +0 -1
- package/src/devices/mailbox/mailbox.js +76 -43
- package/src/index.js +3 -0
- package/src/kernel/deviceTranslator.js +1 -1
- package/src/kernel/gc-actions.js +2 -3
- package/src/kernel/kernel.js +59 -21
- package/src/kernel/state/kernelKeeper.js +230 -128
- package/src/kernel/state/vatKeeper.js +74 -38
- package/src/kernel/vat-warehouse.js +22 -16
- package/src/kernel/vatTranslator.js +7 -3
- package/src/supervisors/subprocess-node/supervisor-subprocess-node.js +1 -0
- package/src/typeGuards.js +3 -2
- package/src/types-external.js +9 -1
- package/src/types-internal.js +11 -0
- package/src/vats/comms/delivery.js +0 -2
- package/src/vats/comms/state.js +0 -4
- package/src/vats/timer/vat-timer.js +0 -2
- package/src/vats/vat-admin/vat-vat-admin.js +0 -4
- package/tools/baggage-check.js +0 -2
- package/tools/bootstrap-dvo-test.js +0 -1
- package/tools/bootstrap-relay.js +9 -0
- package/tools/prepare-strict-test-env-ava.js +19 -0
- package/tools/run-utils.js +11 -4
- package/tools/vat-puppet.js +111 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-use-before-define */
|
|
2
1
|
import { Nat, isNat } from '@endo/nat';
|
|
3
2
|
import { assert, Fail } from '@endo/errors';
|
|
4
3
|
import {
|
|
@@ -43,16 +42,19 @@ const enableKernelGC = true;
|
|
|
43
42
|
* @typedef { import('../../types-external.js').SnapStore } SnapStore
|
|
44
43
|
* @typedef { import('../../types-external.js').TranscriptStore } TranscriptStore
|
|
45
44
|
* @typedef { import('../../types-external.js').VatKeeper } VatKeeper
|
|
45
|
+
* @typedef { Pick<VatKeeper, 'deleteCListEntry' | 'deleteSnapshots' | 'deleteTranscripts'> } VatUndertaker
|
|
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
|
|
@@ -340,6 +422,8 @@ export default function makeKernelKeeper(
|
|
|
340
422
|
const ephemeral = harden({
|
|
341
423
|
/** @type { Map<string, VatKeeper> } */
|
|
342
424
|
vatKeepers: new Map(),
|
|
425
|
+
/** @type { Map<string, VatUndertaker> } */
|
|
426
|
+
vatUndertakers: new Map(),
|
|
343
427
|
deviceKeepers: new Map(), // deviceID -> deviceKeeper
|
|
344
428
|
});
|
|
345
429
|
|
|
@@ -388,14 +472,7 @@ export default function makeKernelKeeper(
|
|
|
388
472
|
return tail - head;
|
|
389
473
|
}
|
|
390
474
|
|
|
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
|
-
}
|
|
475
|
+
const dumpQueue = queue => [...readQueue(queue, getRequired)];
|
|
399
476
|
|
|
400
477
|
/**
|
|
401
478
|
* @param {InternalKernelOptions} kernelOptions
|
|
@@ -619,26 +696,9 @@ export default function makeKernelKeeper(
|
|
|
619
696
|
return parseReachableAndVatSlot(kvStore.get(kernelKey));
|
|
620
697
|
}
|
|
621
698
|
|
|
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
|
-
}
|
|
699
|
+
const getObjectRefCount = kref => getObjectReferenceCount(kvStore, kref);
|
|
700
|
+
const setObjectRefCount = (kref, counts) =>
|
|
701
|
+
setObjectReferenceCount(kvStore, kref, counts);
|
|
642
702
|
|
|
643
703
|
/**
|
|
644
704
|
* Iterate over non-durable objects exported by a vat.
|
|
@@ -790,43 +850,41 @@ export default function makeKernelKeeper(
|
|
|
790
850
|
return kpid;
|
|
791
851
|
}
|
|
792
852
|
|
|
853
|
+
/**
|
|
854
|
+
* @param {string} kernelSlot
|
|
855
|
+
* @returns {PromiseRecord}
|
|
856
|
+
*/
|
|
793
857
|
function getKernelPromise(kernelSlot) {
|
|
794
858
|
insistKernelType('promise', kernelSlot);
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
throw Fail`unknown kernelPromise '${kernelSlot}'`;
|
|
799
|
-
}
|
|
859
|
+
const state = getRequired(`${kernelSlot}.state`);
|
|
860
|
+
const refCount = Number(kvStore.get(`${kernelSlot}.refCount`));
|
|
861
|
+
switch (state) {
|
|
800
862
|
case 'unresolved': {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
p.subscribers = commaSplit(kvStore.get(`${kernelSlot}.subscribers`));
|
|
808
|
-
p.queue = Array.from(
|
|
863
|
+
const decider = kvStore.get(`${kernelSlot}.decider`) || undefined;
|
|
864
|
+
const policy = kvStore.get(`${kernelSlot}.policy`) || 'ignore';
|
|
865
|
+
const subscribers = commaSplit(
|
|
866
|
+
kvStore.get(`${kernelSlot}.subscribers`) || '',
|
|
867
|
+
);
|
|
868
|
+
const queue = Array.from(
|
|
809
869
|
getPrefixedValues(kvStore, `${kernelSlot}.queue.`),
|
|
810
870
|
).map(s => JSON.parse(s));
|
|
811
|
-
|
|
871
|
+
return harden({ state, refCount, decider, policy, subscribers, queue });
|
|
812
872
|
}
|
|
813
873
|
case 'fulfilled':
|
|
814
874
|
case 'rejected': {
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
slots: commaSplit(kvStore.get(`${kernelSlot}.data.slots`)),
|
|
875
|
+
const data = {
|
|
876
|
+
body: getRequired(`${kernelSlot}.data.body`),
|
|
877
|
+
slots: commaSplit(getRequired(`${kernelSlot}.data.slots`)),
|
|
819
878
|
};
|
|
820
|
-
for (const s of
|
|
879
|
+
for (const s of data.slots) {
|
|
821
880
|
parseKernelSlot(s);
|
|
822
881
|
}
|
|
823
|
-
|
|
882
|
+
return harden({ state, refCount, data });
|
|
824
883
|
}
|
|
825
884
|
default: {
|
|
826
|
-
throw Fail`unknown state for ${kernelSlot}: ${
|
|
885
|
+
throw Fail`unknown state for ${kernelSlot}: ${state}`;
|
|
827
886
|
}
|
|
828
887
|
}
|
|
829
|
-
return harden(p);
|
|
830
888
|
}
|
|
831
889
|
|
|
832
890
|
function getResolveablePromise(kpid, expectedDecider) {
|
|
@@ -835,7 +893,9 @@ export default function makeKernelKeeper(
|
|
|
835
893
|
insistVatID(expectedDecider);
|
|
836
894
|
}
|
|
837
895
|
const p = getKernelPromise(kpid);
|
|
838
|
-
p.state
|
|
896
|
+
if (p.state !== 'unresolved') {
|
|
897
|
+
throw Fail`${kpid} was already resolved`;
|
|
898
|
+
}
|
|
839
899
|
if (expectedDecider) {
|
|
840
900
|
p.decider === expectedDecider ||
|
|
841
901
|
Fail`${kpid} is decided by ${p.decider}, not ${expectedDecider}`;
|
|
@@ -894,6 +954,7 @@ export default function makeKernelKeeper(
|
|
|
894
954
|
// up the resolution *now* and set the correct target early. Doing that
|
|
895
955
|
// might make it easier to remove the Promise Table entry earlier.
|
|
896
956
|
const p = getKernelPromise(kernelSlot);
|
|
957
|
+
assert.equal(p.state, 'unresolved');
|
|
897
958
|
for (const msg of p.queue) {
|
|
898
959
|
const entry = harden({ type: 'send', target: kernelSlot, msg });
|
|
899
960
|
enqueue('acceptanceQueue', entry);
|
|
@@ -964,6 +1025,7 @@ export default function makeKernelKeeper(
|
|
|
964
1025
|
const work = {
|
|
965
1026
|
exports: 0,
|
|
966
1027
|
imports: 0,
|
|
1028
|
+
promises: 0,
|
|
967
1029
|
kv: 0,
|
|
968
1030
|
snapshots: 0,
|
|
969
1031
|
transcripts: 0,
|
|
@@ -984,10 +1046,11 @@ export default function makeKernelKeeper(
|
|
|
984
1046
|
// first or vref first), and delete the other one in the same
|
|
985
1047
|
// call, so we don't wind up with half an entry.
|
|
986
1048
|
|
|
987
|
-
const
|
|
1049
|
+
const undertaker = provideVatUndertaker(vatID);
|
|
988
1050
|
const clistPrefix = `${vatID}.c.`;
|
|
989
1051
|
const exportPrefix = `${clistPrefix}o+`;
|
|
990
1052
|
const importPrefix = `${clistPrefix}o-`;
|
|
1053
|
+
const promisePrefix = `${clistPrefix}p`;
|
|
991
1054
|
|
|
992
1055
|
// Note: ASCII order is "+,-./", and we rely upon this to split the
|
|
993
1056
|
// keyspace into the various o+NN/o-NN/etc spaces. If we were using a
|
|
@@ -1031,7 +1094,7 @@ export default function makeKernelKeeper(
|
|
|
1031
1094
|
// drop+retire
|
|
1032
1095
|
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1033
1096
|
const vref = stripPrefix(clistPrefix, k);
|
|
1034
|
-
|
|
1097
|
+
undertaker.deleteCListEntry(kref, vref);
|
|
1035
1098
|
// that will also delete both db keys
|
|
1036
1099
|
work.imports += 1;
|
|
1037
1100
|
remaining -= 1;
|
|
@@ -1040,8 +1103,22 @@ export default function makeKernelKeeper(
|
|
|
1040
1103
|
}
|
|
1041
1104
|
}
|
|
1042
1105
|
|
|
1043
|
-
//
|
|
1044
|
-
// so they already
|
|
1106
|
+
// The caller used enumeratePromisesByDecider() before calling us,
|
|
1107
|
+
// so they have already rejected the orphan promises, but those
|
|
1108
|
+
// kpids are still present in the dead vat's c-list. Clean those
|
|
1109
|
+
// up now.
|
|
1110
|
+
remaining = budget.promises ?? budget.default;
|
|
1111
|
+
for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
|
|
1112
|
+
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1113
|
+
const vref = stripPrefix(clistPrefix, k);
|
|
1114
|
+
undertaker.deleteCListEntry(kref, vref);
|
|
1115
|
+
// that will also delete both db keys
|
|
1116
|
+
work.promises += 1;
|
|
1117
|
+
remaining -= 1;
|
|
1118
|
+
if (remaining <= 0) {
|
|
1119
|
+
return { done: false, work };
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1045
1122
|
|
|
1046
1123
|
// now loop back through everything and delete it all
|
|
1047
1124
|
remaining = budget.kv ?? budget.default;
|
|
@@ -1056,7 +1133,7 @@ export default function makeKernelKeeper(
|
|
|
1056
1133
|
|
|
1057
1134
|
// this will internally loop through 'budget' deletions
|
|
1058
1135
|
remaining = budget.snapshots ?? budget.default;
|
|
1059
|
-
const dsc =
|
|
1136
|
+
const dsc = undertaker.deleteSnapshots(remaining);
|
|
1060
1137
|
work.snapshots += dsc.cleanups;
|
|
1061
1138
|
remaining -= dsc.cleanups;
|
|
1062
1139
|
if (remaining <= 0) {
|
|
@@ -1065,7 +1142,7 @@ export default function makeKernelKeeper(
|
|
|
1065
1142
|
|
|
1066
1143
|
// same
|
|
1067
1144
|
remaining = budget.transcripts ?? budget.default;
|
|
1068
|
-
const dts =
|
|
1145
|
+
const dts = undertaker.deleteTranscripts(remaining);
|
|
1069
1146
|
work.transcripts += dts.cleanups;
|
|
1070
1147
|
remaining -= dts.cleanups;
|
|
1071
1148
|
// last task, so increment cleanups, but dc.done is authoritative
|
|
@@ -1139,18 +1216,22 @@ export default function makeKernelKeeper(
|
|
|
1139
1216
|
function setDecider(kpid, decider) {
|
|
1140
1217
|
insistVatID(decider);
|
|
1141
1218
|
const p = getKernelPromise(kpid);
|
|
1142
|
-
p.state
|
|
1143
|
-
!p.decider
|
|
1219
|
+
assert.equal(p.state, 'unresolved', `${kpid} was already resolved`);
|
|
1220
|
+
assert(!p.decider, `${kpid} has decider ${p.decider}, not empty`);
|
|
1144
1221
|
kvStore.set(`${kpid}.decider`, decider);
|
|
1145
1222
|
}
|
|
1146
1223
|
|
|
1147
1224
|
function clearDecider(kpid) {
|
|
1148
1225
|
const p = getKernelPromise(kpid);
|
|
1149
|
-
p.state
|
|
1150
|
-
p.decider
|
|
1226
|
+
assert.equal(p.state, 'unresolved', `${kpid} was already resolved`);
|
|
1227
|
+
assert(p.decider, `${kpid} does not have a decider`);
|
|
1151
1228
|
kvStore.set(`${kpid}.decider`, '');
|
|
1152
1229
|
}
|
|
1153
1230
|
|
|
1231
|
+
/**
|
|
1232
|
+
* @param {string} vatID
|
|
1233
|
+
* @returns {IterableIterator<[kpid: string, p: PromiseRecord]>}
|
|
1234
|
+
*/
|
|
1154
1235
|
function* enumeratePromisesByDecider(vatID) {
|
|
1155
1236
|
insistVatID(vatID);
|
|
1156
1237
|
const promisePrefix = `${vatID}.c.p`;
|
|
@@ -1164,10 +1245,10 @@ export default function makeKernelKeeper(
|
|
|
1164
1245
|
// whether the vat is the decider or not. If it is, we add the promise
|
|
1165
1246
|
// to the list of promises that must be rejected because the dead vat
|
|
1166
1247
|
// will never be able to act upon them.
|
|
1167
|
-
const kpid =
|
|
1248
|
+
const kpid = getRequired(k);
|
|
1168
1249
|
const p = getKernelPromise(kpid);
|
|
1169
1250
|
if (p.state === 'unresolved' && p.decider === vatID) {
|
|
1170
|
-
yield kpid;
|
|
1251
|
+
yield [kpid, p];
|
|
1171
1252
|
}
|
|
1172
1253
|
}
|
|
1173
1254
|
}
|
|
@@ -1177,6 +1258,7 @@ export default function makeKernelKeeper(
|
|
|
1177
1258
|
insistKernelType('promise', kernelSlot);
|
|
1178
1259
|
insistVatID(vatID);
|
|
1179
1260
|
const p = getKernelPromise(kernelSlot);
|
|
1261
|
+
assert.equal(p.state, 'unresolved');
|
|
1180
1262
|
const s = new Set(p.subscribers);
|
|
1181
1263
|
s.add(vatID);
|
|
1182
1264
|
const v = Array.from(s).sort().join(',');
|
|
@@ -1207,6 +1289,27 @@ export default function makeKernelKeeper(
|
|
|
1207
1289
|
return dequeue('acceptanceQueue');
|
|
1208
1290
|
}
|
|
1209
1291
|
|
|
1292
|
+
function injectQueuedUpgradeEvents() {
|
|
1293
|
+
// refcounts: Any krefs in `upgradeEvents` must have a refcount to
|
|
1294
|
+
// represent the list's hold on those objects. When
|
|
1295
|
+
// upgradeSwingset() creates these events, it must also
|
|
1296
|
+
// incref(kref), otherwise we run the risk of dropping the kref by
|
|
1297
|
+
// the time injectQueuedUpgradeEvents() is called. We're nominally
|
|
1298
|
+
// removing each event from upgradeEvents (decref), then pushing
|
|
1299
|
+
// it onto the run-queue (incref), but since those two cancel each
|
|
1300
|
+
// other out, we don't actually need to modify any reference
|
|
1301
|
+
// counts from within this function. Note that
|
|
1302
|
+
// addToAcceptanceQueue does not increment refcounts, just kernel
|
|
1303
|
+
// queue-length stats.
|
|
1304
|
+
|
|
1305
|
+
const events = JSON.parse(kvStore.get('upgradeEvents') || '[]');
|
|
1306
|
+
kvStore.delete('upgradeEvents');
|
|
1307
|
+
for (const e of events) {
|
|
1308
|
+
assert(e.type, `not an event`);
|
|
1309
|
+
addToAcceptanceQueue(e);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1210
1313
|
function allocateMeter(remaining, threshold) {
|
|
1211
1314
|
if (remaining !== 'unlimited') {
|
|
1212
1315
|
assert.typeof(remaining, 'bigint');
|
|
@@ -1414,40 +1517,8 @@ export default function makeKernelKeeper(
|
|
|
1414
1517
|
maybeFreeKrefs.add(kref);
|
|
1415
1518
|
}
|
|
1416
1519
|
|
|
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
|
-
}
|
|
1520
|
+
const incrementRefCount = (kref, tag, options = {}) =>
|
|
1521
|
+
incrementReferenceCount(getRequired, kvStore, kref, tag, options);
|
|
1451
1522
|
|
|
1452
1523
|
/**
|
|
1453
1524
|
* Decrement the reference count associated with some kernel object.
|
|
@@ -1539,13 +1610,15 @@ export default function makeKernelKeeper(
|
|
|
1539
1610
|
const kp = getKernelPromise(kpid);
|
|
1540
1611
|
if (kp.refCount === 0) {
|
|
1541
1612
|
let idx = 0;
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1613
|
+
if (kp.state === 'fulfilled' || kp.state === 'rejected') {
|
|
1614
|
+
// #9889 don't assume promise is settled
|
|
1615
|
+
for (const slot of kp.data.slots) {
|
|
1616
|
+
// Note: the following decrement can result in an addition to the
|
|
1617
|
+
// maybeFreeKrefs set, which we are in the midst of iterating.
|
|
1618
|
+
// TC39 went to a lot of trouble to ensure that this is kosher.
|
|
1619
|
+
decrementRefCount(slot, `gc|${kpid}|s${idx}`);
|
|
1620
|
+
idx += 1;
|
|
1621
|
+
}
|
|
1549
1622
|
}
|
|
1550
1623
|
deleteKernelPromise(kpid);
|
|
1551
1624
|
}
|
|
@@ -1626,6 +1699,26 @@ export default function makeKernelKeeper(
|
|
|
1626
1699
|
initializeVatState(kvStore, transcriptStore, vatID, source, options);
|
|
1627
1700
|
}
|
|
1628
1701
|
|
|
1702
|
+
/** @type {import('./vatKeeper.js').VatKeeperPowers} */
|
|
1703
|
+
const vatKeeperPowers = {
|
|
1704
|
+
transcriptStore,
|
|
1705
|
+
kernelSlog,
|
|
1706
|
+
addKernelObject,
|
|
1707
|
+
addKernelPromiseForVat,
|
|
1708
|
+
kernelObjectExists,
|
|
1709
|
+
incrementRefCount,
|
|
1710
|
+
decrementRefCount,
|
|
1711
|
+
getObjectRefCount,
|
|
1712
|
+
setObjectRefCount,
|
|
1713
|
+
getReachableAndVatSlot,
|
|
1714
|
+
addMaybeFreeKref,
|
|
1715
|
+
incStat,
|
|
1716
|
+
decStat,
|
|
1717
|
+
getCrankNumber,
|
|
1718
|
+
scheduleReap,
|
|
1719
|
+
snapStore,
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1629
1722
|
function provideVatKeeper(vatID) {
|
|
1630
1723
|
insistVatID(vatID);
|
|
1631
1724
|
const found = ephemeral.vatKeepers.get(vatID);
|
|
@@ -1633,30 +1726,36 @@ export default function makeKernelKeeper(
|
|
|
1633
1726
|
return found;
|
|
1634
1727
|
}
|
|
1635
1728
|
assert(kvStore.has(`${vatID}.o.nextID`), `${vatID} was not initialized`);
|
|
1636
|
-
const vk = makeVatKeeper(
|
|
1637
|
-
kvStore,
|
|
1638
|
-
transcriptStore,
|
|
1639
|
-
kernelSlog,
|
|
1640
|
-
vatID,
|
|
1641
|
-
addKernelObject,
|
|
1642
|
-
addKernelPromiseForVat,
|
|
1643
|
-
kernelObjectExists,
|
|
1644
|
-
incrementRefCount,
|
|
1645
|
-
decrementRefCount,
|
|
1646
|
-
getObjectRefCount,
|
|
1647
|
-
setObjectRefCount,
|
|
1648
|
-
getReachableAndVatSlot,
|
|
1649
|
-
addMaybeFreeKref,
|
|
1650
|
-
incStat,
|
|
1651
|
-
decStat,
|
|
1652
|
-
getCrankNumber,
|
|
1653
|
-
scheduleReap,
|
|
1654
|
-
snapStore,
|
|
1655
|
-
);
|
|
1729
|
+
const vk = makeVatKeeper(vatID, kvStore, vatKeeperPowers);
|
|
1656
1730
|
ephemeral.vatKeepers.set(vatID, vk);
|
|
1657
1731
|
return vk;
|
|
1658
1732
|
}
|
|
1659
1733
|
|
|
1734
|
+
/**
|
|
1735
|
+
* Produce an attenuated vatKeeper for slow vat termination (and that
|
|
1736
|
+
* therefore does not insist on liveness, unlike provideVatKeeper).
|
|
1737
|
+
*
|
|
1738
|
+
* @param {string} vatID
|
|
1739
|
+
*/
|
|
1740
|
+
function provideVatUndertaker(vatID) {
|
|
1741
|
+
insistVatID(vatID);
|
|
1742
|
+
const found = ephemeral.vatUndertakers.get(vatID);
|
|
1743
|
+
if (found !== undefined) {
|
|
1744
|
+
return found;
|
|
1745
|
+
}
|
|
1746
|
+
const { deleteCListEntry, deleteSnapshots, deleteTranscripts } =
|
|
1747
|
+
ephemeral.vatKeepers.get(vatID) ||
|
|
1748
|
+
makeVatKeeper(vatID, kvStore, vatKeeperPowers);
|
|
1749
|
+
/** @type {VatUndertaker} */
|
|
1750
|
+
const undertaker = harden({
|
|
1751
|
+
deleteCListEntry,
|
|
1752
|
+
deleteSnapshots,
|
|
1753
|
+
deleteTranscripts,
|
|
1754
|
+
});
|
|
1755
|
+
ephemeral.vatUndertakers.set(vatID, undertaker);
|
|
1756
|
+
return undertaker;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1660
1759
|
function vatIsAlive(vatID) {
|
|
1661
1760
|
insistVatID(vatID);
|
|
1662
1761
|
return kvStore.has(`${vatID}.o.nextID`) && !terminatedVats.includes(vatID);
|
|
@@ -1936,6 +2035,8 @@ export default function makeKernelKeeper(
|
|
|
1936
2035
|
getAcceptanceQueueLength,
|
|
1937
2036
|
getNextAcceptanceQueueMsg,
|
|
1938
2037
|
|
|
2038
|
+
injectQueuedUpgradeEvents,
|
|
2039
|
+
|
|
1939
2040
|
allocateMeter,
|
|
1940
2041
|
addMeterRemaining,
|
|
1941
2042
|
setMeterThreshold,
|
|
@@ -1981,3 +2082,4 @@ export default function makeKernelKeeper(
|
|
|
1981
2082
|
dump,
|
|
1982
2083
|
});
|
|
1983
2084
|
}
|
|
2085
|
+
/** @typedef {ReturnType<typeof makeKernelKeeper>} KernelKeeper */
|