UNPKG

@metamask/ocap-kernel

Version:
212 lines 10.8 kB
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