Use case · Testing
Drive virtual time in a test
Run the same engine on a VirtualClock. Advancing time wakes sleepers in deadline order, each observing now equal to its own deadline — and advancing before a sleeper registers still wakes it, so tests are race-free with no real waiting.
A race-free wait
Construct a VirtualClock, spawn work that sleeps, then advance. The sleeper wakes when now reaches its deadline and observes exactly that value.
use std::sync::Arc;
use ferro_replay::{Clock, VirtualClock};
#[tokio::test]
async fn wakes_at_its_deadline() {
let clock = Arc::new(VirtualClock::new(1_000));
let c = clock.clone();
let task = tokio::spawn(async move {
c.sleep_until(2_000).await;
c.now_ns()
});
// Even if this runs before the task registers, the wait still fires.
clock.advance_to(2_000).await;
assert_eq!(task.await.unwrap(), 2_000);
}Deadline-ordered wakes
Multiple sleepers wake in (deadline, registration-order) order, and each observes now equal to its own deadline — never a later one. Time never runs ahead of the timers it fires.
let clock = Arc::new(VirtualClock::new(0));
let mut handles = Vec::new();
for deadline in [3_000i64, 1_000, 2_000] {
let c = clock.clone();
handles.push(tokio::spawn(async move {
c.sleep_until(deadline).await;
(deadline, c.now_ns()) // (d, d) — observed == scheduled
}));
}
clock.advance_to(3_000).await;
// Fires in deadline order: (1000,1000), (2000,2000), (3000,3000).The strict ordering guarantee holds on a current-thread runtime — the default #[tokio::test] flavor. A multi-thread runtime would reintroduce wake-order races.
Cancelled sleeps cost nothing
A sleep cancelled by a select! arm (its receiver dropped) is discarded on pop without a wake or a yield — O(1) each, even when dead waiters accumulate one-per-event on a busy timeline. A live sleeper behind a pile of dead ones still observes its own deadline.