withSlot
uses node:async_hooks
Run work in a slot of the machine's: one taken for it (as what: <kind> of <root> on <branch>), waiting its turn
with every other tcm's work, and given back when it ends. Work that's already in one runs at once, taking no more:
inside a suite's slot (inSuiteSlot), or inside another withSlot's work in this process (so nested work
never waits behind itself). What the work starts as a process of its own runs in its slot too when started with
env (TCM_SUITE_SLOT). A wait is said by say (stderr's, unless given), as slotWaits says it. pool is the
machine's (slotPool in slotsDir, machineSlots of them) unless given.
import { inSuiteSlot, machineSlots, slotPool, slotsDir, slotWaits } from "#codebase/tcmts/cli/budget"; import type { SlotPool } from "#codebase/tcmts/cli/budget"; import { AsyncLocalStorage } from "node:async_hooks"; export async function withSlot<T>( what: string, work: (env: Record<string, string>) => Promise<T>, options: { say?: (line: string) => void; pool?: SlotPool } = {}, ): Promise<T> { const env = { TCM_SUITE_SLOT: "1" }; const g = globalThis as unknown as Record<symbol, unknown>; const held = (g[Symbol.for("tcm.slot")] ??= new AsyncLocalStorage<true>()) as AsyncLocalStorage<true>; if (held.getStore() || (!options.pool && inSuiteSlot())) return work(env); const say = options.say ?? ((line: string) => process.stderr.write(`${line}\n`)); const release = await slotWaits(say).take( options.pool ?? slotPool(slotsDir(), machineSlots()), what, ); try { return await held.run(true, () => work(env)); } finally { release(); } }
uses
- tcmts.cli.budget.SlotPool
- tcmts.cli.budget.inSuiteSlot
- tcmts.cli.budget.machineSlots
- tcmts.cli.budget.slotPool
- tcmts.cli.budget.slotWaits
- tcmts.cli.budget.slotsDir