@agoric/swingset-vat 0.33.0-u16.0 → 0.33.0-u17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +31 -30
- package/src/controller/controller.js +48 -1
- package/src/controller/initializeKernel.js +35 -10
- package/src/controller/initializeSwingset.js +6 -3
- package/src/controller/startXSnap.js +1 -1
- package/src/controller/upgradeSwingset.js +212 -0
- package/src/devices/bridge/device-bridge.js +1 -1
- package/src/devices/bundle/device-bundle.js +1 -1
- package/src/devices/command/command.js +1 -2
- package/src/devices/command/device-command.js +1 -2
- package/src/devices/lib/deviceTools.js +1 -1
- package/src/devices/loopbox/device-loopbox.js +1 -1
- package/src/devices/loopbox/loopbox.js +1 -1
- package/src/devices/mailbox/device-mailbox.js +1 -2
- package/src/devices/mailbox/mailbox.js +1 -2
- package/src/devices/plugin/device-plugin.js +1 -1
- package/src/devices/timer/device-timer.js +1 -1
- package/src/devices/timer/timer.js +1 -1
- package/src/devices/vat-admin/device-vat-admin.js +4 -2
- package/src/index.js +1 -1
- package/src/kernel/deviceManager.js +1 -1
- package/src/kernel/deviceSlots.js +1 -1
- package/src/kernel/deviceTranslator.js +1 -1
- package/src/kernel/dummyMeterControl.js +1 -1
- package/src/kernel/gc-actions.js +55 -34
- package/src/kernel/kernel.js +216 -51
- package/src/kernel/kernelSyscall.js +2 -13
- package/src/kernel/parseKernelSlots.js +1 -1
- package/src/kernel/slogger.js +2 -2
- package/src/kernel/state/deviceKeeper.js +1 -1
- package/src/kernel/state/kernelKeeper.js +427 -81
- package/src/kernel/state/reachable.js +1 -1
- package/src/kernel/state/stats.js +1 -1
- package/src/kernel/state/storageHelper.js +1 -1
- package/src/kernel/state/vatKeeper.js +159 -44
- package/src/kernel/vat-admin-hooks.js +4 -1
- package/src/kernel/vat-loader/manager-factory.js +1 -2
- package/src/kernel/vat-loader/manager-helper.js +1 -1
- package/src/kernel/vat-loader/manager-local.js +1 -1
- package/src/kernel/vat-loader/manager-subprocess-node.js +1 -1
- package/src/kernel/vat-loader/manager-subprocess-xsnap.js +1 -1
- package/src/kernel/vat-loader/vat-loader.js +2 -2
- package/src/kernel/vat-warehouse.js +5 -1
- package/src/kernel/vatTranslator.js +1 -4
- package/src/lib/assertOptions.js +1 -1
- package/src/lib/capdata.js +1 -1
- package/src/lib/id.js +1 -1
- package/src/lib/message.js +1 -1
- package/src/lib/parseVatSlots.js +1 -1
- package/src/lib/recordVatOptions.js +18 -6
- package/src/lib/runPolicies.js +50 -4
- package/src/lib/storageAPI.js +1 -1
- package/src/lib/workerOptions.js +1 -1
- package/src/supervisors/subprocess-node/supervisor-subprocess-node.js +1 -1
- package/src/types-external.js +71 -22
- package/src/types-internal.js +56 -3
- package/src/vats/comms/clist-inbound.js +1 -1
- package/src/vats/comms/clist-kernel.js +1 -1
- package/src/vats/comms/clist-outbound.js +1 -1
- package/src/vats/comms/controller.js +1 -1
- package/src/vats/comms/delivery.js +1 -1
- package/src/vats/comms/dispatch.js +1 -1
- package/src/vats/comms/gc-comms.js +1 -1
- package/src/vats/comms/parseLocalSlots.js +1 -1
- package/src/vats/comms/parseRemoteSlot.js +1 -1
- package/src/vats/comms/remote.js +1 -1
- package/src/vats/comms/state.js +1 -1
- package/src/vats/timer/vat-timer.js +9 -9
- package/src/vats/vat-admin/vat-vat-admin.js +23 -8
- package/src/vats/vattp/vat-vattp.js +1 -1
- package/tools/bootstrap-relay.js +1 -3
- package/tools/bundleTool.js +9 -1
- package/tools/dvo-test-harness.js +1 -1
- package/tools/manual-timer.js +1 -1
- package/tools/run-utils.js +1 -1
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
1
2
|
import { Nat, isNat } from '@endo/nat';
|
|
2
|
-
import { assert, Fail } from '@
|
|
3
|
-
import {
|
|
3
|
+
import { assert, Fail } from '@endo/errors';
|
|
4
|
+
import {
|
|
5
|
+
initializeVatState,
|
|
6
|
+
makeVatKeeper,
|
|
7
|
+
DEFAULT_REAP_DIRT_THRESHOLD_KEY,
|
|
8
|
+
} from './vatKeeper.js';
|
|
4
9
|
import { initializeDeviceState, makeDeviceKeeper } from './deviceKeeper.js';
|
|
5
10
|
import { parseReachableAndVatSlot } from './reachable.js';
|
|
6
11
|
import { insistStorageAPI } from '../../lib/storageAPI.js';
|
|
@@ -33,14 +38,22 @@ const enableKernelGC = true;
|
|
|
33
38
|
* @typedef { import('../../types-external.js').BundleCap } BundleCap
|
|
34
39
|
* @typedef { import('../../types-external.js').BundleID } BundleID
|
|
35
40
|
* @typedef { import('../../types-external.js').EndoZipBase64Bundle } EndoZipBase64Bundle
|
|
36
|
-
* @typedef { import('../../types-external.js').KernelOptions } KernelOptions
|
|
37
41
|
* @typedef { import('../../types-external.js').KernelSlog } KernelSlog
|
|
38
42
|
* @typedef { import('../../types-external.js').ManagerType } ManagerType
|
|
39
43
|
* @typedef { import('../../types-external.js').SnapStore } SnapStore
|
|
40
44
|
* @typedef { import('../../types-external.js').TranscriptStore } TranscriptStore
|
|
41
45
|
* @typedef { import('../../types-external.js').VatKeeper } VatKeeper
|
|
46
|
+
* @typedef { import('../../types-internal.js').InternalKernelOptions } InternalKernelOptions
|
|
47
|
+
* @typedef { import('../../types-internal.js').ReapDirtThreshold } ReapDirtThreshold
|
|
48
|
+
* @import {CleanupBudget, CleanupWork, PolicyOutputCleanupBudget} from '../../types-external.js';
|
|
49
|
+
* @import {RunQueueEventCleanupTerminatedVat} from '../../types-internal.js';
|
|
42
50
|
*/
|
|
43
51
|
|
|
52
|
+
export { DEFAULT_REAP_DIRT_THRESHOLD_KEY };
|
|
53
|
+
|
|
54
|
+
// most recent DB schema version
|
|
55
|
+
export const CURRENT_SCHEMA_VERSION = 2;
|
|
56
|
+
|
|
44
57
|
// Kernel state lives in a key-value store supporting key retrieval by
|
|
45
58
|
// lexicographic range. All keys and values are strings.
|
|
46
59
|
// We simulate a tree by concatenating path-name components with ".". When we
|
|
@@ -52,13 +65,21 @@ const enableKernelGC = true;
|
|
|
52
65
|
// allowed to vary between instances in a consensus machine. Everything else
|
|
53
66
|
// is required to be deterministic.
|
|
54
67
|
//
|
|
55
|
-
// The schema is:
|
|
56
68
|
//
|
|
69
|
+
// The schema is indicated by the value of the "version" key, which
|
|
70
|
+
// was added for version 1 (i.e., version 0 had no such key), and is
|
|
71
|
+
// only modified by a call to upgradeSwingset(). See below for
|
|
72
|
+
// deltas/upgrades from one version to the next.
|
|
73
|
+
//
|
|
74
|
+
// The current ("v2") schema keys/values are:
|
|
75
|
+
//
|
|
76
|
+
// version = '2'
|
|
57
77
|
// vat.names = JSON([names..])
|
|
58
78
|
// vat.dynamicIDs = JSON([vatIDs..])
|
|
59
79
|
// vat.name.$NAME = $vatID = v$NN
|
|
60
80
|
// vat.nextID = $NN
|
|
61
81
|
// vat.nextUpgradeID = $NN
|
|
82
|
+
// vats.terminated = JSON([vatIDs..])
|
|
62
83
|
// device.names = JSON([names..])
|
|
63
84
|
// device.name.$NAME = $deviceID = d$NN
|
|
64
85
|
// device.nextID = $NN
|
|
@@ -68,13 +89,22 @@ const enableKernelGC = true;
|
|
|
68
89
|
// bundle.$BUNDLEID = JSON(bundle)
|
|
69
90
|
//
|
|
70
91
|
// kernel.defaultManagerType = managerType
|
|
71
|
-
// kernel.defaultReapInterval = $NN
|
|
92
|
+
// (old) kernel.defaultReapInterval = $NN
|
|
93
|
+
// kernel.defaultReapDirtThreshold = JSON({ thresholds })
|
|
94
|
+
// thresholds (all optional)
|
|
95
|
+
// deliveries: number or 'never' (default)
|
|
96
|
+
// gcKrefs: number or 'never' (default)
|
|
97
|
+
// computrons: number or 'never' (default)
|
|
98
|
+
// never: boolean (default false)
|
|
72
99
|
// kernel.relaxDurabilityRules = missing | 'true'
|
|
73
100
|
// kernel.snapshotInitial = $NN
|
|
74
101
|
// kernel.snapshotInterval = $NN
|
|
75
102
|
|
|
76
103
|
// v$NN.source = JSON({ bundle }) or JSON({ bundleName })
|
|
77
|
-
// v$NN.options = JSON
|
|
104
|
+
// v$NN.options = JSON , options include:
|
|
105
|
+
// .reapDirtThreshold = JSON({ thresholds })
|
|
106
|
+
// thresholds (all optional, default to kernel-wide defaultReapDirtThreshold)
|
|
107
|
+
// (leave room for .snapshotDirtThreshold for #6786)
|
|
78
108
|
// v$NN.o.nextID = $NN
|
|
79
109
|
// v$NN.p.nextID = $NN
|
|
80
110
|
// v$NN.d.nextID = $NN
|
|
@@ -83,9 +113,10 @@ const enableKernelGC = true;
|
|
|
83
113
|
// $vatSlot is one of: o+$NN/o-$NN/p+$NN/p-$NN/d+$NN/d-$NN
|
|
84
114
|
// v$NN.c.$vatSlot = $kernelSlot = ko$NN/kp$NN/kd$NN
|
|
85
115
|
// v$NN.vs.$key = string
|
|
86
|
-
// v$NN.
|
|
87
|
-
// v$NN.
|
|
88
|
-
// v$NN.
|
|
116
|
+
// old (v0): v$NN.reapInterval = $NN or 'never'
|
|
117
|
+
// old (v0): v$NN.reapCountdown = $NN or 'never'
|
|
118
|
+
// v$NN.reapDirt = JSON({ deliveries, gcKrefs, computrons }) // missing keys treated as zero
|
|
119
|
+
// (leave room for v$NN.snapshotDirt and options.snapshotDirtThreshold for #6786)
|
|
89
120
|
// exclude from consensus
|
|
90
121
|
// local.*
|
|
91
122
|
|
|
@@ -132,6 +163,21 @@ const enableKernelGC = true;
|
|
|
132
163
|
// Prefix reserved for host written data:
|
|
133
164
|
// host.
|
|
134
165
|
|
|
166
|
+
// Kernel state schema changes:
|
|
167
|
+
//
|
|
168
|
+
// v0: the original
|
|
169
|
+
// * no `version`
|
|
170
|
+
// * uses `initialized = 'true'`
|
|
171
|
+
// v1:
|
|
172
|
+
// * add `version = '1'`
|
|
173
|
+
// * remove `initialized`
|
|
174
|
+
// * replace `kernel.defaultReapInterval` with `kernel.defaultReapDirtThreshold`
|
|
175
|
+
// * replace vat's `vNN.reapInterval`/`vNN.reapCountdown` with `vNN.reapDirt`
|
|
176
|
+
// and a `vNN.reapDirtThreshold` in `vNN.options`
|
|
177
|
+
// v2:
|
|
178
|
+
// * change `version` to `'2'`
|
|
179
|
+
// * add `vats.terminated` with `[]` as initial value
|
|
180
|
+
|
|
135
181
|
export function commaSplit(s) {
|
|
136
182
|
if (s === '') {
|
|
137
183
|
return [];
|
|
@@ -139,12 +185,32 @@ export function commaSplit(s) {
|
|
|
139
185
|
return s.split(',');
|
|
140
186
|
}
|
|
141
187
|
|
|
188
|
+
export function stripPrefix(prefix, str) {
|
|
189
|
+
assert(str.startsWith(prefix), str);
|
|
190
|
+
return str.slice(prefix.length);
|
|
191
|
+
}
|
|
192
|
+
|
|
142
193
|
function insistMeterID(m) {
|
|
143
194
|
assert.typeof(m, 'string');
|
|
144
195
|
assert.equal(m[0], 'm');
|
|
145
196
|
Nat(BigInt(m.slice(1)));
|
|
146
197
|
}
|
|
147
198
|
|
|
199
|
+
export const getAllStaticVats = kvStore => {
|
|
200
|
+
const result = [];
|
|
201
|
+
const prefix = 'vat.name.';
|
|
202
|
+
for (const k of enumeratePrefixedKeys(kvStore, prefix)) {
|
|
203
|
+
const vatID = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
204
|
+
const name = k.slice(prefix.length);
|
|
205
|
+
result.push([name, vatID]);
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export const getAllDynamicVats = getRequired => {
|
|
211
|
+
return JSON.parse(getRequired('vat.dynamicIDs'));
|
|
212
|
+
};
|
|
213
|
+
|
|
148
214
|
// we use different starting index values for the various vNN/koNN/kdNN/kpNN
|
|
149
215
|
// slots, to reduce confusing overlap when looking at debug messages (e.g.
|
|
150
216
|
// seeing both kp1 and ko1, which are completely unrelated despite having the
|
|
@@ -163,15 +229,59 @@ const FIRST_PROMISE_ID = 40n;
|
|
|
163
229
|
const FIRST_CRANK_NUMBER = 0n;
|
|
164
230
|
const FIRST_METER_ID = 1n;
|
|
165
231
|
|
|
232
|
+
// this default "reap interval" is low for the benefit of tests:
|
|
233
|
+
// applications should set it to something higher (perhaps 200) based
|
|
234
|
+
// on their expected usage
|
|
235
|
+
|
|
236
|
+
export const DEFAULT_DELIVERIES_PER_BOYD = 1;
|
|
237
|
+
|
|
238
|
+
// "20" will trigger a BOYD after 10 krefs are dropped and retired
|
|
239
|
+
// (drops and retires are delivered in separate messages, so
|
|
240
|
+
// 10+10=20). The worst case work-expansion we've seen is in #8401,
|
|
241
|
+
// where one drop breaks one cycle, and each cycle's cleanup causes 50
|
|
242
|
+
// syscalls in the next v9-zoe BOYD. So this should limit each BOYD
|
|
243
|
+
// to cleaning 10 cycles, in 500 syscalls.
|
|
244
|
+
|
|
245
|
+
export const DEFAULT_GC_KREFS_PER_BOYD = 20;
|
|
246
|
+
|
|
166
247
|
/**
|
|
167
248
|
* @param {SwingStoreKernelStorage} kernelStorage
|
|
168
|
-
* @param {
|
|
249
|
+
* @param {number | 'uninitialized'} expectedVersion
|
|
250
|
+
* @param {KernelSlog} [kernelSlog]
|
|
169
251
|
*/
|
|
170
|
-
export default function makeKernelKeeper(
|
|
252
|
+
export default function makeKernelKeeper(
|
|
253
|
+
kernelStorage,
|
|
254
|
+
expectedVersion,
|
|
255
|
+
kernelSlog,
|
|
256
|
+
) {
|
|
171
257
|
const { kvStore, transcriptStore, snapStore, bundleStore } = kernelStorage;
|
|
172
258
|
|
|
173
259
|
insistStorageAPI(kvStore);
|
|
174
260
|
|
|
261
|
+
// the terminated-vats cache is normally populated from
|
|
262
|
+
// 'vats.terminated', but for initialization purposes we need give
|
|
263
|
+
// it a value here, and then populate it for real if we're dealing
|
|
264
|
+
// with an up-to-date DB
|
|
265
|
+
let terminatedVats = [];
|
|
266
|
+
|
|
267
|
+
const versionString = kvStore.get('version');
|
|
268
|
+
const version = Number(versionString || '0');
|
|
269
|
+
if (expectedVersion === 'uninitialized') {
|
|
270
|
+
if (kvStore.has('initialized')) {
|
|
271
|
+
throw Error(`kernel DB already initialized (v0)`);
|
|
272
|
+
}
|
|
273
|
+
if (versionString) {
|
|
274
|
+
throw Error(`kernel DB already initialized (v${versionString})`);
|
|
275
|
+
}
|
|
276
|
+
} else if (expectedVersion !== version) {
|
|
277
|
+
throw Error(
|
|
278
|
+
`kernel DB is too old: has version v${version}, but expected v${expectedVersion}`,
|
|
279
|
+
);
|
|
280
|
+
} else {
|
|
281
|
+
// DB is up-to-date, so populate any caches we use
|
|
282
|
+
terminatedVats = JSON.parse(getRequired('vats.terminated'));
|
|
283
|
+
}
|
|
284
|
+
|
|
175
285
|
/**
|
|
176
286
|
* @param {string} key
|
|
177
287
|
* @returns {string}
|
|
@@ -233,12 +343,10 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
233
343
|
deviceKeepers: new Map(), // deviceID -> deviceKeeper
|
|
234
344
|
});
|
|
235
345
|
|
|
236
|
-
function getInitialized() {
|
|
237
|
-
return !!kvStore.get('initialized');
|
|
238
|
-
}
|
|
239
|
-
|
|
240
346
|
function setInitialized() {
|
|
241
|
-
kvStore.
|
|
347
|
+
assert(!kvStore.has('initialized'));
|
|
348
|
+
assert(!kvStore.has('version'));
|
|
349
|
+
kvStore.set('version', `${CURRENT_SCHEMA_VERSION}`);
|
|
242
350
|
}
|
|
243
351
|
|
|
244
352
|
function getCrankNumber() {
|
|
@@ -290,12 +398,17 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
290
398
|
}
|
|
291
399
|
|
|
292
400
|
/**
|
|
293
|
-
* @param {
|
|
401
|
+
* @param {InternalKernelOptions} kernelOptions
|
|
294
402
|
*/
|
|
295
403
|
function createStartingKernelState(kernelOptions) {
|
|
404
|
+
// this should probably be a standalone function, not a method
|
|
296
405
|
const {
|
|
297
406
|
defaultManagerType = 'local',
|
|
298
|
-
|
|
407
|
+
defaultReapDirtThreshold = {
|
|
408
|
+
deliveries: DEFAULT_DELIVERIES_PER_BOYD,
|
|
409
|
+
gcKrefs: DEFAULT_GC_KREFS_PER_BOYD,
|
|
410
|
+
computrons: 'never',
|
|
411
|
+
},
|
|
299
412
|
relaxDurabilityRules = false,
|
|
300
413
|
snapshotInitial = 3,
|
|
301
414
|
snapshotInterval = 200,
|
|
@@ -305,6 +418,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
305
418
|
kvStore.set('vat.dynamicIDs', '[]');
|
|
306
419
|
kvStore.set('vat.nextID', `${FIRST_VAT_ID}`);
|
|
307
420
|
kvStore.set('vat.nextUpgradeID', `1`);
|
|
421
|
+
kvStore.set('vats.terminated', '[]');
|
|
308
422
|
kvStore.set('device.names', '[]');
|
|
309
423
|
kvStore.set('device.nextID', `${FIRST_DEVICE_ID}`);
|
|
310
424
|
kvStore.set('ko.nextID', `${FIRST_OBJECT_ID}`);
|
|
@@ -317,7 +431,10 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
317
431
|
initQueue('acceptanceQueue');
|
|
318
432
|
kvStore.set('crankNumber', `${FIRST_CRANK_NUMBER}`);
|
|
319
433
|
kvStore.set('kernel.defaultManagerType', defaultManagerType);
|
|
320
|
-
kvStore.set(
|
|
434
|
+
kvStore.set(
|
|
435
|
+
DEFAULT_REAP_DIRT_THRESHOLD_KEY,
|
|
436
|
+
JSON.stringify(defaultReapDirtThreshold),
|
|
437
|
+
);
|
|
321
438
|
kvStore.set('kernel.snapshotInitial', `${snapshotInitial}`);
|
|
322
439
|
kvStore.set('kernel.snapshotInterval', `${snapshotInterval}`);
|
|
323
440
|
if (relaxDurabilityRules) {
|
|
@@ -352,21 +469,25 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
352
469
|
|
|
353
470
|
/**
|
|
354
471
|
*
|
|
355
|
-
* @returns {
|
|
472
|
+
* @returns {ReapDirtThreshold}
|
|
356
473
|
*/
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
const ri = r === 'never' ? r : Number.parseInt(r, 10);
|
|
360
|
-
assert(ri === 'never' || typeof ri === 'number', `k.dri is '${ri}'`);
|
|
361
|
-
return ri;
|
|
474
|
+
function getDefaultReapDirtThreshold() {
|
|
475
|
+
return JSON.parse(getRequired(DEFAULT_REAP_DIRT_THRESHOLD_KEY));
|
|
362
476
|
}
|
|
363
477
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
);
|
|
369
|
-
|
|
478
|
+
/**
|
|
479
|
+
* @param {ReapDirtThreshold} threshold
|
|
480
|
+
*/
|
|
481
|
+
function setDefaultReapDirtThreshold(threshold) {
|
|
482
|
+
assert.typeof(threshold, 'object');
|
|
483
|
+
assert(threshold);
|
|
484
|
+
for (const [key, value] of Object.entries(threshold)) {
|
|
485
|
+
assert(
|
|
486
|
+
(typeof value === 'number' && value > 0) || value === 'never',
|
|
487
|
+
`threshold[${key}] ${value} must be a positive number or "never"`,
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
kvStore.set(DEFAULT_REAP_DIRT_THRESHOLD_KEY, JSON.stringify(threshold));
|
|
370
491
|
}
|
|
371
492
|
|
|
372
493
|
function getNat(key) {
|
|
@@ -500,7 +621,9 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
500
621
|
|
|
501
622
|
function getObjectRefCount(kernelSlot) {
|
|
502
623
|
const data = kvStore.get(`${kernelSlot}.refCount`);
|
|
503
|
-
|
|
624
|
+
if (!data) {
|
|
625
|
+
return { reachable: 0, recognizable: 0 };
|
|
626
|
+
}
|
|
504
627
|
const [reachable, recognizable] = commaSplit(data).map(Number);
|
|
505
628
|
reachable <= recognizable ||
|
|
506
629
|
Fail`refmismatch(get) ${kernelSlot} ${reachable},${recognizable}`;
|
|
@@ -578,19 +701,42 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
578
701
|
function ownerOfKernelObject(kernelSlot) {
|
|
579
702
|
insistKernelType('object', kernelSlot);
|
|
580
703
|
const owner = kvStore.get(`${kernelSlot}.owner`);
|
|
581
|
-
if (owner) {
|
|
582
|
-
|
|
704
|
+
if (!owner) {
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
707
|
+
insistVatID(owner);
|
|
708
|
+
if (terminatedVats.includes(owner)) {
|
|
709
|
+
return undefined;
|
|
583
710
|
}
|
|
584
711
|
return owner;
|
|
585
712
|
}
|
|
586
713
|
|
|
714
|
+
function retireKernelObjects(koids) {
|
|
715
|
+
Array.isArray(koids) || Fail`retireExports given non-Array ${koids}`;
|
|
716
|
+
const newActions = [];
|
|
717
|
+
for (const koid of koids) {
|
|
718
|
+
const importers = getImporters(koid);
|
|
719
|
+
for (const vatID of importers) {
|
|
720
|
+
newActions.push(`${vatID} retireImport ${koid}`);
|
|
721
|
+
}
|
|
722
|
+
deleteKernelObject(koid);
|
|
723
|
+
}
|
|
724
|
+
addGCActions(newActions);
|
|
725
|
+
}
|
|
726
|
+
|
|
587
727
|
function orphanKernelObject(kref, oldVat) {
|
|
728
|
+
// termination orphans all exports, upgrade orphans non-durable
|
|
729
|
+
// exports, and syscall.abandonExports orphans specific ones
|
|
588
730
|
const ownerKey = `${kref}.owner`;
|
|
589
731
|
const ownerVat = kvStore.get(ownerKey);
|
|
590
732
|
ownerVat === oldVat || Fail`export ${kref} not owned by old vat`;
|
|
591
733
|
kvStore.delete(ownerKey);
|
|
734
|
+
const { vatSlot: vref } = getReachableAndVatSlot(oldVat, kref);
|
|
735
|
+
kvStore.delete(`${oldVat}.c.${kref}`);
|
|
736
|
+
kvStore.delete(`${oldVat}.c.${vref}`);
|
|
737
|
+
addMaybeFreeKref(kref);
|
|
592
738
|
// note that we do not delete the object here: it will be
|
|
593
|
-
//
|
|
739
|
+
// retired if/when all other references are dropped
|
|
594
740
|
}
|
|
595
741
|
|
|
596
742
|
function deleteKernelObject(koid) {
|
|
@@ -764,7 +910,6 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
764
910
|
|
|
765
911
|
let idx = 0;
|
|
766
912
|
for (const dataSlot of capdata.slots) {
|
|
767
|
-
// eslint-disable-next-line no-use-before-define
|
|
768
913
|
incrementRefCount(dataSlot, `resolve|${kernelSlot}|s${idx}`);
|
|
769
914
|
idx += 1;
|
|
770
915
|
}
|
|
@@ -785,14 +930,64 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
785
930
|
kvStore.set(`${kernelSlot}.data.slots`, capdata.slots.join(','));
|
|
786
931
|
}
|
|
787
932
|
|
|
788
|
-
function
|
|
933
|
+
async function removeVatFromSwingStoreExports(vatID) {
|
|
934
|
+
// Delete primary swingstore records for this vat, in preparation
|
|
935
|
+
// for (slow) deletion. After this, swingstore exports will omit
|
|
936
|
+
// this vat. This is called from the kernel's terminateVat, which
|
|
937
|
+
// initiates (but does not complete) deletion.
|
|
938
|
+
snapStore.stopUsingLastSnapshot(vatID);
|
|
939
|
+
await transcriptStore.stopUsingTranscript(vatID);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Perform some cleanup work for a specific (terminated but not
|
|
944
|
+
* fully-deleted) vat, possibly limited by a budget. Returns 'done'
|
|
945
|
+
* (where false means "please call me again", and true means "you
|
|
946
|
+
* can delete the vatID now"), and a count of how much work was done
|
|
947
|
+
* (so the runPolicy can decide when to stop).
|
|
948
|
+
*
|
|
949
|
+
* @param {string} vatID
|
|
950
|
+
* @param {CleanupBudget} budget
|
|
951
|
+
* @returns {{ done: boolean, work: CleanupWork }}
|
|
952
|
+
*
|
|
953
|
+
*/
|
|
954
|
+
function cleanupAfterTerminatedVat(vatID, budget) {
|
|
955
|
+
// this is called from terminateVat, which is called from either:
|
|
956
|
+
// * end of processDeliveryMessage, if crankResults.terminate
|
|
957
|
+
// * device-vat-admin (when vat-v-a does adminNode.terminateVat)
|
|
958
|
+
// (which always happens inside processDeliveryMessage)
|
|
959
|
+
// so we're always followed by a call to processRefcounts, at
|
|
960
|
+
// end-of-delivery in processDeliveryMessage, after checking
|
|
961
|
+
// crankResults.terminate
|
|
962
|
+
|
|
789
963
|
insistVatID(vatID);
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
964
|
+
const work = {
|
|
965
|
+
exports: 0,
|
|
966
|
+
imports: 0,
|
|
967
|
+
kv: 0,
|
|
968
|
+
snapshots: 0,
|
|
969
|
+
transcripts: 0,
|
|
970
|
+
};
|
|
971
|
+
let remaining = 0;
|
|
972
|
+
|
|
973
|
+
// TODO: it would be slightly cheaper to walk all kvStore keys in
|
|
974
|
+
// order, and act on each one according to its category (c-list
|
|
975
|
+
// export, c-list import, vatstore, other), so we use a single
|
|
976
|
+
// enumeratePrefixedKeys() call each time. Until we do that, the
|
|
977
|
+
// last phase of the cleanup (where we've deleted all the exports
|
|
978
|
+
// and imports, and are working on the remaining keys) will waste
|
|
979
|
+
// two DB queries on each call. OTOH, those queries will probably
|
|
980
|
+
// hit the same SQLite index page as the successful one, so it
|
|
981
|
+
// probably won't cause any extra disk IO. So we can defer this
|
|
982
|
+
// optimization for a while. Note: when we implement it, be
|
|
983
|
+
// prepared to encounter the clist entries in eiher order (kref
|
|
984
|
+
// first or vref first), and delete the other one in the same
|
|
985
|
+
// call, so we don't wind up with half an entry.
|
|
794
986
|
|
|
795
|
-
vatKeeper
|
|
987
|
+
const vatKeeper = provideVatKeeper(vatID);
|
|
988
|
+
const clistPrefix = `${vatID}.c.`;
|
|
989
|
+
const exportPrefix = `${clistPrefix}o+`;
|
|
990
|
+
const importPrefix = `${clistPrefix}o-`;
|
|
796
991
|
|
|
797
992
|
// Note: ASCII order is "+,-./", and we rely upon this to split the
|
|
798
993
|
// keyspace into the various o+NN/o-NN/etc spaces. If we were using a
|
|
@@ -808,6 +1003,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
808
1003
|
// used.
|
|
809
1004
|
|
|
810
1005
|
// first, scan for exported objects, which must be orphaned
|
|
1006
|
+
remaining = budget.exports ?? budget.default;
|
|
811
1007
|
for (const k of enumeratePrefixedKeys(kvStore, exportPrefix)) {
|
|
812
1008
|
// The void for an object exported by a vat will always be of the form
|
|
813
1009
|
// `o+NN`. The '+' means that the vat exported the object (rather than
|
|
@@ -816,28 +1012,67 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
816
1012
|
// begin with `vMM.c.o+`. In addition to deleting the c-list entry, we
|
|
817
1013
|
// must also delete the corresponding kernel owner entry for the object,
|
|
818
1014
|
// since the object will no longer be accessible.
|
|
1015
|
+
const vref = stripPrefix(clistPrefix, k);
|
|
1016
|
+
assert(vref.startsWith('o+'), vref);
|
|
819
1017
|
const kref = kvStore.get(k);
|
|
1018
|
+
// note: adds to maybeFreeKrefs, deletes c-list and .owner
|
|
820
1019
|
orphanKernelObject(kref, vatID);
|
|
1020
|
+
work.exports += 1;
|
|
1021
|
+
remaining -= 1;
|
|
1022
|
+
if (remaining <= 0) {
|
|
1023
|
+
return { done: false, work };
|
|
1024
|
+
}
|
|
821
1025
|
}
|
|
822
1026
|
|
|
823
1027
|
// then scan for imported objects, which must be decrefed
|
|
1028
|
+
remaining = budget.imports ?? budget.default;
|
|
824
1029
|
for (const k of enumeratePrefixedKeys(kvStore, importPrefix)) {
|
|
825
1030
|
// abandoned imports: delete the clist entry as if the vat did a
|
|
826
1031
|
// drop+retire
|
|
827
1032
|
const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
828
|
-
const vref = k
|
|
1033
|
+
const vref = stripPrefix(clistPrefix, k);
|
|
829
1034
|
vatKeeper.deleteCListEntry(kref, vref);
|
|
830
1035
|
// that will also delete both db keys
|
|
1036
|
+
work.imports += 1;
|
|
1037
|
+
remaining -= 1;
|
|
1038
|
+
if (remaining <= 0) {
|
|
1039
|
+
return { done: false, work };
|
|
1040
|
+
}
|
|
831
1041
|
}
|
|
832
1042
|
|
|
833
1043
|
// the caller used enumeratePromisesByDecider() before calling us,
|
|
834
1044
|
// so they already know the orphaned promises to reject
|
|
835
1045
|
|
|
836
1046
|
// now loop back through everything and delete it all
|
|
1047
|
+
remaining = budget.kv ?? budget.default;
|
|
837
1048
|
for (const k of enumeratePrefixedKeys(kvStore, `${vatID}.`)) {
|
|
838
1049
|
kvStore.delete(k);
|
|
1050
|
+
work.kv += 1;
|
|
1051
|
+
remaining -= 1;
|
|
1052
|
+
if (remaining <= 0) {
|
|
1053
|
+
return { done: false, work };
|
|
1054
|
+
}
|
|
839
1055
|
}
|
|
840
1056
|
|
|
1057
|
+
// this will internally loop through 'budget' deletions
|
|
1058
|
+
remaining = budget.snapshots ?? budget.default;
|
|
1059
|
+
const dsc = vatKeeper.deleteSnapshots(remaining);
|
|
1060
|
+
work.snapshots += dsc.cleanups;
|
|
1061
|
+
remaining -= dsc.cleanups;
|
|
1062
|
+
if (remaining <= 0) {
|
|
1063
|
+
return { done: false, work };
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// same
|
|
1067
|
+
remaining = budget.transcripts ?? budget.default;
|
|
1068
|
+
const dts = vatKeeper.deleteTranscripts(remaining);
|
|
1069
|
+
work.transcripts += dts.cleanups;
|
|
1070
|
+
remaining -= dts.cleanups;
|
|
1071
|
+
// last task, so increment cleanups, but dc.done is authoritative
|
|
1072
|
+
return { done: dts.done, work };
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
function deleteVatID(vatID) {
|
|
841
1076
|
// TODO: deleting entries from the dynamic vat IDs list requires a linear
|
|
842
1077
|
// scan of the list; arguably this collection ought to be represented in a
|
|
843
1078
|
// different way that makes it efficient to remove an entry from it, though
|
|
@@ -1102,15 +1337,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1102
1337
|
kvStore.set(KEY, JSON.stringify(dynamicVatIDs));
|
|
1103
1338
|
}
|
|
1104
1339
|
|
|
1105
|
-
|
|
1106
|
-
const result = [];
|
|
1107
|
-
for (const k of enumeratePrefixedKeys(kvStore, 'vat.name.')) {
|
|
1108
|
-
const name = k.slice(9);
|
|
1109
|
-
const vatID = kvStore.get(k) || Fail`getNextKey ensures get`;
|
|
1110
|
-
result.push([name, vatID]);
|
|
1111
|
-
}
|
|
1112
|
-
return result;
|
|
1113
|
-
}
|
|
1340
|
+
const getStaticVats = () => getAllStaticVats(kvStore);
|
|
1114
1341
|
|
|
1115
1342
|
function getDevices() {
|
|
1116
1343
|
const result = [];
|
|
@@ -1122,9 +1349,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1122
1349
|
return result;
|
|
1123
1350
|
}
|
|
1124
1351
|
|
|
1125
|
-
|
|
1126
|
-
return JSON.parse(getRequired('vat.dynamicIDs'));
|
|
1127
|
-
}
|
|
1352
|
+
const getDynamicVats = () => getAllDynamicVats(getRequired);
|
|
1128
1353
|
|
|
1129
1354
|
function allocateUpgradeID() {
|
|
1130
1355
|
const nextID = Nat(BigInt(getRequired(`vat.nextUpgradeID`)));
|
|
@@ -1132,6 +1357,44 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1132
1357
|
return makeUpgradeID(nextID);
|
|
1133
1358
|
}
|
|
1134
1359
|
|
|
1360
|
+
function markVatAsTerminated(vatID) {
|
|
1361
|
+
if (!terminatedVats.includes(vatID)) {
|
|
1362
|
+
terminatedVats.push(vatID);
|
|
1363
|
+
kvStore.set(`vats.terminated`, JSON.stringify(terminatedVats));
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
function getFirstTerminatedVat() {
|
|
1368
|
+
if (terminatedVats.length) {
|
|
1369
|
+
return terminatedVats[0];
|
|
1370
|
+
}
|
|
1371
|
+
return undefined;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
function forgetTerminatedVat(vatID) {
|
|
1375
|
+
terminatedVats = terminatedVats.filter(id => id !== vatID);
|
|
1376
|
+
kvStore.set(`vats.terminated`, JSON.stringify(terminatedVats));
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* @param {PolicyOutputCleanupBudget} allowCleanup
|
|
1381
|
+
* @returns {RunQueueEventCleanupTerminatedVat | undefined}
|
|
1382
|
+
*/
|
|
1383
|
+
function nextCleanupTerminatedVatAction(allowCleanup) {
|
|
1384
|
+
if (allowCleanup === false) {
|
|
1385
|
+
return undefined;
|
|
1386
|
+
} else {
|
|
1387
|
+
const unlimited = { default: Infinity };
|
|
1388
|
+
/** @type {CleanupBudget} */
|
|
1389
|
+
const budget = allowCleanup === true ? unlimited : allowCleanup;
|
|
1390
|
+
const vatID = getFirstTerminatedVat();
|
|
1391
|
+
if (vatID) {
|
|
1392
|
+
return { type: 'cleanup-terminated-vat', vatID, budget };
|
|
1393
|
+
}
|
|
1394
|
+
return undefined;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1135
1398
|
// As refcounts are decremented, we accumulate a set of krefs for which
|
|
1136
1399
|
// action might need to be taken:
|
|
1137
1400
|
// * promises which are now resolved and unreferenced can be deleted
|
|
@@ -1232,14 +1495,43 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1232
1495
|
return false;
|
|
1233
1496
|
}
|
|
1234
1497
|
|
|
1498
|
+
// TODO (#9888): change the processRefcounts maybeFreeKrefs
|
|
1499
|
+
// iteration to handle krefs that get added multiple times (while
|
|
1500
|
+
// we're iterating), because we might do different work the second
|
|
1501
|
+
// time around. The concerning scenario is:
|
|
1502
|
+
//
|
|
1503
|
+
// * kp2 has resolution data which references ko1
|
|
1504
|
+
// * somehow both kp2 and ko1 end up on maybeFreeKrefs, but ko1.reachable=1
|
|
1505
|
+
// (via kp2)
|
|
1506
|
+
// * our iterator visits ko1 first, sees it is still reachable, ignores it
|
|
1507
|
+
// * then the iterator visits kp2, decrefs its kp.data.slots
|
|
1508
|
+
// * this pushes ko1 back onto maybeFreeKrefs
|
|
1509
|
+
// * we need to examine ko1 again, so it can be released
|
|
1510
|
+
//
|
|
1511
|
+
// It could also happen if/when we implement #2069 auxdata:
|
|
1512
|
+
//
|
|
1513
|
+
// * ko2 has auxdata that references ko1
|
|
1514
|
+
// * both ko1 and ko2 end up on maybeFreeKrefs, but ko1.reachable = 1
|
|
1515
|
+
// (via the ko2 auxdata)
|
|
1516
|
+
// * our iterator visits ko1 first, sees it is still reachable, ignores it
|
|
1517
|
+
// * then the iterator visits ko2, does deleteKernelObject
|
|
1518
|
+
// * this frees the auxdata, which pushes ko1 back onto maybeFreeKrefs
|
|
1519
|
+
// * we need to examine ko1 again, so it can be released
|
|
1520
|
+
//
|
|
1521
|
+
// We should use something like an ordered Set, and a loop that does:
|
|
1522
|
+
// * pop the first kref off
|
|
1523
|
+
// * processes it (maybe adding more krefs)
|
|
1524
|
+
// * repeats until the thing is empty
|
|
1525
|
+
// Or maybe make a copy of maybeFreeKrefs at the start, clear the
|
|
1526
|
+
// original, and wrap this in a loop that keeps going until
|
|
1527
|
+
// maybeFreeKrefs is still empty at the end. Be sure to imagine a
|
|
1528
|
+
// very deep linked list: don't just process it twice, keep
|
|
1529
|
+
// processing until there's nothing left to do, otherwise we'll be
|
|
1530
|
+
// leaving work for the next delivery.
|
|
1531
|
+
|
|
1235
1532
|
function processRefcounts() {
|
|
1236
1533
|
if (enableKernelGC) {
|
|
1237
|
-
const actions =
|
|
1238
|
-
// TODO (else buggy): change the iteration to handle krefs that get
|
|
1239
|
-
// added multiple times (while we're iterating), because we might do
|
|
1240
|
-
// different work the second time around. Something like an ordered
|
|
1241
|
-
// Set, and a loop that: pops the first kref off, processes it (maybe
|
|
1242
|
-
// adding more krefs), repeats until the thing is empty.
|
|
1534
|
+
const actions = new Set();
|
|
1243
1535
|
for (const kref of maybeFreeKrefs.values()) {
|
|
1244
1536
|
const { type } = parseKernelSlot(kref);
|
|
1245
1537
|
if (type === 'promise') {
|
|
@@ -1247,6 +1539,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1247
1539
|
const kp = getKernelPromise(kpid);
|
|
1248
1540
|
if (kp.refCount === 0) {
|
|
1249
1541
|
let idx = 0;
|
|
1542
|
+
// TODO (#9889) don't assume promise is settled
|
|
1250
1543
|
for (const slot of kp.data.slots) {
|
|
1251
1544
|
// Note: the following decrement can result in an addition to the
|
|
1252
1545
|
// maybeFreeKrefs set, which we are in the midst of iterating.
|
|
@@ -1257,46 +1550,89 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1257
1550
|
deleteKernelPromise(kpid);
|
|
1258
1551
|
}
|
|
1259
1552
|
}
|
|
1553
|
+
|
|
1260
1554
|
if (type === 'object') {
|
|
1261
1555
|
const { reachable, recognizable } = getObjectRefCount(kref);
|
|
1262
1556
|
if (reachable === 0) {
|
|
1263
|
-
|
|
1264
|
-
if (
|
|
1265
|
-
|
|
1557
|
+
// We avoid ownerOfKernelObject(), which will report
|
|
1558
|
+
// 'undefined' if the owner is dead (and being slowly
|
|
1559
|
+
// deleted). Message delivery should use that, but not us.
|
|
1560
|
+
const ownerKey = `${kref}.owner`;
|
|
1561
|
+
let ownerVatID = kvStore.get(ownerKey);
|
|
1562
|
+
const terminated = terminatedVats.includes(ownerVatID);
|
|
1563
|
+
|
|
1564
|
+
// Some objects that are still owned, but the owning vat
|
|
1565
|
+
// might still alive, or might be terminated and in the
|
|
1566
|
+
// process of being deleted. These two clauses are
|
|
1567
|
+
// mutually exclusive.
|
|
1568
|
+
|
|
1569
|
+
if (ownerVatID && !terminated) {
|
|
1266
1570
|
const vatKeeper = provideVatKeeper(ownerVatID);
|
|
1267
|
-
const
|
|
1268
|
-
if (
|
|
1571
|
+
const vatConsidersReachable = vatKeeper.getReachableFlag(kref);
|
|
1572
|
+
if (vatConsidersReachable) {
|
|
1269
1573
|
// the reachable count is zero, but the vat doesn't realize it
|
|
1270
1574
|
actions.add(`${ownerVatID} dropExport ${kref}`);
|
|
1271
1575
|
}
|
|
1272
1576
|
if (recognizable === 0) {
|
|
1273
|
-
// TODO: rethink this
|
|
1274
|
-
// assert.equal(
|
|
1577
|
+
// TODO: rethink this assert
|
|
1578
|
+
// assert.equal(vatConsidersReachable, false, `${kref} is reachable but not recognizable`);
|
|
1275
1579
|
actions.add(`${ownerVatID} retireExport ${kref}`);
|
|
1276
1580
|
}
|
|
1277
|
-
} else if (
|
|
1278
|
-
//
|
|
1279
|
-
//
|
|
1280
|
-
//
|
|
1281
|
-
|
|
1581
|
+
} else if (ownerVatID && terminated) {
|
|
1582
|
+
// When we're slowly deleting a vat, and one of its
|
|
1583
|
+
// exports becomes unreferenced, we obviously must not
|
|
1584
|
+
// send dropExports or retireExports into the dead vat.
|
|
1585
|
+
// We fast-forward the abandonment that slow-deletion
|
|
1586
|
+
// would have done, then treat the object as orphaned.
|
|
1587
|
+
|
|
1588
|
+
const { vatSlot } = getReachableAndVatSlot(ownerVatID, kref);
|
|
1589
|
+
// delete directly, not orphanKernelObject(), which
|
|
1590
|
+
// would re-submit to maybeFreeKrefs
|
|
1591
|
+
kvStore.delete(ownerKey);
|
|
1592
|
+
kvStore.delete(`${ownerVatID}.c.${kref}`);
|
|
1593
|
+
kvStore.delete(`${ownerVatID}.c.${vatSlot}`);
|
|
1594
|
+
// now fall through to the orphaned case
|
|
1595
|
+
ownerVatID = undefined;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// Now handle objects which were orphaned. NOTE: this
|
|
1599
|
+
// includes objects which were owned by a terminated (but
|
|
1600
|
+
// not fully deleted) vat, where `ownerVatID` was cleared
|
|
1601
|
+
// in the last line of that previous clause (the
|
|
1602
|
+
// fall-through case). Don't try to change this `if
|
|
1603
|
+
// (!ownerVatID)` into an `else if`: the two clauses are
|
|
1604
|
+
// *not* mutually-exclusive.
|
|
1605
|
+
|
|
1606
|
+
if (!ownerVatID) {
|
|
1607
|
+
// orphaned and unreachable, so retire it. If the kref
|
|
1608
|
+
// is recognizable, then we need retireKernelObjects()
|
|
1609
|
+
// to scan for importers and send retireImports (and
|
|
1610
|
+
// delete), else we can call deleteKernelObject directly
|
|
1611
|
+
if (recognizable) {
|
|
1612
|
+
retireKernelObjects([kref]);
|
|
1613
|
+
} else {
|
|
1614
|
+
deleteKernelObject(kref);
|
|
1615
|
+
}
|
|
1282
1616
|
}
|
|
1283
1617
|
}
|
|
1284
1618
|
}
|
|
1285
1619
|
}
|
|
1286
|
-
|
|
1620
|
+
addGCActions([...actions]);
|
|
1287
1621
|
}
|
|
1288
1622
|
maybeFreeKrefs.clear();
|
|
1289
1623
|
}
|
|
1290
1624
|
|
|
1625
|
+
function createVatState(vatID, source, options) {
|
|
1626
|
+
initializeVatState(kvStore, transcriptStore, vatID, source, options);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1291
1629
|
function provideVatKeeper(vatID) {
|
|
1292
1630
|
insistVatID(vatID);
|
|
1293
1631
|
const found = ephemeral.vatKeepers.get(vatID);
|
|
1294
1632
|
if (found !== undefined) {
|
|
1295
1633
|
return found;
|
|
1296
1634
|
}
|
|
1297
|
-
|
|
1298
|
-
initializeVatState(kvStore, transcriptStore, vatID);
|
|
1299
|
-
}
|
|
1635
|
+
assert(kvStore.has(`${vatID}.o.nextID`), `${vatID} was not initialized`);
|
|
1300
1636
|
const vk = makeVatKeeper(
|
|
1301
1637
|
kvStore,
|
|
1302
1638
|
transcriptStore,
|
|
@@ -1314,6 +1650,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1314
1650
|
incStat,
|
|
1315
1651
|
decStat,
|
|
1316
1652
|
getCrankNumber,
|
|
1653
|
+
scheduleReap,
|
|
1317
1654
|
snapStore,
|
|
1318
1655
|
);
|
|
1319
1656
|
ephemeral.vatKeepers.set(vatID, vk);
|
|
@@ -1322,7 +1659,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1322
1659
|
|
|
1323
1660
|
function vatIsAlive(vatID) {
|
|
1324
1661
|
insistVatID(vatID);
|
|
1325
|
-
return kvStore.has(`${vatID}.o.nextID`);
|
|
1662
|
+
return kvStore.has(`${vatID}.o.nextID`) && !terminatedVats.includes(vatID);
|
|
1326
1663
|
}
|
|
1327
1664
|
|
|
1328
1665
|
/**
|
|
@@ -1530,13 +1867,13 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1530
1867
|
}
|
|
1531
1868
|
|
|
1532
1869
|
return harden({
|
|
1533
|
-
getInitialized,
|
|
1534
1870
|
setInitialized,
|
|
1535
1871
|
createStartingKernelState,
|
|
1536
1872
|
getDefaultManagerType,
|
|
1537
|
-
getDefaultReapInterval,
|
|
1538
1873
|
getRelaxDurabilityRules,
|
|
1539
|
-
|
|
1874
|
+
getDefaultReapDirtThreshold,
|
|
1875
|
+
setDefaultReapDirtThreshold,
|
|
1876
|
+
|
|
1540
1877
|
getSnapshotInitial,
|
|
1541
1878
|
getSnapshotInterval,
|
|
1542
1879
|
setSnapshotInterval,
|
|
@@ -1570,6 +1907,7 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1570
1907
|
kernelObjectExists,
|
|
1571
1908
|
getImporters,
|
|
1572
1909
|
orphanKernelObject,
|
|
1910
|
+
retireKernelObjects,
|
|
1573
1911
|
deleteKernelObject,
|
|
1574
1912
|
pinObject,
|
|
1575
1913
|
|
|
@@ -1610,14 +1948,22 @@ export default function makeKernelKeeper(kernelStorage, kernelSlog) {
|
|
|
1610
1948
|
getVatIDForName,
|
|
1611
1949
|
allocateVatIDForNameIfNeeded,
|
|
1612
1950
|
allocateUnusedVatID,
|
|
1951
|
+
createVatState,
|
|
1613
1952
|
provideVatKeeper,
|
|
1614
1953
|
vatIsAlive,
|
|
1615
1954
|
evictVatKeeper,
|
|
1955
|
+
removeVatFromSwingStoreExports,
|
|
1616
1956
|
cleanupAfterTerminatedVat,
|
|
1617
1957
|
addDynamicVatID,
|
|
1618
1958
|
getDynamicVats,
|
|
1619
1959
|
getStaticVats,
|
|
1620
1960
|
getDevices,
|
|
1961
|
+
deleteVatID,
|
|
1962
|
+
|
|
1963
|
+
markVatAsTerminated,
|
|
1964
|
+
getFirstTerminatedVat,
|
|
1965
|
+
forgetTerminatedVat,
|
|
1966
|
+
nextCleanupTerminatedVatAction,
|
|
1621
1967
|
|
|
1622
1968
|
allocateUpgradeID,
|
|
1623
1969
|
|