@agoric/swingset-vat 0.32.3-dev-ad1e50e.0 → 0.32.3-dev-0eaf7cc.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/swingset-vat",
3
- "version": "0.32.3-dev-ad1e50e.0+ad1e50e",
3
+ "version": "0.32.3-dev-0eaf7cc.0+0eaf7cc",
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-ad1e50e.0+ad1e50e",
31
- "@agoric/kmarshal": "0.1.1-dev-ad1e50e.0+ad1e50e",
32
- "@agoric/store": "0.9.3-dev-ad1e50e.0+ad1e50e",
33
- "@agoric/swing-store": "0.9.2-dev-ad1e50e.0+ad1e50e",
34
- "@agoric/swingset-liveslots": "0.10.3-dev-ad1e50e.0+ad1e50e",
35
- "@agoric/swingset-xsnap-supervisor": "0.10.3-dev-ad1e50e.0+ad1e50e",
36
- "@agoric/time": "0.3.3-dev-ad1e50e.0+ad1e50e",
37
- "@agoric/vat-data": "0.5.3-dev-ad1e50e.0+ad1e50e",
38
- "@agoric/xsnap": "0.14.3-dev-ad1e50e.0+ad1e50e",
39
- "@agoric/xsnap-lockdown": "0.14.1-dev-ad1e50e.0+ad1e50e",
30
+ "@agoric/internal": "0.3.3-dev-0eaf7cc.0+0eaf7cc",
31
+ "@agoric/kmarshal": "0.1.1-dev-0eaf7cc.0+0eaf7cc",
32
+ "@agoric/store": "0.9.3-dev-0eaf7cc.0+0eaf7cc",
33
+ "@agoric/swing-store": "0.9.2-dev-0eaf7cc.0+0eaf7cc",
34
+ "@agoric/swingset-liveslots": "0.10.3-dev-0eaf7cc.0+0eaf7cc",
35
+ "@agoric/swingset-xsnap-supervisor": "0.10.3-dev-0eaf7cc.0+0eaf7cc",
36
+ "@agoric/time": "0.3.3-dev-0eaf7cc.0+0eaf7cc",
37
+ "@agoric/vat-data": "0.5.3-dev-0eaf7cc.0+0eaf7cc",
38
+ "@agoric/xsnap": "0.14.3-dev-0eaf7cc.0+0eaf7cc",
39
+ "@agoric/xsnap-lockdown": "0.14.1-dev-0eaf7cc.0+0eaf7cc",
40
40
  "@endo/base64": "^1.0.6",
41
41
  "@endo/bundle-source": "^3.3.0",
42
42
  "@endo/captp": "^4.2.2",
@@ -102,5 +102,5 @@
102
102
  "typeCoverage": {
103
103
  "atLeast": 75.1
104
104
  },
105
- "gitHead": "ad1e50e1abf378cc5421da7144c7f11fb3fde45f"
105
+ "gitHead": "0eaf7cc8151b4e1966f3f33e43362d3d021457aa"
106
106
  }
@@ -9,14 +9,32 @@ import { insistVatID } from '../lib/id.js';
9
9
  import { makeVatSlot } from '../lib/parseVatSlots.js';
10
10
  import { insistStorageAPI } from '../lib/storageAPI.js';
11
11
  import { makeVatOptionRecorder } from '../lib/recordVatOptions.js';
12
- import makeKernelKeeper from '../kernel/state/kernelKeeper.js';
12
+ import makeKernelKeeper, {
13
+ DEFAULT_DELIVERIES_PER_BOYD,
14
+ DEFAULT_GC_KREFS_PER_BOYD,
15
+ } from '../kernel/state/kernelKeeper.js';
13
16
  import { exportRootObject } from '../kernel/kernel.js';
14
17
  import { makeKernelQueueHandler } from '../kernel/kernelQueue.js';
15
18
 
19
+ /**
20
+ * @typedef { import('../types-external.js').SwingSetKernelConfig } SwingSetKernelConfig
21
+ * @typedef { import('../types-external.js').SwingStoreKernelStorage } SwingStoreKernelStorage
22
+ * @typedef { import('../types-internal.js').InternalKernelOptions } InternalKernelOptions
23
+ * @typedef { import('../types-internal.js').ReapDirtThreshold } ReapDirtThreshold
24
+ */
25
+
16
26
  function makeVatRootObjectSlot() {
17
27
  return makeVatSlot('object', true, 0);
18
28
  }
19
29
 
30
+ /**
31
+ * @param {SwingSetKernelConfig} config
32
+ * @param {SwingStoreKernelStorage} kernelStorage
33
+ * @param {*} [options]
34
+ * @returns {Promise<string | undefined>} KPID of the bootstrap message
35
+ * result promise
36
+ */
37
+
20
38
  export async function initializeKernel(config, kernelStorage, options = {}) {
21
39
  const {
22
40
  verbose = false,
@@ -25,22 +43,27 @@ export async function initializeKernel(config, kernelStorage, options = {}) {
25
43
  const logStartup = verbose ? console.debug : () => 0;
26
44
  insistStorageAPI(kernelStorage.kvStore);
27
45
 
28
- const kernelSlog = null;
29
- const kernelKeeper = makeKernelKeeper(kernelStorage, kernelSlog);
46
+ const kernelKeeper = makeKernelKeeper(kernelStorage, 'uninitialized');
30
47
  const optionRecorder = makeVatOptionRecorder(kernelKeeper, bundleHandler);
31
48
 
32
- const wasInitialized = kernelKeeper.getInitialized();
33
- assert(!wasInitialized);
34
49
  const {
35
50
  defaultManagerType,
36
- defaultReapInterval,
51
+ defaultReapInterval = DEFAULT_DELIVERIES_PER_BOYD,
52
+ defaultReapGCKrefs = DEFAULT_GC_KREFS_PER_BOYD,
37
53
  relaxDurabilityRules,
38
54
  snapshotInitial,
39
55
  snapshotInterval,
40
56
  } = config;
57
+ /** @type { ReapDirtThreshold } */
58
+ const defaultReapDirtThreshold = {
59
+ deliveries: defaultReapInterval,
60
+ gcKrefs: defaultReapGCKrefs,
61
+ computrons: 'never', // TODO no knob?
62
+ };
63
+ /** @type { InternalKernelOptions } */
41
64
  const kernelOptions = {
42
65
  defaultManagerType,
43
- defaultReapInterval,
66
+ defaultReapDirtThreshold,
44
67
  relaxDurabilityRules,
45
68
  snapshotInitial,
46
69
  snapshotInterval,
@@ -49,7 +72,7 @@ export async function initializeKernel(config, kernelStorage, options = {}) {
49
72
 
50
73
  for (const id of Object.keys(config.idToBundle || {})) {
51
74
  const bundle = config.idToBundle[id];
52
- assert.equal(bundle.moduleFormat, 'endoZipBase64');
75
+ assert(bundle.moduleFormat === 'endoZipBase64');
53
76
  if (!kernelKeeper.hasBundle(id)) {
54
77
  kernelKeeper.addBundle(id, bundle);
55
78
  }
@@ -64,7 +87,7 @@ export async function initializeKernel(config, kernelStorage, options = {}) {
64
87
 
65
88
  // generate the genesis vats
66
89
  await null;
67
- if (config.vats) {
90
+ if (config.vats && Object.keys(config.vats).length) {
68
91
  for (const name of Object.keys(config.vats)) {
69
92
  const {
70
93
  bundleID,
@@ -86,6 +109,8 @@ export async function initializeKernel(config, kernelStorage, options = {}) {
86
109
  'useTranscript',
87
110
  'critical',
88
111
  'reapInterval',
112
+ 'reapGCKrefs',
113
+ 'neverReap',
89
114
  'nodeOptions',
90
115
  ]);
91
116
  const vatID = kernelKeeper.allocateVatIDForNameIfNeeded(name);
@@ -256,7 +256,10 @@ export async function loadSwingsetConfigFile(configPath) {
256
256
  }
257
257
 
258
258
  export function swingsetIsInitialized(kernelStorage) {
259
- return !!kernelStorage.kvStore.get('initialized');
259
+ return !!(
260
+ kernelStorage.kvStore.get('version') ||
261
+ kernelStorage.kvStore.get('initialized')
262
+ );
260
263
  }
261
264
 
262
265
  /**
@@ -397,7 +400,7 @@ export async function initializeSwingset(
397
400
  enableSetup: true,
398
401
  managerType: 'local',
399
402
  useTranscript: false,
400
- reapInterval: 'never',
403
+ neverReap: true,
401
404
  },
402
405
  };
403
406
  }
@@ -0,0 +1,204 @@
1
+ import {
2
+ DEFAULT_REAP_DIRT_THRESHOLD_KEY,
3
+ DEFAULT_GC_KREFS_PER_BOYD,
4
+ getAllDynamicVats,
5
+ getAllStaticVats,
6
+ } from '../kernel/state/kernelKeeper.js';
7
+
8
+ const upgradeVatV0toV1 = (kvStore, defaultReapDirtThreshold, vatID) => {
9
+ // This is called, once per vat, when upgradeSwingset migrates from
10
+ // v0 to v1
11
+
12
+ // schema v0:
13
+ // Each vat has a `vNN.reapInterval` and `vNN.reapCountdown`.
14
+ // vNN.options has a `.reapInterval` property (however it was not
15
+ // updated by processChangeVatOptions). Either all are numbers, or
16
+ // all are 'never'.
17
+
18
+ const oldReapIntervalKey = `${vatID}.reapInterval`;
19
+ const oldReapCountdownKey = `${vatID}.reapCountdown`;
20
+ const vatOptionsKey = `${vatID}.options`;
21
+
22
+ // schema v1:
23
+ // Each vat has a `vNN.reapDirt`, and vNN.options has a
24
+ // `.reapDirtThreshold` property (which overrides kernel-wide
25
+ // `defaultReapDirtThreshold`)
26
+
27
+ const reapDirtKey = `${vatID}.reapDirt`;
28
+
29
+ assert(kvStore.has(oldReapIntervalKey), oldReapIntervalKey);
30
+ assert(kvStore.has(oldReapCountdownKey), oldReapCountdownKey);
31
+ assert(!kvStore.has(reapDirtKey), reapDirtKey);
32
+
33
+ const reapIntervalString = kvStore.get(oldReapIntervalKey);
34
+ const reapCountdownString = kvStore.get(oldReapCountdownKey);
35
+ assert(reapIntervalString !== undefined);
36
+ assert(reapCountdownString !== undefined);
37
+
38
+ const intervalIsNever = reapIntervalString === 'never';
39
+ const countdownIsNever = reapCountdownString === 'never';
40
+ // these were supposed to be the same
41
+ assert(
42
+ intervalIsNever === countdownIsNever,
43
+ `reapInterval=${reapIntervalString}, reapCountdown=${reapCountdownString}`,
44
+ );
45
+
46
+ // initialize or upgrade state
47
+ const reapDirt = {}; // all missing keys are treated as zero
48
+ const threshold = {};
49
+
50
+ if (intervalIsNever) {
51
+ // old vats that were never reaped (eg comms) used
52
+ // reapInterval='never', so respect that and set the other
53
+ // threshold values to never as well
54
+ threshold.never = true;
55
+ } else {
56
+ // deduce delivery count from old countdown values
57
+ const reapInterval = Number.parseInt(reapIntervalString, 10);
58
+ const reapCountdown = Number.parseInt(reapCountdownString, 10);
59
+ const deliveries = reapInterval - reapCountdown;
60
+ reapDirt.deliveries = Math.max(deliveries, 0); // just in case
61
+ if (reapInterval !== defaultReapDirtThreshold.deliveries) {
62
+ threshold.deliveries = reapInterval;
63
+ }
64
+ }
65
+
66
+ kvStore.delete(oldReapIntervalKey);
67
+ kvStore.delete(oldReapCountdownKey);
68
+ kvStore.set(reapDirtKey, JSON.stringify(reapDirt));
69
+
70
+ // Update options to use the new schema.
71
+ const options = JSON.parse(kvStore.get(vatOptionsKey));
72
+ delete options.reapInterval;
73
+ options.reapDirtThreshold = threshold;
74
+ kvStore.set(vatOptionsKey, JSON.stringify(options));
75
+ };
76
+
77
+ /**
78
+ * (maybe) upgrade the kernel state to the current schema
79
+ *
80
+ * This function is responsible for bringing the kernel's portion of
81
+ * swing-store (kernelStorage) up to the current version. The host app
82
+ * must call this each time it launches with a new version of
83
+ * swingset, before using makeSwingsetController() to build the
84
+ * kernel/controller (which will throw an error if handed an old
85
+ * database). It is ok to call it only on those reboots, but it is
86
+ * also safe to call on every reboot, because upgradeSwingset() is a
87
+ * no-op if the DB is already up-to-date.
88
+ *
89
+ * If an upgrade is needed, this function will modify the DB state, so
90
+ * the host app must be prepared for export-data callbacks being
91
+ * called during the upgrade, and it is responsible for doing a
92
+ * `hostStorage.commit()` afterwards.
93
+ *
94
+ * @param {SwingStoreKernelStorage} kernelStorage
95
+ * @returns {boolean} true if any changes were made
96
+ */
97
+ export const upgradeSwingset = kernelStorage => {
98
+ const { kvStore } = kernelStorage;
99
+ let modified = false;
100
+ let vstring = kvStore.get('version');
101
+ if (vstring === undefined) {
102
+ vstring = '0';
103
+ }
104
+ let version = Number(vstring);
105
+
106
+ /**
107
+ * @param {string} key
108
+ * @returns {string}
109
+ */
110
+ function getRequired(key) {
111
+ if (!kvStore.has(key)) {
112
+ throw Error(`storage lacks required key ${key}`);
113
+ }
114
+ // @ts-expect-error already checked .has()
115
+ return kvStore.get(key);
116
+ }
117
+
118
+ // kernelKeeper.js has a large comment that defines our current
119
+ // kvStore schema, with a section describing the deltas. The upgrade
120
+ // steps applied here must match.
121
+
122
+ // schema v0:
123
+ //
124
+ // The kernel overall has `kernel.defaultReapInterval` and
125
+ // `kernel.initialized = 'true'`.
126
+ //
127
+ // Each vat has a `vNN.reapInterval` and `vNN.reapCountdown`.
128
+ // vNN.options has a `.reapInterval` property (however it was not
129
+ // updated by processChangeVatOptions, so do not rely upon its
130
+ // value). Either all are numbers, or all are 'never'.
131
+
132
+ if (version < 1) {
133
+ // schema v1:
134
+ //
135
+ // The kernel overall has `kernel.defaultReapDirtThreshold` and
136
+ // `kernel.version = '1'` (instead of .initialized).
137
+ //
138
+ // Each vat has a `vNN.reapDirt`, and vNN.options has a
139
+ // `.reapDirtThreshold` property
140
+
141
+ // So:
142
+ // * replace `kernel.initialized` with `kernel.version`
143
+ // * replace `kernel.defaultReapInterval` with
144
+ // `kernel.defaultReapDirtThreshold`
145
+ // * replace vat's `vNN.reapInterval`/`vNN.reapCountdown` with
146
+ // `vNN.reapDirt` and a `vNN.reapDirtThreshold` in `vNN.options`
147
+ // * then do per-vat upgrades with upgradeVatV0toV1
148
+
149
+ assert(kvStore.has('initialized'));
150
+ kvStore.delete('initialized');
151
+ // 'version' will be set at the end
152
+
153
+ // upgrade from old kernel.defaultReapInterval
154
+
155
+ const oldDefaultReapIntervalKey = 'kernel.defaultReapInterval';
156
+ assert(kvStore.has(oldDefaultReapIntervalKey));
157
+ assert(!kvStore.has(DEFAULT_REAP_DIRT_THRESHOLD_KEY));
158
+
159
+ /**
160
+ * @typedef { import('../types-internal.js').ReapDirtThreshold } ReapDirtThreshold
161
+ */
162
+
163
+ /** @type ReapDirtThreshold */
164
+ const threshold = {
165
+ deliveries: 'never',
166
+ gcKrefs: 'never',
167
+ computrons: 'never',
168
+ };
169
+
170
+ const oldValue = getRequired(oldDefaultReapIntervalKey);
171
+ if (oldValue !== 'never') {
172
+ const value = Number.parseInt(oldValue, 10);
173
+ assert.typeof(value, 'number');
174
+ threshold.deliveries = value;
175
+ // if BOYD wasn't turned off entirely (eg
176
+ // defaultReapInterval='never', which only happens in unit
177
+ // tests), then pretend we wanted a gcKrefs= threshold all
178
+ // along, so all vats get a retroactive gcKrefs threshold, which
179
+ // we need for the upcoming slow-vat-deletion to not trigger
180
+ // gigantic BOYD and break the kernel
181
+ threshold.gcKrefs = DEFAULT_GC_KREFS_PER_BOYD;
182
+ }
183
+ harden(threshold);
184
+ kvStore.set(DEFAULT_REAP_DIRT_THRESHOLD_KEY, JSON.stringify(threshold));
185
+ kvStore.delete(oldDefaultReapIntervalKey);
186
+
187
+ // now upgrade all vats
188
+ for (const [_name, vatID] of getAllStaticVats(kvStore)) {
189
+ upgradeVatV0toV1(kvStore, threshold, vatID);
190
+ }
191
+ for (const vatID of getAllDynamicVats(getRequired)) {
192
+ upgradeVatV0toV1(kvStore, threshold, vatID);
193
+ }
194
+
195
+ modified = true;
196
+ version = 1;
197
+ }
198
+
199
+ if (modified) {
200
+ kvStore.set('version', `${version}`);
201
+ }
202
+ return modified;
203
+ };
204
+ harden(upgradeSwingset);
package/src/index.js CHANGED
@@ -9,7 +9,7 @@ export {
9
9
  loadBasedir,
10
10
  loadSwingsetConfigFile,
11
11
  } from './controller/initializeSwingset.js';
12
-
12
+ export { upgradeSwingset } from './controller/upgradeSwingset.js';
13
13
  export {
14
14
  buildMailboxStateMap,
15
15
  buildMailbox,
@@ -10,7 +10,9 @@ import { foreverPolicy } from '../lib/runPolicies.js';
10
10
  import { makeVatManagerFactory } from './vat-loader/manager-factory.js';
11
11
  import { makeVatWarehouse } from './vat-warehouse.js';
12
12
  import makeDeviceManager from './deviceManager.js';
13
- import makeKernelKeeper from './state/kernelKeeper.js';
13
+ import makeKernelKeeper, {
14
+ CURRENT_SCHEMA_VERSION,
15
+ } from './state/kernelKeeper.js';
14
16
  import {
15
17
  kdebug,
16
18
  kdebugEnable,
@@ -112,7 +114,11 @@ export default function buildKernel(
112
114
  ? makeSlogger(slogCallbacks, writeSlogObject)
113
115
  : makeDummySlogger(slogCallbacks, makeConsole('disabled slogger'));
114
116
 
115
- const kernelKeeper = makeKernelKeeper(kernelStorage, kernelSlog);
117
+ const kernelKeeper = makeKernelKeeper(
118
+ kernelStorage,
119
+ CURRENT_SCHEMA_VERSION,
120
+ kernelSlog,
121
+ );
116
122
 
117
123
  /** @type {ReturnType<makeVatWarehouse>} */
118
124
  let vatWarehouse;
@@ -252,8 +258,7 @@ export default function buildKernel(
252
258
  */
253
259
  async function terminateVat(vatID, shouldReject, info) {
254
260
  console.log(`kernel terminating vat ${vatID} (failure=${shouldReject})`);
255
- const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
256
- const critical = vatKeeper.getOptions().critical;
261
+ let critical = false;
257
262
  insistCapData(info);
258
263
  // ISSUE: terminate stuff in its own crank like creation?
259
264
  // TODO: if a static vat terminates, panic the kernel?
@@ -264,6 +269,14 @@ export default function buildKernel(
264
269
  // check will report 'false'. That's fine, there's no state to
265
270
  // clean up.
266
271
  if (kernelKeeper.vatIsAlive(vatID)) {
272
+ // If there was no vat state, we can't make a vatKeeper to ask for
273
+ // options. For now, pretend the vat was non-critical. This will fail
274
+ // to panic the kernel for startVat failures in critical vats
275
+ // (#9157). The fix will add .critical to CrankResults, populated by a
276
+ // getOptions query in deliveryCrankResults() or copied from
277
+ // dynamicOptions in processCreateVat.
278
+ critical = kernelKeeper.provideVatKeeper(vatID).getOptions().critical;
279
+
267
280
  // Reject all promises decided by the vat, making sure to capture the list
268
281
  // of kpids before that data is deleted.
269
282
  const deadPromises = [...kernelKeeper.enumeratePromisesByDecider(vatID)];
@@ -357,6 +370,7 @@ export default function buildKernel(
357
370
  *
358
371
  * @typedef { import('@agoric/swingset-liveslots').MeterConsumption } MeterConsumption
359
372
  * @typedef { import('../types-internal.js').MeterID } MeterID
373
+ * @typedef { import('../types-internal.js').Dirt } Dirt
360
374
  *
361
375
  * Any delivery crank (send, notify, start-vat.. anything which is allowed
362
376
  * to make vat delivery) emits one of these status events if a delivery
@@ -375,7 +389,7 @@ export default function buildKernel(
375
389
  * didDelivery?: VatID, // we made a delivery to a vat, for run policy and save-snapshot
376
390
  * computrons?: BigInt, // computron count for run policy
377
391
  * meterID?: string, // deduct those computrons from a meter
378
- * decrementReapCount?: { vatID: VatID }, // the reap counter should decrement
392
+ * measureDirt?: { vatID: VatID, dirt: Dirt }, // dirt counters should increment
379
393
  * terminate?: { vatID: VatID, reject: boolean, info: SwingSetCapData }, // terminate vat, notify vat-admin
380
394
  * vatAdminMethargs?: RawMethargs, // methargs to notify vat-admin about create/upgrade results
381
395
  * } } CrankResults
@@ -442,16 +456,17 @@ export default function buildKernel(
442
456
  * event handler.
443
457
  *
444
458
  * Two flags influence this:
445
- * `decrementReapCount` is used for deliveries that run userspace code
459
+ * `measureDirt` is used for non-BOYD deliveries
446
460
  * `meterID` means we should check a meter
447
461
  *
448
462
  * @param {VatID} vatID
449
463
  * @param {DeliveryStatus} status
450
- * @param {boolean} decrementReapCount
464
+ * @param {boolean} measureDirt
451
465
  * @param {MeterID} [meterID]
466
+ * @param {number} [gcKrefs]
452
467
  * @returns {CrankResults}
453
468
  */
454
- function deliveryCrankResults(vatID, status, decrementReapCount, meterID) {
469
+ function deliveryCrankResults(vatID, status, measureDirt, meterID, gcKrefs) {
455
470
  let meterUnderrun = false;
456
471
  let computrons;
457
472
  if (status.metering?.compute) {
@@ -495,8 +510,16 @@ export default function buildKernel(
495
510
  results.terminate = { vatID, ...status.vatRequestedTermination };
496
511
  }
497
512
 
498
- if (decrementReapCount && !(results.abort || results.terminate)) {
499
- results.decrementReapCount = { vatID };
513
+ if (measureDirt && !(results.abort || results.terminate)) {
514
+ const dirt = { deliveries: 1 };
515
+ if (computrons) {
516
+ // this is BigInt, but we use plain Number in Dirt records
517
+ dirt.computrons = Number(computrons);
518
+ }
519
+ if (gcKrefs) {
520
+ dirt.gcKrefs = gcKrefs;
521
+ }
522
+ results.measureDirt = { vatID, dirt };
500
523
  }
501
524
 
502
525
  // We leave results.consumeMessage up to the caller. Send failures
@@ -535,7 +558,8 @@ export default function buildKernel(
535
558
  }
536
559
 
537
560
  const status = await deliverAndLogToVat(vatID, kd, vd);
538
- return deliveryCrankResults(vatID, status, true, meterID);
561
+ const gcKrefs = undefined; // TODO maybe increase by number of vrefs in args?
562
+ return deliveryCrankResults(vatID, status, true, meterID, gcKrefs);
539
563
  }
540
564
 
541
565
  /**
@@ -581,7 +605,8 @@ export default function buildKernel(
581
605
  const vd = vatWarehouse.kernelDeliveryToVatDelivery(vatID, kd);
582
606
  vatKeeper.deleteCListEntriesForKernelSlots(targets);
583
607
  const status = await deliverAndLogToVat(vatID, kd, vd);
584
- return deliveryCrankResults(vatID, status, true, meterID);
608
+ const gcKrefs = undefined; // TODO maybe increase by number of vrefs in args?
609
+ return deliveryCrankResults(vatID, status, true, meterID, gcKrefs);
585
610
  }
586
611
 
587
612
  /**
@@ -609,7 +634,9 @@ export default function buildKernel(
609
634
  }
610
635
  const vd = vatWarehouse.kernelDeliveryToVatDelivery(vatID, kd);
611
636
  const status = await deliverAndLogToVat(vatID, kd, vd);
612
- return deliveryCrankResults(vatID, status, false); // no meterID
637
+ const meterID = undefined; // no meterID
638
+ const gcKrefs = krefs.length;
639
+ return deliveryCrankResults(vatID, status, true, meterID, gcKrefs);
613
640
  }
614
641
 
615
642
  /**
@@ -624,11 +651,14 @@ export default function buildKernel(
624
651
  if (!vatWarehouse.lookup(vatID)) {
625
652
  return NO_DELIVERY_CRANK_RESULTS; // can't collect from the dead
626
653
  }
654
+ const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
627
655
  /** @type { KernelDeliveryBringOutYourDead } */
628
656
  const kd = harden([type]);
629
657
  const vd = vatWarehouse.kernelDeliveryToVatDelivery(vatID, kd);
630
658
  const status = await deliverAndLogToVat(vatID, kd, vd);
631
- return deliveryCrankResults(vatID, status, false); // no meter
659
+ vatKeeper.clearReapDirt(); // BOYD zeros out the when-to-BOYD counters
660
+ // no gcKrefs, BOYD clears them anyways
661
+ return deliveryCrankResults(vatID, status, false); // no meter, BOYD clears dirt
632
662
  }
633
663
 
634
664
  /**
@@ -669,8 +699,9 @@ export default function buildKernel(
669
699
  const status = await deliverAndLogToVat(vatID, kd, vd);
670
700
  // note: if deliveryCrankResults() learns to suspend vats,
671
701
  // startVat errors should still terminate them
702
+ const gcKrefs = undefined; // TODO maybe increase by number of vrefs in args?
672
703
  const results = harden({
673
- ...deliveryCrankResults(vatID, status, true, meterID),
704
+ ...deliveryCrankResults(vatID, status, true, meterID, gcKrefs),
674
705
  consumeMessage: true,
675
706
  });
676
707
  return results;
@@ -735,9 +766,17 @@ export default function buildKernel(
735
766
  function setKernelVatOption(vatID, option, value) {
736
767
  switch (option) {
737
768
  case 'reapInterval': {
769
+ // This still controls reapDirtThreshold.deliveries, and we do not
770
+ // yet offer controls for the other limits (gcKrefs or computrons).
738
771
  if (value === 'never' || isNat(value)) {
739
772
  const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
740
- vatKeeper.updateReapInterval(value);
773
+ const threshold = { ...vatKeeper.getReapDirtThreshold() };
774
+ if (value === 'never') {
775
+ threshold.deliveries = value;
776
+ } else {
777
+ threshold.deliveries = Number(value);
778
+ }
779
+ vatKeeper.setReapDirtThreshold(threshold);
741
780
  } else {
742
781
  console.log(`WARNING: invalid reapInterval value`, value);
743
782
  }
@@ -877,6 +916,7 @@ export default function buildKernel(
877
916
  const boydVD = vatWarehouse.kernelDeliveryToVatDelivery(vatID, boydKD);
878
917
  const boydStatus = await deliverAndLogToVat(vatID, boydKD, boydVD);
879
918
  const boydResults = deliveryCrankResults(vatID, boydStatus, false);
919
+ vatKeeper.clearReapDirt();
880
920
 
881
921
  // we don't meter bringOutYourDead since no user code is running, but we
882
922
  // still report computrons to the runPolicy
@@ -951,7 +991,14 @@ export default function buildKernel(
951
991
  startVatKD,
952
992
  startVatVD,
953
993
  );
954
- const startVatResults = deliveryCrankResults(vatID, startVatStatus, false);
994
+ const gcKrefs = undefined; // TODO maybe increase by number of vrefs in args?
995
+ const startVatResults = deliveryCrankResults(
996
+ vatID,
997
+ startVatStatus,
998
+ true,
999
+ meterID,
1000
+ gcKrefs,
1001
+ );
955
1002
  computrons = addComputrons(computrons, startVatResults.computrons);
956
1003
 
957
1004
  if (startVatResults.terminate) {
@@ -1292,13 +1339,11 @@ export default function buildKernel(
1292
1339
  }
1293
1340
  }
1294
1341
  }
1295
- if (crankResults.decrementReapCount) {
1342
+ if (crankResults.measureDirt) {
1296
1343
  // deliveries cause garbage, garbage needs collection
1297
- const { vatID } = crankResults.decrementReapCount;
1344
+ const { vatID, dirt } = crankResults.measureDirt;
1298
1345
  const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
1299
- if (vatKeeper.countdownToReap()) {
1300
- kernelKeeper.scheduleReap(vatID);
1301
- }
1346
+ vatKeeper.addDirt(dirt); // might schedule a reap for that vat
1302
1347
  }
1303
1348
 
1304
1349
  // Vat termination (during delivery) is triggered by an illegal
@@ -1572,10 +1617,14 @@ export default function buildKernel(
1572
1617
  'bundleID',
1573
1618
  'enablePipelining',
1574
1619
  'reapInterval',
1620
+ 'reapGCKrefs',
1621
+ 'neverReap',
1575
1622
  ]);
1576
1623
  const {
1577
1624
  bundleID = 'b1-00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
1578
1625
  reapInterval = 'never',
1626
+ reapGCKrefs = 'never',
1627
+ neverReap = false,
1579
1628
  enablePipelining,
1580
1629
  } = creationOptions;
1581
1630
  const vatID = kernelKeeper.allocateVatIDForNameIfNeeded(name);
@@ -1587,6 +1636,8 @@ export default function buildKernel(
1587
1636
  const options = {
1588
1637
  name,
1589
1638
  reapInterval,
1639
+ reapGCKrefs,
1640
+ neverReap,
1590
1641
  enablePipelining,
1591
1642
  managerType,
1592
1643
  };
@@ -1617,7 +1668,6 @@ export default function buildKernel(
1617
1668
  throw Error('kernel.start already called');
1618
1669
  }
1619
1670
  started = true;
1620
- kernelKeeper.getInitialized() || Fail`kernel not initialized`;
1621
1671
 
1622
1672
  kernelKeeper.loadStats();
1623
1673
 
@@ -1733,14 +1783,36 @@ export default function buildKernel(
1733
1783
  }
1734
1784
 
1735
1785
  function changeKernelOptions(options) {
1736
- assertKnownOptions(options, ['defaultReapInterval', 'snapshotInterval']);
1786
+ assertKnownOptions(options, [
1787
+ 'defaultReapInterval',
1788
+ 'defaultReapGCKrefs',
1789
+ 'snapshotInterval',
1790
+ ]);
1737
1791
  kernelKeeper.startCrank();
1738
1792
  try {
1739
1793
  for (const option of Object.getOwnPropertyNames(options)) {
1740
1794
  const value = options[option];
1741
1795
  switch (option) {
1742
1796
  case 'defaultReapInterval': {
1743
- kernelKeeper.setDefaultReapInterval(value);
1797
+ assert(
1798
+ (typeof value === 'number' && value > 0) || value === 'never',
1799
+ `defaultReapInterval ${value} must be a positive number or "never"`,
1800
+ );
1801
+ kernelKeeper.setDefaultReapDirtThreshold({
1802
+ ...kernelKeeper.getDefaultReapDirtThreshold(),
1803
+ deliveries: value,
1804
+ });
1805
+ break;
1806
+ }
1807
+ case 'defaultReapGCKrefs': {
1808
+ assert(
1809
+ (typeof value === 'number' && value > 0) || value === 'never',
1810
+ `defaultReapGCKrefs ${value} must be a positive number or "never"`,
1811
+ );
1812
+ kernelKeeper.setDefaultReapDirtThreshold({
1813
+ ...kernelKeeper.getDefaultReapDirtThreshold(),
1814
+ gcKrefs: value,
1815
+ });
1744
1816
  break;
1745
1817
  }
1746
1818
  case 'snapshotInterval': {