withSlot

tcmts.cli.budget.withSlot · #ysl4qy23 · term · runs on the server

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

used by