Skip to content

Determinism Model ​

Clockworks is designed so simulated scheduler time deterministically drives timers/timeouts, while wall time can be manipulated independently to model clock skew, rewinds, and other wall-clock effects.

Two notions of time ​

When using SimulatedTimeProvider, there are two separate “clocks”:

  • Wall time (GetUtcNow()): controllable, may move backwards via SetUtcNow(...).
  • Scheduler time (timers + GetTimestamp()): monotonic, advances only via Advance(...).

This split is intentional:

  • Timers/timeouts are driven by scheduler time so they’re reproducible and ordered deterministically.
  • Wall time remains available for scenarios where you want to simulate clock drift, skew, or rewinds without changing timer ordering.

Deterministic timer behavior ​

Timers created via SimulatedTimeProvider.CreateTimer(...) fire when scheduler time reaches their due time. Advancing time:

csharp
var tp = new SimulatedTimeProvider();
var fired = 0;

using var timer = tp.CreateTimer(_ => fired++, null, TimeSpan.FromSeconds(1), Timeout.InfiniteTimeSpan);

tp.Advance(TimeSpan.FromSeconds(1));
// fired == 1

Periodic timers default to coalescing on large time jumps: if you advance by a long duration, the timer is rescheduled from “now” (rather than firing repeatedly for every missed period). This keeps simulations fast and avoids unbounded callback loops.

Timeout determinism ​

Timeouts.CreateTimeout(...) and Timeouts.CreateTimeoutHandle(...) schedule cancellation using the provided TimeProvider. If that provider is simulated, timeouts are fast-forwardable and fully deterministic.

Released under the MIT License.