@agoric/swingset-vat 0.33.0-u19.2 → 0.33.0-u21.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.
@@ -1,5 +1,6 @@
1
1
  import { Nat, isNat } from '@endo/nat';
2
2
  import { assert, Fail } from '@endo/errors';
3
+ import { KERNEL_STATS_METRICS } from '@agoric/internal/src/metrics.js';
3
4
  import { naturalCompare } from '@agoric/internal/src/natural-sort.js';
4
5
  import { makeDummySlogger, noopConsole } from '../slogger.js';
5
6
  import {
@@ -25,7 +26,6 @@ import {
25
26
  makeUpgradeID,
26
27
  } from '../../lib/id.js';
27
28
  import { kdebug } from '../../lib/kdebug.js';
28
- import { KERNEL_STATS_METRICS } from '../metrics.js';
29
29
  import { makeKernelStats } from './stats.js';
30
30
  import {
31
31
  enumeratePrefixedKeys,
@@ -212,9 +212,8 @@ function insistMeterID(m) {
212
212
  export const getAllStaticVats = kvStore => {
213
213
  const result = [];
214
214
  const prefix = 'vat.name.';
215
- for (const k of enumeratePrefixedKeys(kvStore, prefix)) {
216
- const vatID = kvStore.get(k) || Fail`getNextKey ensures get`;
217
- const name = k.slice(prefix.length);
215
+ for (const { key, suffix: name } of enumeratePrefixedKeys(kvStore, prefix)) {
216
+ const vatID = kvStore.get(key) || Fail`getNextKey ensures get`;
218
217
  result.push([name, vatID]);
219
218
  }
220
219
  return result;
@@ -718,17 +717,22 @@ export default function makeKernelKeeper(
718
717
  // We iterate through all ephemeral and virtual entries so the kernel
719
718
  // can ensure that they are abandoned by a vat being upgraded.
720
719
  const prefix = `${vatID}.c.`;
721
- const ephStart = `${prefix}o+`;
722
- const durStart = `${prefix}o+d`;
723
- const virStart = `${prefix}o+v`;
720
+ const ephStart = `o+`;
721
+ const durStart = `o+d`;
722
+ const virStart = `o+v`;
724
723
  /** @type {[string, string?][]} */
725
724
  const ranges = [[ephStart, durStart], [virStart]];
726
725
  for (const range of ranges) {
727
- for (const k of enumeratePrefixedKeys(kvStore, ...range)) {
728
- const vref = k.slice(prefix.length);
726
+ const rangeSuffix = range[0];
727
+ const args = /** @type {typeof ranges[0]} */ (
728
+ /** @type {unknown} */ (range.map(s => `${prefix}${s}`))
729
+ );
730
+ const prefixedKeys = enumeratePrefixedKeys(kvStore, ...args);
731
+ for (const { key, suffix } of prefixedKeys) {
732
+ const vref = `${rangeSuffix}${suffix}`;
729
733
  // exclude the root object, which is replaced by upgrade
730
734
  if (vref !== 'o+0') {
731
- const kref = kvStore.get(k);
735
+ const kref = kvStore.get(key);
732
736
  assert.typeof(kref, 'string');
733
737
  yield { kref, vref };
734
738
  }
@@ -1072,7 +1076,7 @@ export default function makeKernelKeeper(
1072
1076
 
1073
1077
  // first, scan for exported objects, which must be orphaned
1074
1078
  remaining = budget.exports ?? budget.default;
1075
- for (const k of enumeratePrefixedKeys(kvStore, exportPrefix)) {
1079
+ for (const { key } of enumeratePrefixedKeys(kvStore, exportPrefix)) {
1076
1080
  // The void for an object exported by a vat will always be of the form
1077
1081
  // `o+NN`. The '+' means that the vat exported the object (rather than
1078
1082
  // importing it) and therefore the object is owned by (i.e., within) the
@@ -1080,9 +1084,7 @@ export default function makeKernelKeeper(
1080
1084
  // begin with `vMM.c.o+`. In addition to deleting the c-list entry, we
1081
1085
  // must also delete the corresponding kernel owner entry for the object,
1082
1086
  // since the object will no longer be accessible.
1083
- const vref = stripPrefix(clistPrefix, k);
1084
- assert(vref.startsWith('o+'), vref);
1085
- const kref = kvStore.get(k);
1087
+ const kref = kvStore.get(key);
1086
1088
  // note: adds to maybeFreeKrefs, deletes c-list and .owner
1087
1089
  orphanKernelObject(kref, vatID);
1088
1090
  work.exports += 1;
@@ -1094,11 +1096,14 @@ export default function makeKernelKeeper(
1094
1096
 
1095
1097
  // then scan for imported objects, which must be decrefed
1096
1098
  remaining = budget.imports ?? budget.default;
1097
- for (const k of enumeratePrefixedKeys(kvStore, importPrefix)) {
1099
+ for (const { key, suffix } of enumeratePrefixedKeys(
1100
+ kvStore,
1101
+ importPrefix,
1102
+ )) {
1098
1103
  // abandoned imports: delete the clist entry as if the vat did a
1099
1104
  // drop+retire
1100
- const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
1101
- const vref = stripPrefix(clistPrefix, k);
1105
+ const kref = kvStore.get(key) || Fail`getNextKey ensures get`;
1106
+ const vref = `o-${suffix}`;
1102
1107
  undertaker.deleteCListEntry(kref, vref);
1103
1108
  // that will also delete both db keys
1104
1109
  work.imports += 1;
@@ -1113,9 +1118,12 @@ export default function makeKernelKeeper(
1113
1118
  // kpids are still present in the dead vat's c-list. Clean those
1114
1119
  // up now.
1115
1120
  remaining = budget.promises ?? budget.default;
1116
- for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
1117
- const kref = kvStore.get(k) || Fail`getNextKey ensures get`;
1118
- const vref = stripPrefix(clistPrefix, k);
1121
+ for (const { key, suffix } of enumeratePrefixedKeys(
1122
+ kvStore,
1123
+ promisePrefix,
1124
+ )) {
1125
+ const kref = kvStore.get(key) || Fail`getNextKey ensures get`;
1126
+ const vref = `p${suffix}`;
1119
1127
  undertaker.deleteCListEntry(kref, vref);
1120
1128
  // that will also delete both db keys
1121
1129
  work.promises += 1;
@@ -1127,8 +1135,8 @@ export default function makeKernelKeeper(
1127
1135
 
1128
1136
  // now loop back through everything and delete it all
1129
1137
  remaining = budget.kv ?? budget.default;
1130
- for (const k of enumeratePrefixedKeys(kvStore, `${vatID}.`)) {
1131
- kvStore.delete(k);
1138
+ for (const { key } of enumeratePrefixedKeys(kvStore, `${vatID}.`)) {
1139
+ kvStore.delete(key);
1132
1140
  work.kv += 1;
1133
1141
  remaining -= 1;
1134
1142
  if (remaining <= 0) {
@@ -1167,11 +1175,11 @@ export default function makeKernelKeeper(
1167
1175
  kvStore.set(DYNAMIC_IDS_KEY, JSON.stringify(newDynamicVatIDs));
1168
1176
  } else {
1169
1177
  kdebug(`removing static vat ${vatID}`);
1170
- for (const k of enumeratePrefixedKeys(kvStore, 'vat.name.')) {
1171
- if (kvStore.get(k) === vatID) {
1172
- kvStore.delete(k);
1178
+ const prefixedKeys = enumeratePrefixedKeys(kvStore, 'vat.name.');
1179
+ for (const { key, suffix: name } of prefixedKeys) {
1180
+ if (kvStore.get(key) === vatID) {
1181
+ kvStore.delete(key);
1173
1182
  const VAT_NAMES_KEY = 'vat.names';
1174
- const name = k.slice('vat.name.'.length);
1175
1183
  const oldStaticVatNames = JSON.parse(getRequired(VAT_NAMES_KEY));
1176
1184
  const newStaticVatNames = oldStaticVatNames.filter(v => v !== name);
1177
1185
  kvStore.set(VAT_NAMES_KEY, JSON.stringify(newStaticVatNames));
@@ -1240,7 +1248,7 @@ export default function makeKernelKeeper(
1240
1248
  function* enumeratePromisesByDecider(vatID) {
1241
1249
  insistVatID(vatID);
1242
1250
  const promisePrefix = `${vatID}.c.p`;
1243
- for (const k of enumeratePrefixedKeys(kvStore, promisePrefix)) {
1251
+ for (const { key } of enumeratePrefixedKeys(kvStore, promisePrefix)) {
1244
1252
  // The vpid for a promise imported or exported by a vat (and thus
1245
1253
  // potentially a promise for which the vat *might* be the decider) will
1246
1254
  // always be of the form `p+NN` or `p-NN`. The corresponding vpid->kpid
@@ -1250,7 +1258,7 @@ export default function makeKernelKeeper(
1250
1258
  // whether the vat is the decider or not. If it is, we add the promise
1251
1259
  // to the list of promises that must be rejected because the dead vat
1252
1260
  // will never be able to act upon them.
1253
- const kpid = getRequired(k);
1261
+ const kpid = getRequired(key);
1254
1262
  const p = getKernelPromise(kpid);
1255
1263
  if (p.state === 'unresolved' && p.decider === vatID) {
1256
1264
  yield [kpid, p];
@@ -1449,9 +1457,9 @@ export default function makeKernelKeeper(
1449
1457
 
1450
1458
  function getDevices() {
1451
1459
  const result = [];
1452
- for (const k of enumeratePrefixedKeys(kvStore, 'device.name.')) {
1453
- const name = k.slice(12);
1454
- const deviceID = kvStore.get(k) || Fail`getNextKey ensures get`;
1460
+ const prefixedKeys = enumeratePrefixedKeys(kvStore, 'device.name.');
1461
+ for (const { key, suffix: name } of prefixedKeys) {
1462
+ const deviceID = kvStore.get(key) || Fail`getNextKey ensures get`;
1455
1463
  result.push([name, deviceID]);
1456
1464
  }
1457
1465
  return result;
@@ -70,10 +70,9 @@ export const makeKernelStats = kernelStatsMetrics => {
70
70
  Object.freeze(allStatsKeys);
71
71
 
72
72
  const pickStats = (stat, gauge = false) => {
73
- assert(
74
- kernelConsensusStats && kernelLocalStats,
75
- 'Kernel stats not initialized',
76
- );
73
+ if (!kernelConsensusStats || !kernelLocalStats) {
74
+ throw Fail`Kernel stats not initialized`;
75
+ }
77
76
  const metricType = allStatsKeys[stat];
78
77
  if (gauge) {
79
78
  metricType === 'gauge' || Fail`Invalid kernel gauge stat ${stat}`;
@@ -116,7 +115,12 @@ export const makeKernelStats = kernelStatsMetrics => {
116
115
  kernelStats[downStat] += delta;
117
116
  };
118
117
 
119
- /** @param {boolean | undefined} [consensusOnly] */
118
+ /**
119
+ * Return a fresh snapshot, with or without local stats.
120
+ *
121
+ * @param {boolean | undefined} [consensusOnly]
122
+ * @returns {Record<string, number>}
123
+ */
120
124
  const getStats = consensusOnly => {
121
125
  return {
122
126
  ...(consensusOnly ? {} : kernelLocalStats),
@@ -130,10 +134,9 @@ export const makeKernelStats = kernelStatsMetrics => {
130
134
  };
131
135
 
132
136
  const getSerializedStats = () => {
133
- assert(
134
- kernelConsensusStats && kernelLocalStats,
135
- 'Kernel stats not initialized',
136
- );
137
+ if (!kernelConsensusStats || !kernelLocalStats) {
138
+ throw Fail`Kernel stats not initialized`;
139
+ }
137
140
 
138
141
  return {
139
142
  consensusStats: JSON.stringify(kernelConsensusStats),
@@ -2,6 +2,10 @@
2
2
 
3
3
  import { Fail } from '@endo/errors';
4
4
 
5
+ /**
6
+ * @import {KVStore} from '@agoric/swing-store';
7
+ */
8
+
5
9
  /**
6
10
  * Iterate over keys with a given prefix, in lexicographic order,
7
11
  * excluding an exact match of the prefix.
@@ -9,7 +13,8 @@ import { Fail } from '@endo/errors';
9
13
  * @param {KVStore} kvStore
10
14
  * @param {string} prefix
11
15
  * @param {string} [exclusiveEnd]
12
- * @yields {string} the next key with the prefix that is not >= exclusiveEnd
16
+ * @yields {{key: string; suffix: string}} the next `key` with the prefix that is not >= exclusiveEnd
17
+ * and the `suffix` which is obtained by stripping the supplied prefix from the key
13
18
  */
14
19
  export function* enumeratePrefixedKeys(kvStore, prefix, exclusiveEnd) {
15
20
  /** @type {string | undefined} */
@@ -22,7 +27,7 @@ export function* enumeratePrefixedKeys(kvStore, prefix, exclusiveEnd) {
22
27
  if (exclusiveEnd && key >= exclusiveEnd) {
23
28
  break;
24
29
  }
25
- yield key;
30
+ yield { key, suffix: key.slice(prefix.length) };
26
31
  }
27
32
  }
28
33
  harden(enumeratePrefixedKeys);
@@ -761,11 +761,13 @@ export function makeVatKeeper(
761
761
  function dumpState() {
762
762
  const res = [];
763
763
  const prefix = `${vatID}.c.`;
764
- for (const k of enumeratePrefixedKeys(kvStore, prefix)) {
765
- const slot = k.slice(prefix.length);
764
+ for (const { key, suffix: slot } of enumeratePrefixedKeys(
765
+ kvStore,
766
+ prefix,
767
+ )) {
766
768
  if (!slot.startsWith('k')) {
767
769
  const vatSlot = slot;
768
- const kernelSlot = kvStore.get(k) || Fail`getNextKey ensures get`;
770
+ const kernelSlot = kvStore.get(key) || Fail`getNextKey ensures get`;
769
771
  /** @type { [string, string, string] } */
770
772
  const item = [kernelSlot, vatID, vatSlot];
771
773
  res.push(item);
@@ -299,7 +299,10 @@ export function makeVatWarehouse({
299
299
  */
300
300
  async function replayTranscript(vatID, vatKeeper, manager) {
301
301
  const total = vatKeeper.transcriptSize();
302
- kernelSlog.write({ type: 'start-replay', vatID, deliveries: total });
302
+ const finish = kernelSlog.startDuration(['start-replay', 'finish-replay'], {
303
+ vatID,
304
+ deliveries: total,
305
+ });
303
306
  let first = true;
304
307
  for await (const [deliveryNum, te] of vatKeeper.getTranscript()) {
305
308
  // if (deliveryNum % 100 === 0) {
@@ -326,7 +329,7 @@ export function makeVatWarehouse({
326
329
  finishSlog(status);
327
330
  sim.finishSimulation(); // will throw if syscalls did not match
328
331
  }
329
- kernelSlog.write({ type: 'finish-replay', vatID });
332
+ finish({ deliveries: undefined });
330
333
  }
331
334
 
332
335
  /**
@@ -563,18 +566,20 @@ export function makeVatWarehouse({
563
566
 
564
567
  /**
565
568
  * Save a heap snapshot for the given vatID, if the snapshotInterval
566
- * is satisified
569
+ * is satisfied or a lower explicit delivery count has been reached.
567
570
  *
568
571
  * @param {VatID} vatID
572
+ * @param {number} [minDeliveryCount]
569
573
  */
570
- async function maybeSaveSnapshot(vatID) {
571
- const recreate = true; // PANIC in the failure case
572
- const { manager } = await ensureVatOnline(vatID, recreate);
573
- if (!manager.makeSnapshot) {
574
- return false; // worker cannot make snapshots
574
+ async function maybeSaveSnapshot(vatID, minDeliveryCount = snapshotInterval) {
575
+ kernelKeeper.vatIsAlive(vatID) || Fail`${q(vatID)}: not alive`;
576
+ const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
577
+ const vatOptions = vatKeeper.getOptions();
578
+
579
+ if (!vatOptions.useTranscript) {
580
+ return false;
575
581
  }
576
582
 
577
- const vatKeeper = kernelKeeper.provideVatKeeper(vatID);
578
583
  let reason;
579
584
 
580
585
  const hasSnapshot = !!vatKeeper.getSnapshotInfo();
@@ -583,15 +588,21 @@ export function makeVatWarehouse({
583
588
  if (!hasSnapshot && deliveriesInSpan >= snapshotInitial) {
584
589
  // begin snapshot after 'snapshotInitial' deliveries in an incarnation
585
590
  reason = { snapshotInitial };
586
- } else if (deliveriesInSpan >= snapshotInterval) {
591
+ } else if (deliveriesInSpan >= minDeliveryCount) {
587
592
  // begin snapshot after 'snapshotInterval' deliveries in a span
588
- reason = { snapshotInterval };
593
+ reason = { snapshotInterval: minDeliveryCount };
589
594
  }
590
595
  // console.log('maybeSaveSnapshot: reason:', reason);
591
596
  if (!reason) {
592
597
  return false; // not time to make a snapshot
593
598
  }
594
599
 
600
+ const recreate = true; // PANIC in the failure case
601
+ const { manager } = await ensureVatOnline(vatID, recreate);
602
+ if (!manager.makeSnapshot) {
603
+ return false; // worker cannot make snapshots
604
+ }
605
+
595
606
  // always do a bringOutYourDead just before a snapshot, to shake
596
607
  // loose as much garbage as we can, and to minimize the GC
597
608
  // sensitivity effects of the forced GC that snapshots perform
@@ -1,24 +1,11 @@
1
- import { Fail } from '@endo/errors';
1
+ import { krefOf, kunser } from '@agoric/kmarshal';
2
2
  import { passStyleOf } from '@endo/far';
3
- import { kunser, krefOf } from '@agoric/kmarshal';
4
3
 
5
4
  /**
6
- * Assert function to ensure that something expected to be a capdata object
7
- * actually is. A capdata object should have a .body property that's a string
8
- * and a .slots property that's an array of strings.
9
- *
10
- * @param {any} capdata The object to be tested
11
- * @throws {Error} if, upon inspection, the parameter does not satisfy the above
12
- * criteria.
13
- * @returns {asserts capdata is import('@endo/marshal').CapData<string>}
5
+ * @import {CapData} from '@endo/marshal';
14
6
  */
15
- export function insistCapData(capdata) {
16
- typeof capdata.body === 'string' ||
17
- Fail`capdata has non-string .body ${capdata.body}`;
18
- Array.isArray(capdata.slots) ||
19
- Fail`capdata has non-Array slots ${capdata.slots}`;
20
- // TODO check that the .slots array elements are actually strings
21
- }
7
+
8
+ export { insistCapData } from '@agoric/swingset-liveslots/src/capdata.js';
22
9
 
23
10
  /**
24
11
  * Returns the slot of a presence if the provided capdata is composed
@@ -1,6 +1,10 @@
1
1
  import { assert, Fail } from '@endo/errors';
2
2
  import { insistCapData } from './capdata.js';
3
3
 
4
+ /**
5
+ * @import {VatSyscallObject, VatSyscallResult, VatDeliveryResult} from '@agoric/swingset-liveslots';
6
+ */
7
+
4
8
  /**
5
9
  * Assert function to ensure that something expected to be a message object
6
10
  * actually is. A message object should have a .method property that's a
@@ -5,6 +5,7 @@ export {};
5
5
  * @import {ERef} from '@endo/far';
6
6
  * @import {Passable, RemotableObject} from '@endo/pass-style';
7
7
  * @import {LimitedConsole} from '@agoric/internal/src/js-utils.js';
8
+ * @import {SlogProps, SlogDurationProps} from './controller/controller.js';
8
9
  */
9
10
 
10
11
  /* This file defines types that part of the external API of swingset. That
@@ -12,19 +13,36 @@ export {};
12
13
  * with, like VatAdminService. */
13
14
 
14
15
  /**
15
- * @typedef {'getExport' | 'nestedEvaluate' | 'endoZipBase64'} BundleFormat
16
+ * @template T
17
+ * @typedef {'Device' & { __deviceType__: T }} Device
18
+ */
19
+
20
+ /** @typedef {<T>(target: Device<T>) => T} DProxy (approximately) */
21
+
22
+ /**
23
+ * @typedef {(extraProps?: SlogDurationProps) => void} FinishSlogDuration
16
24
  */
17
25
 
18
26
  /**
19
27
  * @typedef {import('@endo/marshal').CapData<string>} SwingSetCapData
20
28
  */
21
29
 
30
+ // TODO move Bundle types into Endo
22
31
  /**
32
+ * @typedef {'getExport' | 'nestedEvaluate' | 'endoZipBase64'} BundleFormat
23
33
  * @typedef { { moduleFormat: 'getExport', source: string, sourceMap?: string } } GetExportBundle
24
34
  * @typedef { { moduleFormat: 'nestedEvaluate', source: string, sourceMap?: string } } NestedEvaluateBundle
25
- * @typedef { EndoZipBase64Bundle | GetExportBundle | NestedEvaluateBundle } Bundle
26
- *
35
+ * @typedef { { moduleFormat: 'test', [x: symbol]: Record<PropertyKey, unknown> } } TestBundle
36
+ * @typedef { EndoZipBase64Bundle | GetExportBundle | NestedEvaluateBundle | TestBundle} Bundle
37
+ */
38
+
39
+ /**
27
40
  * @typedef { 'local' | 'node-subprocess' | 'xsnap' | 'xs-worker' } ManagerType
41
+ * The type of worker for hosting a vat.
42
+ * - **local**: a Compartment in the SwingSet Node.js process
43
+ * - **node-subprocess**: a child process using the same Node.js executable
44
+ * (`process.execPath`)
45
+ * - **xsnap** or **xs-worker**: an {@link @agoric/xsnap! @agoric/xsnap} worker
28
46
  */
29
47
 
30
48
  /**
@@ -128,7 +146,9 @@ export {};
128
146
  * @typedef { Awaited<ReturnType<typeof import('@agoric/xsnap').xsnap>> } XSnap
129
147
  * @typedef { (dr: VatDeliveryResult) => void } SlogFinishDelivery
130
148
  * @typedef { (ksr: KernelSyscallResult, vsr: VatSyscallResult) => void } SlogFinishSyscall
131
- * @typedef { { write: ({}) => void,
149
+ * @typedef { { write: (obj: SlogProps) => void,
150
+ * startDuration: (labels: readonly [startLabel: string, endLabel: string],
151
+ * startProps: SlogDurationProps) => FinishSlogDuration,
132
152
  * provideVatSlogger: (vatID: string,
133
153
  * dynamic?: boolean,
134
154
  * description?: string,
@@ -382,7 +402,9 @@ export {};
382
402
  *
383
403
  * @typedef { { vatParameters?: object, upgradeMessage?: string } } VatUpgradeOptions
384
404
  * @typedef { { incarnationNumber: number } } VatUpgradeResults
385
- *
405
+ */
406
+
407
+ /**
386
408
  * @callback ShutdownWithFailure
387
409
  * Called to shut something down because something went wrong, where the reason
388
410
  * is supposed to be an Error that describes what went wrong. Some valid
@@ -393,7 +415,9 @@ export {};
393
415
  *
394
416
  * @param {Error} reason
395
417
  * @returns {void}
396
- *
418
+ */
419
+
420
+ /**
397
421
  * @typedef {object} VatAdminFacet
398
422
  * A powerful object corresponding with a vat
399
423
  * that can be used to upgrade it with new code or parameters,
@@ -411,8 +435,9 @@ export {};
411
435
  * in which the JS memory space is abandoned. The new image is launched with access to 'baggage'
412
436
  * and any durable storage reachable from it, and must fulfill all the obligations of the previous
413
437
  * incarnation.
414
- *
415
- *
438
+ */
439
+
440
+ /**
416
441
  * @typedef {{ adminNode: Guarded<VatAdminFacet>, root: object }} CreateVatResults
417
442
  *
418
443
  * @typedef {object} VatAdminSvc
@@ -421,5 +446,4 @@ export {};
421
446
  * @property {(name: string) => ERef<BundleCap>} getNamedBundleCap
422
447
  * @property {(name: string) => ERef<BundleID>} getBundleIDByName
423
448
  * @property {(bundleCap: BundleCap, options?: DynamicVatOptions) => ERef<CreateVatResults>} createVat
424
- *
425
449
  */
@@ -6,8 +6,9 @@ import { Remotable } from '@endo/marshal';
6
6
  import { Far, E } from '@endo/far';
7
7
 
8
8
  /**
9
- * @template T
10
- * @typedef {T | PromiseLike<T>} ERef
9
+ * @import {ERef} from '@endo/far';
10
+ * @import {MapStore} from '@agoric/store';
11
+ * @import {Device, DProxy} from '@agoric/swingset-vat/src/types-external.js';
11
12
  */
12
13
 
13
14
  /** @type {{ onReset: (firstTime: Promise<boolean>) => void}} */
@@ -16,13 +17,6 @@ const DEFAULT_RESETTER = Far('resetter', { onReset: _ => {} });
16
17
  /** @type {{ walk: (pluginRootP: any) => any }} */
17
18
  const DEFAULT_WALKER = Far('walker', { walk: pluginRootP => pluginRootP });
18
19
 
19
- /**
20
- * @template T
21
- * @typedef {'Device' & { __deviceType__: T }} Device
22
- */
23
-
24
- /** @typedef {<T>(target: Device<T>) => T} DProxy (approximately) */
25
-
26
20
  /**
27
21
  * @callback LoadPlugin
28
22
  * @param {string} specifier
@@ -15,9 +15,11 @@ import { TimeMath } from '@agoric/time';
15
15
 
16
16
  /**
17
17
  * @import {LegacyWeakMap, WeakMapStore} from '@agoric/store';
18
- * @import {MapStore} from '@agoric/swingset-liveslots';
18
+ * @import {Baggage, MapStore} from '@agoric/swingset-liveslots';
19
19
  * @import {Passable, RemotableObject} from '@endo/pass-style';
20
20
  * @import {Key} from '@endo/patterns';
21
+ * @import {TimerDevice} from '../../devices/timer/device-timer.js';
22
+ * @import {DProxy} from '../../types-external.js';
21
23
  */
22
24
 
23
25
  // This consumes O(N) RAM only for outstanding promises, via wakeAt(),
@@ -237,9 +239,17 @@ const measureInterval = (start, interval, now) => {
237
239
  return { latest, next };
238
240
  };
239
241
 
242
+ /**
243
+ * @param {{
244
+ * D: DProxy;
245
+ * }} vatPowers
246
+ * @param {{}} _vatParameters
247
+ * @param {Baggage} baggage
248
+ */
240
249
  export const buildRootObject = (vatPowers, _vatParameters, baggage) => {
241
250
  const { D } = vatPowers;
242
251
 
252
+ /** @type {TimerDevice} */
243
253
  let timerDevice;
244
254
  const insistDevice = () => {
245
255
  assert(timerDevice, 'TimerService used before createTimerService()');
@@ -455,9 +465,6 @@ export const buildRootObject = (vatPowers, _vatParameters, baggage) => {
455
465
  },
456
466
  };
457
467
 
458
- /**
459
- * @returns { PromiseEvent }
460
- */
461
468
  const makePromiseEvent = prepareKind(
462
469
  baggage,
463
470
  'promiseEvent',
@@ -948,7 +955,7 @@ export const buildRootObject = (vatPowers, _vatParameters, baggage) => {
948
955
  * device, but we don't prohibit it from being called again (to
949
956
  * replace the device), just in case that's useful someday
950
957
  *
951
- * @param {unknown} timerNode
958
+ * @param {TimerDevice} timerNode
952
959
  * @returns {TimerService}
953
960
  */
954
961
  const createTimerService = timerNode => {
@@ -20,7 +20,7 @@ import {
20
20
 
21
21
  /**
22
22
  * @import {VatAdminRootDeviceNode} from '../../devices/vat-admin/device-vat-admin.js';
23
- * @import {DProxy} from'../plugin-manager.js';
23
+ * @import {DProxy} from'../../types-external.js';
24
24
  * @import {Baggage} from '@agoric/vat-data';
25
25
  */
26
26
 
@@ -49,7 +49,7 @@ export function buildRootObject(vatPowers, _vatParameters, baggage) {
49
49
  const pendingBundles = new Map();
50
50
  const pendingUpgrades = new Map(); // upgradeID -> Promise<UpgradeResults>
51
51
 
52
- /** @type {import('../plugin-manager.js').Device<VatAdminRootDeviceNode>} */
52
+ /** @type {import('../../types-external.js').Device<VatAdminRootDeviceNode>} */
53
53
  let vatAdminDev;
54
54
 
55
55
  const runningVats = new Map(); // vatID -> [doneP, { resolve, reject }]
@@ -8,7 +8,7 @@ import { buildRootObject } from '../src/vats/timer/vat-timer.js';
8
8
  /**
9
9
  * @import {Timestamp} from '@agoric/time'
10
10
  * @import {TimerService} from '@agoric/time'
11
- * @import {Waker} from '../src/devices/timer/device-timer.js'
11
+ * @import {TimerDevice, Waker} from '../src/devices/timer/device-timer.js'
12
12
  *
13
13
  * @typedef {object} ManualTimerCallbacks
14
14
  * @property {(newTime: bigint, msg?: string) => void} [advanceTo]
@@ -34,7 +34,7 @@ const setup = callbacks => {
34
34
  currentWakeup: undefined,
35
35
  currentHandler: undefined,
36
36
  };
37
- const deviceMarker = harden({});
37
+ const deviceMarker = /** @type {TimerDevice} */ (harden({}));
38
38
  const timerDeviceFuncs = harden({
39
39
  getLastPolled: () => state.now,
40
40
  setWakeup: (when, handler) => {
@@ -54,6 +54,7 @@ const setup = callbacks => {
54
54
  state.currentHandler = undefined;
55
55
  },
56
56
  });
57
+ /** @type {any} */
57
58
  const D = node => {
58
59
  assert.equal(node, deviceMarker, 'fake D only supports devices.timer');
59
60
  return timerDeviceFuncs;
@@ -15,9 +15,10 @@ import { makeQueue } from '@endo/stream';
15
15
  */
16
16
  export const makeRunUtils = (controller, harness) => {
17
17
  const mutex = makeQueue();
18
+ mutex.put('dummy result'); // so the first `await mutex.get()` doesn't hang
19
+
18
20
  const logRunFailure = reason =>
19
21
  console.log('controller.run() failure', reason);
20
- mutex.put(controller.run().catch(logRunFailure));
21
22
 
22
23
  /**
23
24
  * Wait for exclusive access to the controller, then before relinquishing that access,