@crashlab/scheduler 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -123,6 +123,7 @@ var Scheduler = class {
123
123
  while (j < ready.length && ready[j].when === ready[i].when) j++;
124
124
  if (j - i > 1) {
125
125
  const group = ready.slice(i, j);
126
+ group.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
126
127
  shuffleInPlace(group, this._rng);
127
128
  for (let k = 0; k < group.length; k++) {
128
129
  ready[i + k] = group[k];
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/prng.ts"],"sourcesContent":["import type { IClock, PendingOp } from './types.js';\nimport { mulberry32, shuffleInPlace } from './prng.js';\n\nexport type { IClock, PendingOp } from './types.js';\n\nexport interface SchedulerOptions {\n /** Optional virtual clock. When attached, the scheduler can be called from clock.advance(). */\n clock?: IClock;\n /** Seed for the PRNG that determines execution order of same-tick ops. */\n prngSeed?: number;\n}\n\n/**\n * Cooperative deterministic scheduler.\n *\n * Holds mock-I/O completions until their virtual time arrives, then\n * releases them in a PRNG-determined order — making all macro-level\n * race conditions reproducible across runs with the same seed.\n *\n * **This scheduler does NOT intercept V8 microtasks or Promise internals.**\n * It controls ordering exclusively at mock I/O boundaries.\n */\nexport class Scheduler {\n private _clock: IClock | undefined;\n private readonly _rng: () => number;\n private readonly _pending: PendingOp[] = [];\n private _runChain: Promise<void> = Promise.resolve();\n private _autoDrainScheduled = false;\n private _requestedTick: number | null = null;\n\n constructor(opts: SchedulerOptions = {}) {\n this._clock = opts.clock;\n this._rng = mulberry32(opts.prngSeed ?? 0);\n }\n\n // public API\n\n /**\n * Enqueue a mock-I/O completion.\n *\n * The `run` callback is **not** invoked immediately — it is held in the\n * pending queue until `runTick(t)` is called with `t >= op.when`.\n */\n enqueueCompletion(op: PendingOp): void {\n this._pending.push(op);\n }\n\n /**\n * Request an asynchronous drain for ops ready at `virtualTime`.\n *\n * Multiple calls in the same turn are coalesced into one microtask and the\n * highest requested virtual time is used.\n */\n requestRunTick(virtualTime: number): void {\n this._requestedTick = this._requestedTick == null\n ? virtualTime\n : Math.max(this._requestedTick, virtualTime);\n\n if (this._autoDrainScheduled) return;\n this._autoDrainScheduled = true;\n\n queueMicrotask(() => {\n this._autoDrainScheduled = false;\n const tick = this._requestedTick;\n this._requestedTick = null;\n if (tick == null) return;\n void this.runTick(tick).catch(() => {\n // Errors still surface to explicit runTick callers; requestRunTick is\n // best-effort and must not throw asynchronously.\n });\n if (this._requestedTick != null) {\n this.requestRunTick(this._requestedTick);\n }\n });\n }\n\n /**\n * Attach (or replace) the clock reference.\n *\n * Integration contract: when the clock advances to time `t`, it should\n * call `await scheduler.runTick(t)`.\n */\n attachClock(clock: IClock): void {\n this._clock = clock;\n }\n\n /**\n * Collect all enqueued ops with `when <= virtualTime`, shuffle them\n * deterministically via the seeded PRNG, then execute their `run()`\n * callbacks **sequentially** in that shuffled order, awaiting one\n * microtask checkpoint (`await Promise.resolve()`) between each.\n */\n async runTick(virtualTime: number): Promise<void> {\n const run = async (): Promise<void> => {\n // Loop to handle cascading completions: ops enqueued during callback\n // execution that are also ready at this virtual time are picked up\n // in the next iteration, preserving causal ordering.\n while (true) {\n // 1. Partition: pull out all ops ready at this tick.\n const ready: PendingOp[] = [];\n const remaining: PendingOp[] = [];\n for (const op of this._pending) {\n if (op.when <= virtualTime) {\n ready.push(op);\n } else {\n remaining.push(op);\n }\n }\n this._pending.length = 0;\n this._pending.push(...remaining);\n\n if (ready.length === 0) break;\n\n // 2. Stable-sort by `when` ascending so earlier ops run first,\n // then shuffle within each same-`when` group via PRNG.\n ready.sort((a, b) => a.when - b.when);\n\n // Group by `when` and shuffle each group.\n let i = 0;\n while (i < ready.length) {\n let j = i;\n while (j < ready.length && ready[j].when === ready[i].when) j++;\n if (j - i > 1) {\n const group = ready.slice(i, j);\n shuffleInPlace(group, this._rng);\n for (let k = 0; k < group.length; k++) {\n ready[i + k] = group[k];\n }\n }\n i = j;\n }\n\n // 3. Execute sequentially, with a microtask boundary between each.\n for (const op of ready) {\n await op.run();\n await Promise.resolve(); // microtask checkpoint\n }\n }\n };\n\n const chained = this._runChain.then(run);\n this._runChain = chained.catch(() => {});\n return chained;\n }\n\n /**\n * Drain **all** pending ops immediately, regardless of their `when`.\n * Useful for test teardown.\n */\n async drain(): Promise<void> {\n await this.runTick(Number.MAX_SAFE_INTEGER);\n }\n\n /** Number of operations currently held in the pending queue. */\n get pendingCount(): number {\n return this._pending.length;\n }\n}\n","/**\n * Mulberry32 — fast 32-bit seeded PRNG.\n * Returns a function yielding numbers in [0, 1).\n *\n * This is a self-contained fallback so `@crashlab/scheduler` has zero\n * hard dependencies. If `@crashlab/random` is available in the monorepo,\n * consumers can pass their own SeededRandom instead.\n */\nexport function mulberry32(seed: number): () => number {\n let s = seed | 0;\n return function next(): number {\n s = (s + 0x6d2b79f5) | 0;\n let t = Math.imul(s ^ (s >>> 15), 1 | s);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Deterministic Fisher-Yates shuffle using a seeded PRNG.\n * Mutates `arr` in place and returns it.\n */\nexport function shuffleInPlace<T>(arr: T[], rng: () => number): T[] {\n for (let i = arr.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1));\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n return arr;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/prng.ts"],"sourcesContent":["import type { IClock, PendingOp } from './types.js';\nimport { mulberry32, shuffleInPlace } from './prng.js';\n\nexport type { IClock, PendingOp } from './types.js';\n\nexport interface SchedulerOptions {\n /** Optional virtual clock. When attached, the scheduler can be called from clock.advance(). */\n clock?: IClock;\n /** Seed for the PRNG that determines execution order of same-tick ops. */\n prngSeed?: number;\n}\n\n/**\n * Cooperative deterministic scheduler.\n *\n * Holds mock-I/O completions until their virtual time arrives, then\n * releases them in a PRNG-determined order — making all macro-level\n * race conditions reproducible across runs with the same seed.\n *\n * **This scheduler does NOT intercept V8 microtasks or Promise internals.**\n * It controls ordering exclusively at mock I/O boundaries.\n */\nexport class Scheduler {\n private _clock: IClock | undefined;\n private readonly _rng: () => number;\n private readonly _pending: PendingOp[] = [];\n private _runChain: Promise<void> = Promise.resolve();\n private _autoDrainScheduled = false;\n private _requestedTick: number | null = null;\n\n constructor(opts: SchedulerOptions = {}) {\n this._clock = opts.clock;\n this._rng = mulberry32(opts.prngSeed ?? 0);\n }\n\n // public API\n\n /**\n * Enqueue a mock-I/O completion.\n *\n * The `run` callback is **not** invoked immediately — it is held in the\n * pending queue until `runTick(t)` is called with `t >= op.when`.\n */\n enqueueCompletion(op: PendingOp): void {\n this._pending.push(op);\n }\n\n /**\n * Request an asynchronous drain for ops ready at `virtualTime`.\n *\n * Multiple calls in the same turn are coalesced into one microtask and the\n * highest requested virtual time is used.\n */\n requestRunTick(virtualTime: number): void {\n this._requestedTick = this._requestedTick == null\n ? virtualTime\n : Math.max(this._requestedTick, virtualTime);\n\n if (this._autoDrainScheduled) return;\n this._autoDrainScheduled = true;\n\n queueMicrotask(() => {\n this._autoDrainScheduled = false;\n const tick = this._requestedTick;\n this._requestedTick = null;\n if (tick == null) return;\n void this.runTick(tick).catch(() => {\n // Errors still surface to explicit runTick callers; requestRunTick is\n // best-effort and must not throw asynchronously.\n });\n if (this._requestedTick != null) {\n this.requestRunTick(this._requestedTick);\n }\n });\n }\n\n /**\n * Attach (or replace) the clock reference.\n *\n * Integration contract: when the clock advances to time `t`, it should\n * call `await scheduler.runTick(t)`.\n */\n attachClock(clock: IClock): void {\n this._clock = clock;\n }\n\n /**\n * Collect all enqueued ops with `when <= virtualTime`, shuffle them\n * deterministically via the seeded PRNG, then execute their `run()`\n * callbacks **sequentially** in that shuffled order, awaiting one\n * microtask checkpoint (`await Promise.resolve()`) between each.\n */\n async runTick(virtualTime: number): Promise<void> {\n const run = async (): Promise<void> => {\n // Loop to handle cascading completions: ops enqueued during callback\n // execution that are also ready at this virtual time are picked up\n // in the next iteration, preserving causal ordering.\n while (true) {\n // 1. Partition: pull out all ops ready at this tick.\n const ready: PendingOp[] = [];\n const remaining: PendingOp[] = [];\n for (const op of this._pending) {\n if (op.when <= virtualTime) {\n ready.push(op);\n } else {\n remaining.push(op);\n }\n }\n this._pending.length = 0;\n this._pending.push(...remaining);\n\n if (ready.length === 0) break;\n\n // 2. Stable-sort by `when` ascending so earlier ops run first,\n // then shuffle within each same-`when` group via PRNG.\n ready.sort((a, b) => a.when - b.when);\n\n // Group by `when` and shuffle each group.\n let i = 0;\n while (i < ready.length) {\n let j = i;\n while (j < ready.length && ready[j].when === ready[i].when) j++;\n if (j - i > 1) {\n const group = ready.slice(i, j);\n // Sort by id first so the shuffle always starts from the same\n // canonical order regardless of real-event-loop enqueue order.\n // Without this, two ops enqueued as [A,B] vs [B,A] (due to\n // non-deterministic real timing) would produce different shuffle\n // outcomes for the same seed, breaking replay determinism.\n group.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));\n shuffleInPlace(group, this._rng);\n for (let k = 0; k < group.length; k++) {\n ready[i + k] = group[k];\n }\n }\n i = j;\n }\n\n // 3. Execute sequentially, with a microtask boundary between each.\n for (const op of ready) {\n await op.run();\n await Promise.resolve(); // microtask checkpoint\n }\n }\n };\n\n const chained = this._runChain.then(run);\n this._runChain = chained.catch(() => {});\n return chained;\n }\n\n /**\n * Drain **all** pending ops immediately, regardless of their `when`.\n * Useful for test teardown.\n */\n async drain(): Promise<void> {\n await this.runTick(Number.MAX_SAFE_INTEGER);\n }\n\n /** Number of operations currently held in the pending queue. */\n get pendingCount(): number {\n return this._pending.length;\n }\n}\n","/**\n * Mulberry32 — fast 32-bit seeded PRNG.\n * Returns a function yielding numbers in [0, 1).\n *\n * This is a self-contained fallback so `@crashlab/scheduler` has zero\n * hard dependencies. If `@crashlab/random` is available in the monorepo,\n * consumers can pass their own SeededRandom instead.\n */\nexport function mulberry32(seed: number): () => number {\n let s = seed | 0;\n return function next(): number {\n s = (s + 0x6d2b79f5) | 0;\n let t = Math.imul(s ^ (s >>> 15), 1 | s);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Deterministic Fisher-Yates shuffle using a seeded PRNG.\n * Mutates `arr` in place and returns it.\n */\nexport function shuffleInPlace<T>(arr: T[], rng: () => number): T[] {\n for (let i = arr.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1));\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n return arr;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGpD,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,cAAc,CAAuB;gBAEjC,IAAI,GAAE,gBAAqB;IAOvC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAItC;;;;;OAKG;IACH,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAuBzC;;;;;OAKG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIhC;;;;;OAKG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqDjD;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,gEAAgE;IAChE,IAAI,YAAY,IAAI,MAAM,CAEzB;CACF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGpD,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,cAAc,CAAuB;gBAEjC,IAAI,GAAE,gBAAqB;IAOvC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAItC;;;;;OAKG;IACH,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAuBzC;;;;;OAKG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIhC;;;;;OAKG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2DjD;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,gEAAgE;IAChE,IAAI,YAAY,IAAI,MAAM,CAEzB;CACF"}
package/dist/index.js CHANGED
@@ -97,6 +97,7 @@ var Scheduler = class {
97
97
  while (j < ready.length && ready[j].when === ready[i].when) j++;
98
98
  if (j - i > 1) {
99
99
  const group = ready.slice(i, j);
100
+ group.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
100
101
  shuffleInPlace(group, this._rng);
101
102
  for (let k = 0; k < group.length; k++) {
102
103
  ready[i + k] = group[k];
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/prng.ts","../src/index.ts"],"sourcesContent":["/**\n * Mulberry32 — fast 32-bit seeded PRNG.\n * Returns a function yielding numbers in [0, 1).\n *\n * This is a self-contained fallback so `@crashlab/scheduler` has zero\n * hard dependencies. If `@crashlab/random` is available in the monorepo,\n * consumers can pass their own SeededRandom instead.\n */\nexport function mulberry32(seed: number): () => number {\n let s = seed | 0;\n return function next(): number {\n s = (s + 0x6d2b79f5) | 0;\n let t = Math.imul(s ^ (s >>> 15), 1 | s);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Deterministic Fisher-Yates shuffle using a seeded PRNG.\n * Mutates `arr` in place and returns it.\n */\nexport function shuffleInPlace<T>(arr: T[], rng: () => number): T[] {\n for (let i = arr.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1));\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n return arr;\n}\n","import type { IClock, PendingOp } from './types.js';\nimport { mulberry32, shuffleInPlace } from './prng.js';\n\nexport type { IClock, PendingOp } from './types.js';\n\nexport interface SchedulerOptions {\n /** Optional virtual clock. When attached, the scheduler can be called from clock.advance(). */\n clock?: IClock;\n /** Seed for the PRNG that determines execution order of same-tick ops. */\n prngSeed?: number;\n}\n\n/**\n * Cooperative deterministic scheduler.\n *\n * Holds mock-I/O completions until their virtual time arrives, then\n * releases them in a PRNG-determined order — making all macro-level\n * race conditions reproducible across runs with the same seed.\n *\n * **This scheduler does NOT intercept V8 microtasks or Promise internals.**\n * It controls ordering exclusively at mock I/O boundaries.\n */\nexport class Scheduler {\n private _clock: IClock | undefined;\n private readonly _rng: () => number;\n private readonly _pending: PendingOp[] = [];\n private _runChain: Promise<void> = Promise.resolve();\n private _autoDrainScheduled = false;\n private _requestedTick: number | null = null;\n\n constructor(opts: SchedulerOptions = {}) {\n this._clock = opts.clock;\n this._rng = mulberry32(opts.prngSeed ?? 0);\n }\n\n // public API\n\n /**\n * Enqueue a mock-I/O completion.\n *\n * The `run` callback is **not** invoked immediately — it is held in the\n * pending queue until `runTick(t)` is called with `t >= op.when`.\n */\n enqueueCompletion(op: PendingOp): void {\n this._pending.push(op);\n }\n\n /**\n * Request an asynchronous drain for ops ready at `virtualTime`.\n *\n * Multiple calls in the same turn are coalesced into one microtask and the\n * highest requested virtual time is used.\n */\n requestRunTick(virtualTime: number): void {\n this._requestedTick = this._requestedTick == null\n ? virtualTime\n : Math.max(this._requestedTick, virtualTime);\n\n if (this._autoDrainScheduled) return;\n this._autoDrainScheduled = true;\n\n queueMicrotask(() => {\n this._autoDrainScheduled = false;\n const tick = this._requestedTick;\n this._requestedTick = null;\n if (tick == null) return;\n void this.runTick(tick).catch(() => {\n // Errors still surface to explicit runTick callers; requestRunTick is\n // best-effort and must not throw asynchronously.\n });\n if (this._requestedTick != null) {\n this.requestRunTick(this._requestedTick);\n }\n });\n }\n\n /**\n * Attach (or replace) the clock reference.\n *\n * Integration contract: when the clock advances to time `t`, it should\n * call `await scheduler.runTick(t)`.\n */\n attachClock(clock: IClock): void {\n this._clock = clock;\n }\n\n /**\n * Collect all enqueued ops with `when <= virtualTime`, shuffle them\n * deterministically via the seeded PRNG, then execute their `run()`\n * callbacks **sequentially** in that shuffled order, awaiting one\n * microtask checkpoint (`await Promise.resolve()`) between each.\n */\n async runTick(virtualTime: number): Promise<void> {\n const run = async (): Promise<void> => {\n // Loop to handle cascading completions: ops enqueued during callback\n // execution that are also ready at this virtual time are picked up\n // in the next iteration, preserving causal ordering.\n while (true) {\n // 1. Partition: pull out all ops ready at this tick.\n const ready: PendingOp[] = [];\n const remaining: PendingOp[] = [];\n for (const op of this._pending) {\n if (op.when <= virtualTime) {\n ready.push(op);\n } else {\n remaining.push(op);\n }\n }\n this._pending.length = 0;\n this._pending.push(...remaining);\n\n if (ready.length === 0) break;\n\n // 2. Stable-sort by `when` ascending so earlier ops run first,\n // then shuffle within each same-`when` group via PRNG.\n ready.sort((a, b) => a.when - b.when);\n\n // Group by `when` and shuffle each group.\n let i = 0;\n while (i < ready.length) {\n let j = i;\n while (j < ready.length && ready[j].when === ready[i].when) j++;\n if (j - i > 1) {\n const group = ready.slice(i, j);\n shuffleInPlace(group, this._rng);\n for (let k = 0; k < group.length; k++) {\n ready[i + k] = group[k];\n }\n }\n i = j;\n }\n\n // 3. Execute sequentially, with a microtask boundary between each.\n for (const op of ready) {\n await op.run();\n await Promise.resolve(); // microtask checkpoint\n }\n }\n };\n\n const chained = this._runChain.then(run);\n this._runChain = chained.catch(() => {});\n return chained;\n }\n\n /**\n * Drain **all** pending ops immediately, regardless of their `when`.\n * Useful for test teardown.\n */\n async drain(): Promise<void> {\n await this.runTick(Number.MAX_SAFE_INTEGER);\n }\n\n /** Number of operations currently held in the pending queue. */\n get pendingCount(): number {\n return this._pending.length;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/prng.ts","../src/index.ts"],"sourcesContent":["/**\n * Mulberry32 — fast 32-bit seeded PRNG.\n * Returns a function yielding numbers in [0, 1).\n *\n * This is a self-contained fallback so `@crashlab/scheduler` has zero\n * hard dependencies. If `@crashlab/random` is available in the monorepo,\n * consumers can pass their own SeededRandom instead.\n */\nexport function mulberry32(seed: number): () => number {\n let s = seed | 0;\n return function next(): number {\n s = (s + 0x6d2b79f5) | 0;\n let t = Math.imul(s ^ (s >>> 15), 1 | s);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Deterministic Fisher-Yates shuffle using a seeded PRNG.\n * Mutates `arr` in place and returns it.\n */\nexport function shuffleInPlace<T>(arr: T[], rng: () => number): T[] {\n for (let i = arr.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1));\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n return arr;\n}\n","import type { IClock, PendingOp } from './types.js';\nimport { mulberry32, shuffleInPlace } from './prng.js';\n\nexport type { IClock, PendingOp } from './types.js';\n\nexport interface SchedulerOptions {\n /** Optional virtual clock. When attached, the scheduler can be called from clock.advance(). */\n clock?: IClock;\n /** Seed for the PRNG that determines execution order of same-tick ops. */\n prngSeed?: number;\n}\n\n/**\n * Cooperative deterministic scheduler.\n *\n * Holds mock-I/O completions until their virtual time arrives, then\n * releases them in a PRNG-determined order — making all macro-level\n * race conditions reproducible across runs with the same seed.\n *\n * **This scheduler does NOT intercept V8 microtasks or Promise internals.**\n * It controls ordering exclusively at mock I/O boundaries.\n */\nexport class Scheduler {\n private _clock: IClock | undefined;\n private readonly _rng: () => number;\n private readonly _pending: PendingOp[] = [];\n private _runChain: Promise<void> = Promise.resolve();\n private _autoDrainScheduled = false;\n private _requestedTick: number | null = null;\n\n constructor(opts: SchedulerOptions = {}) {\n this._clock = opts.clock;\n this._rng = mulberry32(opts.prngSeed ?? 0);\n }\n\n // public API\n\n /**\n * Enqueue a mock-I/O completion.\n *\n * The `run` callback is **not** invoked immediately — it is held in the\n * pending queue until `runTick(t)` is called with `t >= op.when`.\n */\n enqueueCompletion(op: PendingOp): void {\n this._pending.push(op);\n }\n\n /**\n * Request an asynchronous drain for ops ready at `virtualTime`.\n *\n * Multiple calls in the same turn are coalesced into one microtask and the\n * highest requested virtual time is used.\n */\n requestRunTick(virtualTime: number): void {\n this._requestedTick = this._requestedTick == null\n ? virtualTime\n : Math.max(this._requestedTick, virtualTime);\n\n if (this._autoDrainScheduled) return;\n this._autoDrainScheduled = true;\n\n queueMicrotask(() => {\n this._autoDrainScheduled = false;\n const tick = this._requestedTick;\n this._requestedTick = null;\n if (tick == null) return;\n void this.runTick(tick).catch(() => {\n // Errors still surface to explicit runTick callers; requestRunTick is\n // best-effort and must not throw asynchronously.\n });\n if (this._requestedTick != null) {\n this.requestRunTick(this._requestedTick);\n }\n });\n }\n\n /**\n * Attach (or replace) the clock reference.\n *\n * Integration contract: when the clock advances to time `t`, it should\n * call `await scheduler.runTick(t)`.\n */\n attachClock(clock: IClock): void {\n this._clock = clock;\n }\n\n /**\n * Collect all enqueued ops with `when <= virtualTime`, shuffle them\n * deterministically via the seeded PRNG, then execute their `run()`\n * callbacks **sequentially** in that shuffled order, awaiting one\n * microtask checkpoint (`await Promise.resolve()`) between each.\n */\n async runTick(virtualTime: number): Promise<void> {\n const run = async (): Promise<void> => {\n // Loop to handle cascading completions: ops enqueued during callback\n // execution that are also ready at this virtual time are picked up\n // in the next iteration, preserving causal ordering.\n while (true) {\n // 1. Partition: pull out all ops ready at this tick.\n const ready: PendingOp[] = [];\n const remaining: PendingOp[] = [];\n for (const op of this._pending) {\n if (op.when <= virtualTime) {\n ready.push(op);\n } else {\n remaining.push(op);\n }\n }\n this._pending.length = 0;\n this._pending.push(...remaining);\n\n if (ready.length === 0) break;\n\n // 2. Stable-sort by `when` ascending so earlier ops run first,\n // then shuffle within each same-`when` group via PRNG.\n ready.sort((a, b) => a.when - b.when);\n\n // Group by `when` and shuffle each group.\n let i = 0;\n while (i < ready.length) {\n let j = i;\n while (j < ready.length && ready[j].when === ready[i].when) j++;\n if (j - i > 1) {\n const group = ready.slice(i, j);\n // Sort by id first so the shuffle always starts from the same\n // canonical order regardless of real-event-loop enqueue order.\n // Without this, two ops enqueued as [A,B] vs [B,A] (due to\n // non-deterministic real timing) would produce different shuffle\n // outcomes for the same seed, breaking replay determinism.\n group.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));\n shuffleInPlace(group, this._rng);\n for (let k = 0; k < group.length; k++) {\n ready[i + k] = group[k];\n }\n }\n i = j;\n }\n\n // 3. Execute sequentially, with a microtask boundary between each.\n for (const op of ready) {\n await op.run();\n await Promise.resolve(); // microtask checkpoint\n }\n }\n };\n\n const chained = this._runChain.then(run);\n this._runChain = chained.catch(() => {});\n return chained;\n }\n\n /**\n * Drain **all** pending ops immediately, regardless of their `when`.\n * Useful for test teardown.\n */\n async drain(): Promise<void> {\n await this.runTick(Number.MAX_SAFE_INTEGER);\n }\n\n /** Number of operations currently held in the pending queue. */\n get pendingCount(): number {\n return this._pending.length;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crashlab/scheduler",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",