UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

74 lines • 3.11 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= export const YIELD_INTERVAL = 2; export const YIELD_CALLS = 4096; /** * A per-consumer time budget between opportunities for timers and * cancellation. `check` runs beside the signal, for a deadline it does not * carry. A busy consumer yields every `YIELD_INTERVAL` ms, the turn Python's * twin takes under the daemon's 1 ms GIL switch interval: the twin yields every * two switch intervals, as a loop that yields more often keeps its other * threads from the GIL, while this one runs every workspace on one thread. A * budget also yields after `YIELD_CALLS` runs, whichever comes first, and * starts at its first `run`, as the twin does. * The interval is read on `performance.now()`, the clock fake timers replace * along with the timer its yield waits on; where that clock stands still while * a task computes, the call bound is what lets a busy line be cancelled. */ export class YieldBudget { signal; check; nextYield = null; calls = 0; constructor(signal, check) { this.signal = signal; this.check = check; } run() { this.signal?.throwIfAborted(); this.check?.(); this.calls += 1; if (this.nextYield === null) { this.nextYield = performance.now() + YIELD_INTERVAL; return; } if (performance.now() < this.nextYield && this.calls < YIELD_CALLS) return; return this.yield(); } async yield() { // A turn of the loop, not Promise.resolve()'s microtask continuation, so // timers and I/O due by now run first. A nested setTimeout(0) idles 1 ms in // Node and 4 ms in a page at every yield; an immediate (Node, Bun) or a // message a page posts itself goes on without that wait. await new Promise((resolve) => { const { setImmediate: immediate } = globalThis; if (immediate !== undefined) { immediate(resolve); return; } const channel = new MessageChannel(); channel.port1.onmessage = () => { channel.port1.close(); resolve(); }; channel.port2.postMessage(null); }); this.calls = 0; this.nextYield = performance.now() + YIELD_INTERVAL; this.signal?.throwIfAborted(); this.check?.(); } } //# sourceMappingURL=yield_budget.js.map