Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
# `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.