@metamask/ocap-kernel
Version:
OCap kernel core components
128 lines • 5.32 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.processGCActionSet = processGCActionSet;
const kernel_slots_ts_1 = require("../store/utils/kernel-slots.cjs");
const types_ts_1 = require("../types.cjs");
const assert_ts_1 = require("../utils/assert.cjs");
/**
* Parse a GC action string into a vat id, type, and kref.
*
* @param action - The GC action string to parse.
* @returns The parsed GC action.
*/
function parseAction(action) {
const [vatId, type, kref] = action.split(' ');
(0, types_ts_1.insistVatId)(vatId);
(0, types_ts_1.insistGCActionType)(type);
(0, kernel_slots_ts_1.insistKernelType)('object', kref);
return harden({ vatId, type, kref });
}
/**
* Determines if a GC action should be processed based on current system state.
*
* @param storage - The kernel storage.
* @param vatId - The vat id of the vat that owns the kref.
* @param type - The type of GC action.
* @param kref - The kref of the object in question.
* @returns True if the action should be processed, false otherwise.
*/
function shouldProcessAction(storage, vatId, type, kref) {
const hasCList = storage.hasCListEntry(vatId, kref);
const isReachable = hasCList
? storage.getReachableFlag(vatId, kref)
: undefined;
const exists = storage.kernelRefExists(kref);
const { reachable, recognizable } = exists
? storage.getObjectRefCount(kref)
: { reachable: 0, recognizable: 0 };
switch (type) {
case 'dropExport':
return exists && reachable === 0 && hasCList && isReachable === true;
case 'retireExport':
return exists && reachable === 0 && recognizable === 0 && hasCList;
case 'retireImport':
return hasCList;
default:
return false;
}
}
/**
* Filters and processes a group of GC actions for a specific vat and action type.
*
* @param storage - The kernel storage.
* @param vatId - The vat id of the vat that owns the krefs.
* @param actions - The set of GC actions to process.
* @param allActionsSet - The complete set of GC actions.
* @returns Object containing the krefs to process and whether the action set was updated.
*/
function filterActionsForProcessing(storage, vatId, actions, allActionsSet) {
const krefs = [];
let actionSetUpdated = false;
for (const action of actions) {
const { type, kref } = parseAction(action);
if (shouldProcessAction(storage, vatId, type, kref)) {
krefs.push(kref);
}
allActionsSet.delete(action);
actionSetUpdated = true;
}
return harden({ krefs, actionSetUpdated });
}
/**
* Process the set of GC actions.
*
* @param storage - The kernel storage.
* @returns The next action to process, or undefined if there are no actions to process.
*/
function processGCActionSet(storage) {
const allActionsSet = storage.getGCActions();
let actionSetUpdated = false;
// Group actions by vat and type
const actionsByVat = new Map();
for (const action of allActionsSet) {
const { vatId, type } = parseAction(action);
if (!actionsByVat.has(vatId)) {
actionsByVat.set(vatId, new Map());
}
const actionsForVatByType = actionsByVat.get(vatId);
(0, assert_ts_1.assert)(actionsForVatByType !== undefined, `No actions for vat: ${vatId}`);
if (!actionsForVatByType.has(type)) {
actionsForVatByType.set(type, new Set());
}
const actions = actionsForVatByType.get(type);
(0, assert_ts_1.assert)(actions !== undefined, `No actions for type: ${type}`);
actions.add(action);
}
// Process actions in priority order
const vatIds = Array.from(actionsByVat.keys()).sort();
for (const vatId of vatIds) {
const actionsForVatByType = actionsByVat.get(vatId);
(0, assert_ts_1.assert)(actionsForVatByType !== undefined, `No actions for vat: ${vatId}`);
// Find the highest-priority type of work to do within this vat
for (const type of types_ts_1.actionTypePriorities) {
if (actionsForVatByType.has(type)) {
const actions = actionsForVatByType.get(type);
(0, assert_ts_1.assert)(actions !== undefined, `No actions for type: ${type}`);
const { krefs, actionSetUpdated: updated } = filterActionsForProcessing(storage, vatId, actions, allActionsSet);
actionSetUpdated = actionSetUpdated || updated;
if (krefs.length > 0) {
// We found actions to process
krefs.sort();
// Update the durable set before returning
storage.setGCActions(allActionsSet);
const queueType = types_ts_1.queueTypeFromActionType.get(type);
(0, assert_ts_1.assert)(queueType !== undefined, `Unknown action type: ${type}`);
return harden({ type: queueType, vatId, krefs });
}
}
}
}
if (actionSetUpdated) {
// Remove negated items from the durable set
storage.setGCActions(allActionsSet);
}
// No GC work to do
return undefined;
}
harden(processGCActionSet);
//# sourceMappingURL=garbage-collection.cjs.map