@metamask/ocap-kernel
Version:
OCap kernel core components
212 lines • 10.8 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _KernelQueue_instances, _KernelQueue_kernelStore, _KernelQueue_terminateVat, _KernelQueue_wakeUpTheRunQueue, _KernelQueue_runQueueItems, _KernelQueue_enqueueRun;
import { makePromiseKit } from "@endo/promise-kit";
import { processGCActionSet } from "./services/garbage-collection.mjs";
import { kser } from "./services/kernel-marshal.mjs";
import { insistVatId } from "./types.mjs";
import { Fail } from "./utils/assert.mjs";
/**
* The kernel's run queue.
*
* This class manages the kernel's run queue, which is a queue of items that
* need to be processed.
*/
export class KernelQueue {
constructor(kernelStore, terminateVat) {
_KernelQueue_instances.add(this);
/** Storage holding the kernel's own persistent state */
_KernelQueue_kernelStore.set(this, void 0);
/** A function that terminates a vat. */
_KernelQueue_terminateVat.set(this, void 0);
/** Message results that the kernel itself has subscribed to */
this.subscriptions = new Map();
/** Thunk to signal run queue transition from empty to non-empty */
_KernelQueue_wakeUpTheRunQueue.set(this, void 0);
__classPrivateFieldSet(this, _KernelQueue_kernelStore, kernelStore, "f");
__classPrivateFieldSet(this, _KernelQueue_terminateVat, terminateVat, "f");
__classPrivateFieldSet(this, _KernelQueue_wakeUpTheRunQueue, null, "f");
}
/**
* The kernel's run loop: take an item off the run queue, deliver it,
* repeat. Note that this loops forever: the returned promise never resolves.
*
* @param deliver - A function that delivers an item to the kernel.
*/
async run(deliver) {
for await (const item of __classPrivateFieldGet(this, _KernelQueue_instances, "m", _KernelQueue_runQueueItems).call(this)) {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").nextTerminatedVatCleanup();
const crankResults = await deliver(item);
if (crankResults?.abort) {
// Rollback the kernel state to before the failed delivery attempt.
// For active vats, this allows the message to be retried in a future crank.
// For terminated vats, the message will just go splat.
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").rollbackCrank('start');
// TODO: Currently all errors terminate the vat, but instead we could
// restart it and terminate the vat only after a certain number of failed
// retries. This is probably where we should implement the vat restart logic.
}
// Vat termination during delivery is triggered by an illegal syscall
// or by syscall.exit().
if (crankResults?.terminate) {
const { vatId, info } = crankResults.terminate;
await __classPrivateFieldGet(this, _KernelQueue_terminateVat, "f").call(this, vatId, info);
}
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").collectGarbage();
}
}
/**
* Queue a message to be delivered from the kernel to an object in a vat.
*
* @param target - The object to which the message is directed.
* @param method - The method to be invoked.
* @param args - Message arguments.
*
* @returns a promise for the (CapData encoded) result of the message invocation.
*/
async enqueueMessage(target, method, args) {
const result = __classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").initKernelPromise()[0];
const { promise, resolve } = makePromiseKit();
this.subscriptions.set(result, resolve);
this.enqueueSend(target, {
methargs: kser([method, args]),
result,
});
return promise;
}
/**
* Enqueue a send message to be delivered to a vat.
*
* @param target - The object to which the message is directed.
* @param message - The message to be delivered.
*/
enqueueSend(target, message) {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").incrementRefCount(target, 'queue|target');
if (message.result) {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").incrementRefCount(message.result, 'queue|result');
}
for (const slot of message.methargs.slots || []) {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").incrementRefCount(slot, 'queue|slot');
}
const queueItem = {
type: 'send',
target,
message,
};
__classPrivateFieldGet(this, _KernelQueue_instances, "m", _KernelQueue_enqueueRun).call(this, queueItem);
}
/**
* Enqueue for delivery a notification to a vat about the resolution of a
* promise.
*
* @param vatId - The vat that will be notified.
* @param kpid - The promise of interest.
*/
enqueueNotify(vatId, kpid) {
const notifyItem = { type: 'notify', vatId, kpid };
__classPrivateFieldGet(this, _KernelQueue_instances, "m", _KernelQueue_enqueueRun).call(this, notifyItem);
// Increment reference count for the promise being notified about
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").incrementRefCount(kpid, 'notify');
}
/**
* Process a set of promise resolutions coming from a vat.
*
* @param vatId - The vat doing the resolving, if there is one.
* @param resolutions - One or more resolutions, to be processed as a group.
*/
resolvePromises(vatId, resolutions) {
if (vatId) {
insistVatId(vatId);
}
for (const resolution of resolutions) {
const [kpid, rejected, dataRaw] = resolution;
const data = dataRaw;
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").incrementRefCount(kpid, 'resolve|kpid');
for (const slot of data.slots || []) {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").incrementRefCount(slot, 'resolve|slot');
}
const promise = __classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").getKernelPromise(kpid);
const { state, decider, subscribers } = promise;
if (state !== 'unresolved') {
Fail `${kpid} was already resolved`;
}
if (decider !== vatId) {
const why = decider ? `its decider is ${decider}` : `it has no decider`;
Fail `${vatId} not permitted to resolve ${kpid} because ${why}`;
}
if (!subscribers) {
throw Fail `${kpid} subscribers not set`;
}
for (const subscriber of subscribers) {
this.enqueueNotify(subscriber, kpid);
}
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").resolveKernelPromise(kpid, rejected, data);
const kernelResolve = this.subscriptions.get(kpid);
if (kernelResolve) {
this.subscriptions.delete(kpid);
kernelResolve(data);
}
}
}
}
_KernelQueue_kernelStore = new WeakMap(), _KernelQueue_terminateVat = new WeakMap(), _KernelQueue_wakeUpTheRunQueue = new WeakMap(), _KernelQueue_instances = new WeakSet(), _KernelQueue_runQueueItems =
/**
* Async generator that yields the items from the kernel run queue, in order.
*
* @yields the next item in the run queue.
*/
async function* _KernelQueue_runQueueItems() {
for (;;) {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").startCrank();
try {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").createCrankSavepoint('start');
const gcAction = processGCActionSet(__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f"));
if (gcAction) {
yield gcAction;
continue;
}
const reapAction = __classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").nextReapAction();
if (reapAction) {
yield reapAction;
continue;
}
while (__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").runQueueLength() > 0) {
const item = __classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").dequeueRun();
if (item) {
yield item;
}
else {
break;
}
}
if (__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").runQueueLength() === 0) {
const { promise, resolve } = makePromiseKit();
if (__classPrivateFieldGet(this, _KernelQueue_wakeUpTheRunQueue, "f") !== null) {
Fail `wakeUpTheRunQueue function already set`;
}
__classPrivateFieldSet(this, _KernelQueue_wakeUpTheRunQueue, resolve, "f");
await promise;
}
}
finally {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").endCrank();
}
}
}, _KernelQueue_enqueueRun = function _KernelQueue_enqueueRun(item) {
__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").enqueueRun(item);
if (__classPrivateFieldGet(this, _KernelQueue_kernelStore, "f").runQueueLength() === 1 && __classPrivateFieldGet(this, _KernelQueue_wakeUpTheRunQueue, "f")) {
const wakeUpTheRunQueue = __classPrivateFieldGet(this, _KernelQueue_wakeUpTheRunQueue, "f");
__classPrivateFieldSet(this, _KernelQueue_wakeUpTheRunQueue, null, "f");
wakeUpTheRunQueue();
}
};
//# sourceMappingURL=KernelQueue.mjs.map