@agoric/swingset-vat 0.32.3-dev-74a89a7.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 +12 -12
- package/src/kernel/kernel.js +1 -4
- package/src/kernel/kernelSyscall.js +1 -12
- package/src/kernel/state/kernelKeeper.js +124 -34
- package/src/kernel/vatTranslator.js +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/swingset-vat",
|
|
3
|
-
"version": "0.32.3-dev-
|
|
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-
|
|
31
|
-
"@agoric/kmarshal": "0.1.1-dev-
|
|
32
|
-
"@agoric/store": "0.9.3-dev-
|
|
33
|
-
"@agoric/swing-store": "0.9.2-dev-
|
|
34
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
35
|
-
"@agoric/swingset-xsnap-supervisor": "0.10.3-dev-
|
|
36
|
-
"@agoric/time": "0.3.3-dev-
|
|
37
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
38
|
-
"@agoric/xsnap": "0.14.3-dev-
|
|
39
|
-
"@agoric/xsnap-lockdown": "0.14.1-dev-
|
|
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": "
|
|
105
|
+
"gitHead": "7f12e80b36a41b74433162db9e8df0344330060d"
|
|
106
106
|
}
|
package/src/kernel/kernel.js
CHANGED
|
@@ -980,10 +980,7 @@ export default function buildKernel(
|
|
|
980
980
|
const abandonedObjects = [
|
|
981
981
|
...kernelKeeper.enumerateNonDurableObjectExports(vatID),
|
|
982
982
|
];
|
|
983
|
-
for (const { kref
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
1003
|
-
|
|
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 =
|
|
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
|
-
|
|
1516
|
-
if (
|
|
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
|
|
1519
|
-
if (
|
|
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(
|
|
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 (
|
|
1529
|
-
//
|
|
1530
|
-
//
|
|
1531
|
-
//
|
|
1532
|
-
|
|
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
|
-
|
|
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(' ')})`);
|