@agoric/swingset-vat 0.32.3-dev-de95355.0 → 0.32.3-dev-b6c58f2.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-de95355.0+de95355",
3
+ "version": "0.32.3-dev-b6c58f2.0+b6c58f2",
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-de95355.0+de95355",
31
- "@agoric/kmarshal": "0.1.1-dev-de95355.0+de95355",
32
- "@agoric/store": "0.9.3-dev-de95355.0+de95355",
33
- "@agoric/swing-store": "0.9.2-dev-de95355.0+de95355",
34
- "@agoric/swingset-liveslots": "0.10.3-dev-de95355.0+de95355",
35
- "@agoric/swingset-xsnap-supervisor": "0.10.3-dev-de95355.0+de95355",
36
- "@agoric/time": "0.3.3-dev-de95355.0+de95355",
37
- "@agoric/vat-data": "0.5.3-dev-de95355.0+de95355",
38
- "@agoric/xsnap": "0.14.3-dev-de95355.0+de95355",
39
- "@agoric/xsnap-lockdown": "0.14.1-dev-de95355.0+de95355",
30
+ "@agoric/internal": "0.3.3-dev-b6c58f2.0+b6c58f2",
31
+ "@agoric/kmarshal": "0.1.1-dev-b6c58f2.0+b6c58f2",
32
+ "@agoric/store": "0.9.3-dev-b6c58f2.0+b6c58f2",
33
+ "@agoric/swing-store": "0.9.2-dev-b6c58f2.0+b6c58f2",
34
+ "@agoric/swingset-liveslots": "0.10.3-dev-b6c58f2.0+b6c58f2",
35
+ "@agoric/swingset-xsnap-supervisor": "0.10.3-dev-b6c58f2.0+b6c58f2",
36
+ "@agoric/time": "0.3.3-dev-b6c58f2.0+b6c58f2",
37
+ "@agoric/vat-data": "0.5.3-dev-b6c58f2.0+b6c58f2",
38
+ "@agoric/xsnap": "0.14.3-dev-b6c58f2.0+b6c58f2",
39
+ "@agoric/xsnap-lockdown": "0.14.1-dev-b6c58f2.0+b6c58f2",
40
40
  "@endo/base64": "^1.0.6",
41
41
  "@endo/bundle-source": "^3.3.0",
42
42
  "@endo/captp": "^4.2.2",
@@ -49,6 +49,7 @@
49
49
  "@endo/init": "^1.1.3",
50
50
  "@endo/marshal": "^1.5.2",
51
51
  "@endo/nat": "^5.0.9",
52
+ "@endo/pass-style": "^1.4.2",
52
53
  "@endo/patterns": "^1.4.2",
53
54
  "@endo/promise-kit": "^1.1.4",
54
55
  "@endo/ses-ava": "^1.2.4",
@@ -102,5 +103,5 @@
102
103
  "typeCoverage": {
103
104
  "atLeast": 75.1
104
105
  },
105
- "gitHead": "de9535585f4913e792caf662d869d6f041881985"
106
+ "gitHead": "b6c58f297e8f902f046cc2c71fe7f6162fe0c76d"
106
107
  }
@@ -4,6 +4,7 @@ import { assert, Fail } from '@endo/errors';
4
4
  import { isNat } from '@endo/nat';
5
5
  import { mustMatch, M } from '@endo/patterns';
6
6
  import { importBundle } from '@endo/import-bundle';
7
+ import { objectMetaMap } from '@agoric/internal';
7
8
  import { makeUpgradeDisconnection } from '@agoric/internal/src/upgrade-api.js';
8
9
  import { kser, kslot, makeError } from '@agoric/kmarshal';
9
10
  import { assertKnownOptions } from '../lib/assertOptions.js';
@@ -39,6 +40,7 @@ import { notifyTermination } from './notifyTermination.js';
39
40
  import { makeVatAdminHooks } from './vat-admin-hooks.js';
40
41
 
41
42
  /** @import * as liveslots from '@agoric/swingset-liveslots' */
43
+ /** @import {PolicyInputCleanupCounts} from '../types-external.js' */
42
44
 
43
45
  function abbreviateReplacer(_, arg) {
44
46
  if (typeof arg === 'bigint') {
@@ -388,12 +390,13 @@ export default function buildKernel(
388
390
  * illegalSyscall: { vatID: VatID, info: SwingSetCapData } | undefined,
389
391
  * vatRequestedTermination: { reject: boolean, info: SwingSetCapData } | undefined,
390
392
  * } } DeliveryStatus
393
+ * @import {PolicyInputCleanupCounts} from '../types-external.js';
391
394
  * @typedef { {
392
395
  * abort?: boolean, // changes should be discarded, not committed
393
396
  * consumeMessage?: boolean, // discard the aborted delivery
394
397
  * didDelivery?: VatID, // we made a delivery to a vat, for run policy and save-snapshot
395
398
  * computrons?: bigint, // computron count for run policy
396
- * cleanups?: number, // cleanup budget spent
399
+ * cleanups?: PolicyInputCleanupCounts, // cleanup budget spent
397
400
  * meterID?: string, // deduct those computrons from a meter
398
401
  * measureDirt?: { vatID: VatID, dirt: Dirt }, // dirt counters should increment
399
402
  * terminate?: { vatID: VatID, reject: boolean, info: SwingSetCapData }, // terminate vat, notify vat-admin
@@ -669,17 +672,34 @@ export default function buildKernel(
669
672
  * Perform a small (budget-limited) amount of dead-vat cleanup work.
670
673
  *
671
674
  * @param {RunQueueEventCleanupTerminatedVat} message
672
- * 'message' is the run-queue cleanup action, which includes a vatID and budget.
673
- * A budget of 'undefined' allows unlimited work. Otherwise, the budget is a Number,
674
- * and cleanup should not touch more than maybe 5*budget DB rows.
675
+ * 'message' is the run-queue cleanup action, which includes a
676
+ * vatID and budget. The budget contains work limits for each
677
+ * phase of cleanup (perhaps Infinity to allow unlimited
678
+ * work). Cleanup should not touch more than maybe 5*limit DB
679
+ * rows.
675
680
  * @returns {Promise<CrankResults>}
676
681
  */
677
682
  async function processCleanupTerminatedVat(message) {
678
683
  const { vatID, budget } = message;
679
- const { done, cleanups } = kernelKeeper.cleanupAfterTerminatedVat(
684
+ const { done, work } = kernelKeeper.cleanupAfterTerminatedVat(
680
685
  vatID,
681
686
  budget,
682
687
  );
688
+ const zeroFreeWorkCounts = objectMetaMap(work, desc =>
689
+ desc.value ? desc : undefined,
690
+ );
691
+ kernelSlog.write({ type: 'vat-cleanup', vatID, work: zeroFreeWorkCounts });
692
+
693
+ /** @type {PolicyInputCleanupCounts} */
694
+ const cleanups = {
695
+ total:
696
+ work.exports +
697
+ work.imports +
698
+ work.kv +
699
+ work.snapshots +
700
+ work.transcripts,
701
+ ...work,
702
+ };
683
703
  if (done) {
684
704
  kernelKeeper.forgetTerminatedVat(vatID);
685
705
  kernelSlog.write({ type: 'vat-cleanup-complete', vatID });
@@ -687,8 +707,7 @@ export default function buildKernel(
687
707
  // We don't perform any deliveries here, so there are no computrons to
688
708
  // report, but we do tell the runPolicy know how much kernel-side DB
689
709
  // work we did, so it can decide how much was too much.
690
- const computrons = 0n;
691
- return harden({ computrons, cleanups });
710
+ return harden({ computrons: 0n, cleanups });
692
711
  }
693
712
 
694
713
  /**
@@ -1782,11 +1801,25 @@ export default function buildKernel(
1782
1801
  }
1783
1802
  }
1784
1803
 
1804
+ // match return value of runPolicy.allowCleanup, which is
1805
+ // PolicyOutputCleanupBudget | true | false
1785
1806
  const allowCleanupShape = M.or(
1786
1807
  // 'false' will prohibit cleanup
1787
1808
  false,
1809
+ // 'true' will allow unlimited cleanup
1810
+ true,
1788
1811
  // otherwise allow cleanup, optionally with a limiting budget
1789
- M.splitRecord({}, { budget: M.number() }, M.record()),
1812
+ M.splitRecord(
1813
+ { default: M.number() },
1814
+ {
1815
+ exports: M.number(),
1816
+ imports: M.number(),
1817
+ kv: M.number(),
1818
+ snapshots: M.number(),
1819
+ transcripts: M.number(),
1820
+ },
1821
+ M.record(),
1822
+ ),
1790
1823
  );
1791
1824
 
1792
1825
  /**
@@ -1808,7 +1841,7 @@ export default function buildKernel(
1808
1841
  };
1809
1842
  }
1810
1843
  // Absent specific configuration, allow unlimited cleanup.
1811
- const allowCleanup = policy?.allowCleanup?.() ?? {};
1844
+ const allowCleanup = policy?.allowCleanup?.() ?? true;
1812
1845
  mustMatch(harden(allowCleanup), allowCleanupShape);
1813
1846
 
1814
1847
  const message =
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable no-use-before-define */
2
2
  import { Nat, isNat } from '@endo/nat';
3
3
  import { assert, Fail } from '@endo/errors';
4
- import { objectMetaMap } from '@agoric/internal';
5
4
  import {
6
5
  initializeVatState,
7
6
  makeVatKeeper,
@@ -46,6 +45,8 @@ const enableKernelGC = true;
46
45
  * @typedef { import('../../types-external.js').VatKeeper } VatKeeper
47
46
  * @typedef { import('../../types-internal.js').InternalKernelOptions } InternalKernelOptions
48
47
  * @typedef { import('../../types-internal.js').ReapDirtThreshold } ReapDirtThreshold
48
+ * @import {CleanupBudget, CleanupWork, PolicyOutputCleanupBudget} from '../../types-external.js';
49
+ * @import {RunQueueEventCleanupTerminatedVat} from '../../types-internal.js';
49
50
  */
50
51
 
51
52
  export { DEFAULT_REAP_DIRT_THRESHOLD_KEY };
@@ -946,11 +947,11 @@ export default function makeKernelKeeper(
946
947
  * (so the runPolicy can decide when to stop).
947
948
  *
948
949
  * @param {string} vatID
949
- * @param {number} [budget]
950
- * @returns {{ done: boolean, cleanups: number }}
950
+ * @param {CleanupBudget} budget
951
+ * @returns {{ done: boolean, work: CleanupWork }}
951
952
  *
952
953
  */
953
- function cleanupAfterTerminatedVat(vatID, budget = undefined) {
954
+ function cleanupAfterTerminatedVat(vatID, budget) {
954
955
  // this is called from terminateVat, which is called from either:
955
956
  // * end of processDeliveryMessage, if crankResults.terminate
956
957
  // * device-vat-admin (when vat-v-a does adminNode.terminateVat)
@@ -960,7 +961,6 @@ export default function makeKernelKeeper(
960
961
  // crankResults.terminate
961
962
 
962
963
  insistVatID(vatID);
963
- let cleanups = 0;
964
964
  const work = {
965
965
  exports: 0,
966
966
  imports: 0,
@@ -968,20 +968,7 @@ export default function makeKernelKeeper(
968
968
  snapshots: 0,
969
969
  transcripts: 0,
970
970
  };
971
- let spend = _did => false; // returns "stop now"
972
- if (budget !== undefined) {
973
- assert.typeof(budget, 'number');
974
- spend = (did = 1) => {
975
- assert(budget !== undefined); // hush TSC
976
- cleanups += did;
977
- budget -= did;
978
- return budget <= 0;
979
- };
980
- }
981
- const logWork = () => {
982
- const w = objectMetaMap(work, desc => (desc.value ? desc : undefined));
983
- kernelSlog?.write({ type: 'vat-cleanup', vatID, work: w });
984
- };
971
+ let remaining = 0;
985
972
 
986
973
  // TODO: it would be slightly cheaper to walk all kvStore keys in
987
974
  // order, and act on each one according to its category (c-list
@@ -1016,6 +1003,7 @@ export default function makeKernelKeeper(
1016
1003
  // used.
1017
1004
 
1018
1005
  // first, scan for exported objects, which must be orphaned
1006
+ remaining = budget.exports ?? budget.default;
1019
1007
  for (const k of enumeratePrefixedKeys(kvStore, exportPrefix)) {
1020
1008
  // The void for an object exported by a vat will always be of the form
1021
1009
  // `o+NN`. The '+' means that the vat exported the object (rather than
@@ -1030,13 +1018,14 @@ export default function makeKernelKeeper(
1030
1018
  // note: adds to maybeFreeKrefs, deletes c-list and .owner
1031
1019
  orphanKernelObject(kref, vatID);
1032
1020
  work.exports += 1;
1033
- if (spend()) {
1034
- logWork();
1035
- return { done: false, cleanups };
1021
+ remaining -= 1;
1022
+ if (remaining <= 0) {
1023
+ return { done: false, work };
1036
1024
  }
1037
1025
  }
1038
1026
 
1039
1027
  // then scan for imported objects, which must be decrefed
1028
+ remaining = budget.imports ?? budget.default;
1040
1029
  for (const k of enumeratePrefixedKeys(kvStore, importPrefix)) {
1041
1030
  // abandoned imports: delete the clist entry as if the vat did a
1042
1031
  // drop+retire
@@ -1045,9 +1034,9 @@ export default function makeKernelKeeper(
1045
1034
  vatKeeper.deleteCListEntry(kref, vref);
1046
1035
  // that will also delete both db keys
1047
1036
  work.imports += 1;
1048
- if (spend()) {
1049
- logWork();
1050
- return { done: false, cleanups };
1037
+ remaining -= 1;
1038
+ if (remaining <= 0) {
1039
+ return { done: false, work };
1051
1040
  }
1052
1041
  }
1053
1042
 
@@ -1055,30 +1044,32 @@ export default function makeKernelKeeper(
1055
1044
  // so they already know the orphaned promises to reject
1056
1045
 
1057
1046
  // now loop back through everything and delete it all
1047
+ remaining = budget.kv ?? budget.default;
1058
1048
  for (const k of enumeratePrefixedKeys(kvStore, `${vatID}.`)) {
1059
1049
  kvStore.delete(k);
1060
1050
  work.kv += 1;
1061
- if (spend()) {
1062
- logWork();
1063
- return { done: false, cleanups };
1051
+ remaining -= 1;
1052
+ if (remaining <= 0) {
1053
+ return { done: false, work };
1064
1054
  }
1065
1055
  }
1066
1056
 
1067
1057
  // this will internally loop through 'budget' deletions
1068
- const dsc = vatKeeper.deleteSnapshots(budget);
1058
+ remaining = budget.snapshots ?? budget.default;
1059
+ const dsc = vatKeeper.deleteSnapshots(remaining);
1069
1060
  work.snapshots += dsc.cleanups;
1070
- if (spend(dsc.cleanups)) {
1071
- logWork();
1072
- return { done: false, cleanups };
1061
+ remaining -= dsc.cleanups;
1062
+ if (remaining <= 0) {
1063
+ return { done: false, work };
1073
1064
  }
1074
1065
 
1075
1066
  // same
1076
- const dts = vatKeeper.deleteTranscripts(budget);
1067
+ remaining = budget.transcripts ?? budget.default;
1068
+ const dts = vatKeeper.deleteTranscripts(remaining);
1077
1069
  work.transcripts += dts.cleanups;
1070
+ remaining -= dts.cleanups;
1078
1071
  // last task, so increment cleanups, but dc.done is authoritative
1079
- spend(dts.cleanups);
1080
- logWork();
1081
- return { done: dts.done, cleanups };
1072
+ return { done: dts.done, work };
1082
1073
  }
1083
1074
 
1084
1075
  function deleteVatID(vatID) {
@@ -1385,20 +1376,23 @@ export default function makeKernelKeeper(
1385
1376
  kvStore.set(`vats.terminated`, JSON.stringify(terminatedVats));
1386
1377
  }
1387
1378
 
1379
+ /**
1380
+ * @param {PolicyOutputCleanupBudget} allowCleanup
1381
+ * @returns {RunQueueEventCleanupTerminatedVat | undefined}
1382
+ */
1388
1383
  function nextCleanupTerminatedVatAction(allowCleanup) {
1389
- if (!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
+ }
1390
1394
  return undefined;
1391
1395
  }
1392
- // budget === undefined means "unlimited"
1393
- const { budget } = allowCleanup;
1394
- // if (getGCActions().size) {
1395
- // return undefined;
1396
- // }
1397
- const vatID = getFirstTerminatedVat();
1398
- if (vatID) {
1399
- return { type: 'cleanup-terminated-vat', vatID, budget };
1400
- }
1401
- return undefined;
1402
1396
  }
1403
1397
 
1404
1398
  // As refcounts are decremented, we accumulate a set of krefs for which
@@ -4,7 +4,7 @@ export function foreverPolicy() {
4
4
  /** @type { RunPolicy } */
5
5
  return harden({
6
6
  allowCleanup() {
7
- return {}; // unlimited budget
7
+ return true; // unlimited budget
8
8
  },
9
9
  vatCreated(_details) {
10
10
  return true;
@@ -31,7 +31,7 @@ export function crankCounter(
31
31
  /** @type { RunPolicy } */
32
32
  const policy = harden({
33
33
  allowCleanup() {
34
- return { budget: 100 }; // limited budget
34
+ return { default: 100 }; // limited budget
35
35
  },
36
36
  vatCreated() {
37
37
  vats += 1;
@@ -64,7 +64,7 @@ export function computronCounter(limit, options = {}) {
64
64
  /** @type { RunPolicy } */
65
65
  const policy = harden({
66
66
  allowCleanup() {
67
- return { budget: cleanupBudget }; // limited budget
67
+ return { default: cleanupBudget }; // limited budget
68
68
  },
69
69
  vatCreated() {
70
70
  total += vatCreatedComputrons;
@@ -93,7 +93,7 @@ export function wallClockWaiter(seconds) {
93
93
  const timeout = Date.now() + 1000 * seconds;
94
94
  /** @type { RunPolicy } */
95
95
  const policy = harden({
96
- allowCleanup: () => ({}), // unlimited budget
96
+ allowCleanup: () => true, // unlimited budget
97
97
  vatCreated: () => Date.now() < timeout,
98
98
  crankComplete: () => Date.now() < timeout,
99
99
  crankFailed: () => Date.now() < timeout,
@@ -121,7 +121,7 @@ export function someCleanup(budget) {
121
121
  allowCleanup: () => {
122
122
  if (once) {
123
123
  once = false;
124
- return { budget };
124
+ return { default: budget };
125
125
  }
126
126
  return false;
127
127
  },
@@ -219,24 +219,65 @@ export {};
219
219
  */
220
220
 
221
221
  /**
222
+ * PolicyInput is used internally within kernel.js, returned by each
223
+ * message processor, and used to decide which of the host's runPolicy
224
+ * methods to invoke. The 'details' portion of PolicyInput is passed
225
+ * as an argument to those methods, so those types are
226
+ * externally-visible.
227
+ *
228
+ * @typedef { { exports: number,
229
+ * imports: number,
230
+ * kv: number,
231
+ * snapshots: number,
232
+ * transcripts: number,
233
+ * } } CleanupWork
234
+ *
235
+ * @typedef { { total: number } & CleanupWork } PolicyInputCleanupCounts
236
+ * @typedef { { cleanups: PolicyInputCleanupCounts, computrons?: bigint } } PolicyInputCleanupDetails
222
237
  * @typedef { { computrons?: bigint } } PolicyInputDetails
238
+ *
223
239
  * @typedef { [tag: 'none', details: PolicyInputDetails ] } PolicyInputNone
224
240
  * @typedef { [tag: 'create-vat', details: PolicyInputDetails ]} PolicyInputCreateVat
225
241
  * @typedef { [tag: 'crank', details: PolicyInputDetails ] } PolicyInputCrankComplete
226
242
  * @typedef { [tag: 'crank-failed', details: PolicyInputDetails ]} PolicyInputCrankFailed
227
- * @typedef { [tag: 'cleanup', details: { cleanups: number, computrons?: number }] } PolicyInputCleanup
243
+ * @typedef { [tag: 'cleanup', details: PolicyInputCleanupDetails] } PolicyInputCleanup
244
+ *
228
245
  * @typedef { PolicyInputNone | PolicyInputCreateVat | PolicyInputCrankComplete |
229
246
  * PolicyInputCrankFailed | PolicyInputCleanup } PolicyInput
230
- * @typedef { boolean } PolicyOutput
247
+ *
248
+ * CleanupBudget is the internal record used to limit the slow
249
+ * deletion of terminated vats. Each property limits the number of
250
+ * deletions per 'cleanup-terminated-vat' run-queue event, for a
251
+ * specific phase (imports, exports, snapshots, etc). It must always
252
+ * have a 'default' property, which is used for phases that aren't
253
+ * otherwise specified.
254
+ *
255
+ * @typedef { { default: number } & Partial<CleanupWork> } CleanupBudget
256
+ *
257
+ * PolicyOutputCleanupBudget is the return value of
258
+ * runPolicy.allowCleanup(), and tells the kernel how much it is
259
+ * allowed to clean up. It is either a CleanupBudget, or 'true' to
260
+ * allow unlimited cleanup, or 'false' to forbid any cleanup.
261
+ *
262
+ * @typedef {CleanupBudget | true | false} PolicyOutputCleanupBudget
263
+ *
264
+ * PolicyOutput is the boolean returned by all the other runPolicy
265
+ * methods, where 'true' means "keep going", and 'false' means "stop
266
+ * now".
267
+ *
268
+ * @typedef {boolean} PolicyOutput
269
+ *
231
270
  * @typedef { {
232
- * allowCleanup?: () => false | { budget?: number },
271
+ * allowCleanup?: () => boolean | PolicyOutputCleanupBudget,
233
272
  * vatCreated: (details: {}) => PolicyOutput,
234
273
  * crankComplete: (details: { computrons?: bigint }) => PolicyOutput,
235
274
  * crankFailed: (details: {}) => PolicyOutput,
236
275
  * emptyCrank: () => PolicyOutput,
237
- * didCleanup?: (details: { cleanups: number }) => PolicyOutput,
276
+ * didCleanup?: (details: PolicyInputCleanupDetails) => PolicyOutput,
238
277
  * } } RunPolicy
239
- *
278
+ */
279
+
280
+ /**
240
281
  * @typedef {object} VatWarehousePolicy
241
282
  * @property { number } [maxVatsOnline] Limit the number of simultaneous workers
242
283
  * @property { boolean } [restartWorkerOnSnapshot] Reload worker immediately upon snapshot creation
@@ -143,8 +143,9 @@ export {};
143
143
  * @typedef { { type: 'retireImports', vatID: VatID, krefs: string[] } } RunQueueEventRetireImports
144
144
  * @typedef { { type: 'negated-gc-action', vatID?: VatID } } RunQueueEventNegatedGCAction
145
145
  * @typedef { { type: 'bringOutYourDead', vatID: VatID } } RunQueueEventBringOutYourDead
146
+ * @import {CleanupBudget} from './types-external.js';
146
147
  * @typedef { { type: 'cleanup-terminated-vat', vatID: VatID,
147
- * budget: number | undefined } } RunQueueEventCleanupTerminatedVat
148
+ * budget: CleanupBudget } } RunQueueEventCleanupTerminatedVat
148
149
  * @typedef { RunQueueEventNotify | RunQueueEventSend | RunQueueEventCreateVat |
149
150
  * RunQueueEventUpgradeVat | RunQueueEventChangeVatOptions | RunQueueEventStartVat |
150
151
  * RunQueueEventDropExports | RunQueueEventRetireExports | RunQueueEventRetireImports |