@agoric/swingset-vat 0.32.3-dev-13c10cc.0 → 0.32.3-dev-7f12e80.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-13c10cc.0+13c10cc",
3
+ "version": "0.32.3-dev-7f12e80.0+7f12e80",
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-13c10cc.0+13c10cc",
31
- "@agoric/kmarshal": "0.1.1-dev-13c10cc.0+13c10cc",
32
- "@agoric/store": "0.9.3-dev-13c10cc.0+13c10cc",
33
- "@agoric/swing-store": "0.9.2-dev-13c10cc.0+13c10cc",
34
- "@agoric/swingset-liveslots": "0.10.3-dev-13c10cc.0+13c10cc",
35
- "@agoric/swingset-xsnap-supervisor": "0.10.3-dev-13c10cc.0+13c10cc",
36
- "@agoric/time": "0.3.3-dev-13c10cc.0+13c10cc",
37
- "@agoric/vat-data": "0.5.3-dev-13c10cc.0+13c10cc",
38
- "@agoric/xsnap": "0.14.3-dev-13c10cc.0+13c10cc",
39
- "@agoric/xsnap-lockdown": "0.14.1-dev-13c10cc.0+13c10cc",
30
+ "@agoric/internal": "0.3.3-dev-7f12e80.0+7f12e80",
31
+ "@agoric/kmarshal": "0.1.1-dev-7f12e80.0+7f12e80",
32
+ "@agoric/store": "0.9.3-dev-7f12e80.0+7f12e80",
33
+ "@agoric/swing-store": "0.9.2-dev-7f12e80.0+7f12e80",
34
+ "@agoric/swingset-liveslots": "0.10.3-dev-7f12e80.0+7f12e80",
35
+ "@agoric/swingset-xsnap-supervisor": "0.10.3-dev-7f12e80.0+7f12e80",
36
+ "@agoric/time": "0.3.3-dev-7f12e80.0+7f12e80",
37
+ "@agoric/vat-data": "0.5.3-dev-7f12e80.0+7f12e80",
38
+ "@agoric/xsnap": "0.14.3-dev-7f12e80.0+7f12e80",
39
+ "@agoric/xsnap-lockdown": "0.14.1-dev-7f12e80.0+7f12e80",
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": "13c10ccbb5ee417be8af41688441a8e7c67c45c2"
105
+ "gitHead": "7f12e80b36a41b74433162db9e8df0344330060d"
106
106
  }
@@ -980,10 +980,7 @@ export default function buildKernel(
980
980
  const abandonedObjects = [
981
981
  ...kernelKeeper.enumerateNonDurableObjectExports(vatID),
982
982
  ];
983
- for (const { kref, vref } of abandonedObjects) {
984
- /** @see translateAbandonExports in {@link ./vatTranslator.js} */
985
- vatKeeper.deleteCListEntry(kref, vref);
986
- /** @see abandonExports in {@link ./kernelSyscall.js} */
983
+ for (const { kref } of abandonedObjects) {
987
984
  kernelKeeper.orphanKernelObject(kref, vatID);
988
985
  }
989
986
 
@@ -178,24 +178,13 @@ export function makeKernelSyscallHandler(tools) {
178
178
 
179
179
  function retireExports(koids) {
180
180
  Array.isArray(koids) || Fail`retireExports given non-Array ${koids}`;
181
- const newActions = [];
182
- for (const koid of koids) {
183
- const importers = kernelKeeper.getImporters(koid);
184
- for (const vatID of importers) {
185
- newActions.push(`${vatID} retireImport ${koid}`);
186
- }
187
- // TODO: decref and delete any #2069 auxdata
188
- kernelKeeper.deleteKernelObject(koid);
189
- }
190
- kernelKeeper.addGCActions(newActions);
181
+ kernelKeeper.retireKernelObjects(koids);
191
182
  return OKNULL;
192
183
  }
193
184
 
194
185
  function abandonExports(vatID, koids) {
195
186
  Array.isArray(koids) || Fail`abandonExports given non-Array ${koids}`;
196
187
  for (const koid of koids) {
197
- // note that this is effectful and also performed outside of a syscall
198
- // by processUpgradeVat in {@link ./kernel.js}
199
188
  kernelKeeper.orphanKernelObject(koid, vatID);
200
189
  }
201
190
  return OKNULL;
@@ -112,7 +112,6 @@ export const CURRENT_SCHEMA_VERSION = 2;
112
112
  // $vatSlot is one of: o+$NN/o-$NN/p+$NN/p-$NN/d+$NN/d-$NN
113
113
  // v$NN.c.$vatSlot = $kernelSlot = ko$NN/kp$NN/kd$NN
114
114
  // v$NN.vs.$key = string
115
- // v$NN.meter = m$NN // XXX does this exist?
116
115
  // old (v0): v$NN.reapInterval = $NN or 'never'
117
116
  // old (v0): v$NN.reapCountdown = $NN or 'never'
118
117
  // v$NN.reapDirt = JSON({ deliveries, gcKrefs, computrons }) // missing keys treated as zero
@@ -621,7 +620,9 @@ export default function makeKernelKeeper(
621
620
 
622
621
  function getObjectRefCount(kernelSlot) {
623
622
  const data = kvStore.get(`${kernelSlot}.refCount`);
624
- data || Fail`getObjectRefCount(${kernelSlot}) was missing`;
623
+ if (!data) {
624
+ return { reachable: 0, recognizable: 0 };
625
+ }
625
626
  const [reachable, recognizable] = commaSplit(data).map(Number);
626
627
  reachable <= recognizable ||
627
628
  Fail`refmismatch(get) ${kernelSlot} ${reachable},${recognizable}`;
@@ -709,13 +710,32 @@ export default function makeKernelKeeper(
709
710
  return owner;
710
711
  }
711
712
 
713
+ function retireKernelObjects(koids) {
714
+ Array.isArray(koids) || Fail`retireExports given non-Array ${koids}`;
715
+ const newActions = [];
716
+ for (const koid of koids) {
717
+ const importers = getImporters(koid);
718
+ for (const vatID of importers) {
719
+ newActions.push(`${vatID} retireImport ${koid}`);
720
+ }
721
+ deleteKernelObject(koid);
722
+ }
723
+ addGCActions(newActions);
724
+ }
725
+
712
726
  function orphanKernelObject(kref, oldVat) {
727
+ // termination orphans all exports, upgrade orphans non-durable
728
+ // exports, and syscall.abandonExports orphans specific ones
713
729
  const ownerKey = `${kref}.owner`;
714
730
  const ownerVat = kvStore.get(ownerKey);
715
731
  ownerVat === oldVat || Fail`export ${kref} not owned by old vat`;
716
732
  kvStore.delete(ownerKey);
733
+ const { vatSlot: vref } = getReachableAndVatSlot(oldVat, kref);
734
+ kvStore.delete(`${oldVat}.c.${kref}`);
735
+ kvStore.delete(`${oldVat}.c.${vref}`);
736
+ addMaybeFreeKref(kref);
717
737
  // note that we do not delete the object here: it will be
718
- // collected if/when all other references are dropped
738
+ // retired if/when all other references are dropped
719
739
  }
720
740
 
721
741
  function deleteKernelObject(koid) {
@@ -931,6 +951,14 @@ export default function makeKernelKeeper(
931
951
  *
932
952
  */
933
953
  function cleanupAfterTerminatedVat(vatID, budget = undefined) {
954
+ // this is called from terminateVat, which is called from either:
955
+ // * end of processDeliveryMessage, if crankResults.terminate
956
+ // * device-vat-admin (when vat-v-a does adminNode.terminateVat)
957
+ // (which always happens inside processDeliveryMessage)
958
+ // so we're always followed by a call to processRefcounts, at
959
+ // end-of-delivery in processDeliveryMessage, after checking
960
+ // crankResults.terminate
961
+
934
962
  insistVatID(vatID);
935
963
  let cleanups = 0;
936
964
  const work = {
@@ -999,19 +1027,8 @@ export default function makeKernelKeeper(
999
1027
  const vref = stripPrefix(clistPrefix, k);
1000
1028
  assert(vref.startsWith('o+'), vref);
1001
1029
  const kref = kvStore.get(k);
1002
- // we must delete the c-list entry, but we don't need to
1003
- // manipulate refcounts like the way vatKeeper/deleteCListEntry
1004
- // does, so just delete the keys directly
1005
- // vatKeeper.deleteCListEntry(kref, vref);
1006
- kvStore.delete(`${vatID}.c.${kref}`);
1007
- kvStore.delete(`${vatID}.c.${vref}`);
1008
- // if this object became unreferenced, processRefcounts will
1009
- // delete it, so don't be surprised. TODO: this results in an
1010
- // extra get() for each delete(), see if there's a way to avoid
1011
- // this. TODO: think carefully about other such races.
1012
- if (kvStore.has(`${kref}.owner`)) {
1013
- orphanKernelObject(kref, vatID);
1014
- }
1030
+ // note: adds to maybeFreeKrefs, deletes c-list and .owner
1031
+ orphanKernelObject(kref, vatID);
1015
1032
  work.exports += 1;
1016
1033
  if (spend()) {
1017
1034
  logWork();
@@ -1484,14 +1501,43 @@ export default function makeKernelKeeper(
1484
1501
  return false;
1485
1502
  }
1486
1503
 
1504
+ // TODO (#9888): change the processRefcounts maybeFreeKrefs
1505
+ // iteration to handle krefs that get added multiple times (while
1506
+ // we're iterating), because we might do different work the second
1507
+ // time around. The concerning scenario is:
1508
+ //
1509
+ // * kp2 has resolution data which references ko1
1510
+ // * somehow both kp2 and ko1 end up on maybeFreeKrefs, but ko1.reachable=1
1511
+ // (via kp2)
1512
+ // * our iterator visits ko1 first, sees it is still reachable, ignores it
1513
+ // * then the iterator visits kp2, decrefs its kp.data.slots
1514
+ // * this pushes ko1 back onto maybeFreeKrefs
1515
+ // * we need to examine ko1 again, so it can be released
1516
+ //
1517
+ // It could also happen if/when we implement #2069 auxdata:
1518
+ //
1519
+ // * ko2 has auxdata that references ko1
1520
+ // * both ko1 and ko2 end up on maybeFreeKrefs, but ko1.reachable = 1
1521
+ // (via the ko2 auxdata)
1522
+ // * our iterator visits ko1 first, sees it is still reachable, ignores it
1523
+ // * then the iterator visits ko2, does deleteKernelObject
1524
+ // * this frees the auxdata, which pushes ko1 back onto maybeFreeKrefs
1525
+ // * we need to examine ko1 again, so it can be released
1526
+ //
1527
+ // We should use something like an ordered Set, and a loop that does:
1528
+ // * pop the first kref off
1529
+ // * processes it (maybe adding more krefs)
1530
+ // * repeats until the thing is empty
1531
+ // Or maybe make a copy of maybeFreeKrefs at the start, clear the
1532
+ // original, and wrap this in a loop that keeps going until
1533
+ // maybeFreeKrefs is still empty at the end. Be sure to imagine a
1534
+ // very deep linked list: don't just process it twice, keep
1535
+ // processing until there's nothing left to do, otherwise we'll be
1536
+ // leaving work for the next delivery.
1537
+
1487
1538
  function processRefcounts() {
1488
1539
  if (enableKernelGC) {
1489
- const actions = getGCActions(); // local cache
1490
- // TODO (else buggy): change the iteration to handle krefs that get
1491
- // added multiple times (while we're iterating), because we might do
1492
- // different work the second time around. Something like an ordered
1493
- // Set, and a loop that: pops the first kref off, processes it (maybe
1494
- // adding more krefs), repeats until the thing is empty.
1540
+ const actions = new Set();
1495
1541
  for (const kref of maybeFreeKrefs.values()) {
1496
1542
  const { type } = parseKernelSlot(kref);
1497
1543
  if (type === 'promise') {
@@ -1499,6 +1545,7 @@ export default function makeKernelKeeper(
1499
1545
  const kp = getKernelPromise(kpid);
1500
1546
  if (kp.refCount === 0) {
1501
1547
  let idx = 0;
1548
+ // TODO (#9889) don't assume promise is settled
1502
1549
  for (const slot of kp.data.slots) {
1503
1550
  // Note: the following decrement can result in an addition to the
1504
1551
  // maybeFreeKrefs set, which we are in the midst of iterating.
@@ -1509,32 +1556,74 @@ export default function makeKernelKeeper(
1509
1556
  deleteKernelPromise(kpid);
1510
1557
  }
1511
1558
  }
1559
+
1512
1560
  if (type === 'object') {
1513
1561
  const { reachable, recognizable } = getObjectRefCount(kref);
1514
1562
  if (reachable === 0) {
1515
- const ownerVatID = ownerOfKernelObject(kref);
1516
- if (ownerVatID) {
1563
+ // We avoid ownerOfKernelObject(), which will report
1564
+ // 'undefined' if the owner is dead (and being slowly
1565
+ // deleted). Message delivery should use that, but not us.
1566
+ const ownerKey = `${kref}.owner`;
1567
+ let ownerVatID = kvStore.get(ownerKey);
1568
+ const terminated = terminatedVats.includes(ownerVatID);
1569
+
1570
+ // Some objects that are still owned, but the owning vat
1571
+ // might still alive, or might be terminated and in the
1572
+ // process of being deleted. These two clauses are
1573
+ // mutually exclusive.
1574
+
1575
+ if (ownerVatID && !terminated) {
1517
1576
  const vatKeeper = provideVatKeeper(ownerVatID);
1518
- const isReachable = vatKeeper.getReachableFlag(kref);
1519
- if (isReachable) {
1577
+ const vatConsidersReachable = vatKeeper.getReachableFlag(kref);
1578
+ if (vatConsidersReachable) {
1520
1579
  // the reachable count is zero, but the vat doesn't realize it
1521
1580
  actions.add(`${ownerVatID} dropExport ${kref}`);
1522
1581
  }
1523
1582
  if (recognizable === 0) {
1524
- // TODO: rethink this
1525
- // assert.equal(isReachable, false, `${kref} is reachable but not recognizable`);
1583
+ // TODO: rethink this assert
1584
+ // assert.equal(vatConsidersReachable, false, `${kref} is reachable but not recognizable`);
1526
1585
  actions.add(`${ownerVatID} retireExport ${kref}`);
1527
1586
  }
1528
- } else if (recognizable === 0) {
1529
- // unreachable, unrecognizable, orphaned: delete the
1530
- // empty refcount here, since we can't send a GC
1531
- // action without an ownerVatID
1532
- deleteKernelObject(kref);
1587
+ } else if (ownerVatID && terminated) {
1588
+ // When we're slowly deleting a vat, and one of its
1589
+ // exports becomes unreferenced, we obviously must not
1590
+ // send dropExports or retireExports into the dead vat.
1591
+ // We fast-forward the abandonment that slow-deletion
1592
+ // would have done, then treat the object as orphaned.
1593
+
1594
+ const { vatSlot } = getReachableAndVatSlot(ownerVatID, kref);
1595
+ // delete directly, not orphanKernelObject(), which
1596
+ // would re-submit to maybeFreeKrefs
1597
+ kvStore.delete(ownerKey);
1598
+ kvStore.delete(`${ownerVatID}.c.${kref}`);
1599
+ kvStore.delete(`${ownerVatID}.c.${vatSlot}`);
1600
+ // now fall through to the orphaned case
1601
+ ownerVatID = undefined;
1602
+ }
1603
+
1604
+ // Now handle objects which were orphaned. NOTE: this
1605
+ // includes objects which were owned by a terminated (but
1606
+ // not fully deleted) vat, where `ownerVatID` was cleared
1607
+ // in the last line of that previous clause (the
1608
+ // fall-through case). Don't try to change this `if
1609
+ // (!ownerVatID)` into an `else if`: the two clauses are
1610
+ // *not* mutually-exclusive.
1611
+
1612
+ if (!ownerVatID) {
1613
+ // orphaned and unreachable, so retire it. If the kref
1614
+ // is recognizable, then we need retireKernelObjects()
1615
+ // to scan for importers and send retireImports (and
1616
+ // delete), else we can call deleteKernelObject directly
1617
+ if (recognizable) {
1618
+ retireKernelObjects([kref]);
1619
+ } else {
1620
+ deleteKernelObject(kref);
1621
+ }
1533
1622
  }
1534
1623
  }
1535
1624
  }
1536
1625
  }
1537
- setGCActions(actions);
1626
+ addGCActions([...actions]);
1538
1627
  }
1539
1628
  maybeFreeKrefs.clear();
1540
1629
  }
@@ -1824,6 +1913,7 @@ export default function makeKernelKeeper(
1824
1913
  kernelObjectExists,
1825
1914
  getImporters,
1826
1915
  orphanKernelObject,
1916
+ retireKernelObjects,
1827
1917
  deleteKernelObject,
1828
1918
  pinObject,
1829
1919
 
@@ -481,9 +481,6 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) {
481
481
  assert.equal(allocatedByVat, true); // abandon *exports*, not imports
482
482
  // kref must already be in the clist
483
483
  const kref = mapVatSlotToKernelSlot(vref, gcSyscallMapOpts);
484
- // note that this is effectful and also performed outside of a syscall
485
- // by processUpgradeVat in {@link ./kernel.js}
486
- vatKeeper.deleteCListEntry(kref, vref);
487
484
  return kref;
488
485
  });
489
486
  kdebug(`syscall[${vatID}].abandonExports(${krefs.join(' ')})`);