# `scheduler` ## Purpose `scheduler.ts` exposes `pickSlot`, the pure function `WorkerPool` calls to decide which slot receives the next submit. The function takes a projection of the pool's slots (only the data the scheduler needs: `name` + `pendingCount`) and the strategy name, and returns the chosen slot plus the next round-robin index. Isolating the scheduling decision in a pure function makes it independently testable, swappable (a future scheduler can be added without touching the pool), and free of side effects. ## Features - Two scheduling strategies: `round-robin` and `least-busy`. - `SchedulerSlot` projection — lightweight view of the pool's slots so the scheduler doesn't depend on `WorkerPool`'s internals. - Returns `undefined` when the slot list is empty, letting the caller decide what to do (typically reject with `RESTART_EXHAUSTED`). - Round-robin returns the next index so the caller can persist it. ## Usage: Basic Example ```ts import { pickSlot } from "@suaveplan/web-workers"; const slots = [ { name: "w0", pendingCount: 0 }, { name: "w1", pendingCount: 0 }, ]; const pick = pickSlot("round-robin", slots, 0); // pick.slot.name === "w0", pick.nextIndex === 1 ``` ## Usage: Advanced Example ```ts import { pickSlot, type SchedulerSlot, type SchedulerStrategy, } from "@suaveplan/web-workers"; class CustomPool { private slots: Array<{ name: string; pendingCount: number }> = []; private nextRR = 0; pick(strategy: SchedulerStrategy): SchedulerSlot | undefined { const projection = this.slots.map((s) => ({ name: s.name, pendingCount: s.pendingCount, })); const result = pickSlot(strategy, projection, this.nextRR); if (result === undefined) return undefined; this.nextRR = result.nextIndex; return result.slot; } } ``` ## API Reference - `type SchedulerStrategy = "round-robin" | "least-busy"`. - `interface SchedulerSlot { name: string; pendingCount: number }`. - `pickSlot(strategy, slots, nextRoundRobinIndex): { slot, nextIndex } | undefined`. ## Implementation Notes `round-robin` uses `nextRoundRobinIndex % slots.length` so the caller can keep a monotonically-increasing counter without worrying about wraparound. `least-busy` linear-scans the slot list; ties go to the lowest-index slot (deterministic). The pure-function shape means the same slot projection always returns the same result — no hidden state, no randomness. For large pools (> 100 slots) a heap-based least-busy could be faster, but the typical pool size (4–16 slots) makes the linear scan negligible.