Use case · Wiring

Inject the clock into an engine

Construct one clock at the composition root and thread it through the engine as Arc<dyn Clock>. Every time read goes through it — which is exactly what makes the later swap to a virtual timeline a no-op for the rest of the code.

Construct once, share widely

The clock is a dependency, not an ambient. Build it where you wire the engine together, and pass clones of the Arc to each component that needs time.

use std::sync::Arc;
use ferro_replay::{Clock, SystemClock};

let clock: Arc<dyn Clock> = Arc::new(SystemClock::new());

let producer = Producer::new(clock.clone());
let consumer = Consumer::new(clock.clone());
let heartbeat = Heartbeat::new(clock.clone());

Read time through the trait

Inside a component, store the clock and read through it. Nothing reaches for SystemTime or Instant directly.

use std::sync::Arc;
use std::time::Duration;
use ferro_replay::Clock;

struct Consumer {
    clock: Arc<dyn Clock>,
}

impl Consumer {
    async fn run(&self) {
        let started_ns = self.clock.now_ns();

        // I/O-timeout race / retry backoff — relative sleep.
        self.clock.sleep(Duration::from_millis(250)).await;

        let elapsed_ns = self.clock.now_ns() - started_ns;
        // ...
    }
}
Cancel-safety

Wrap waits in a biased tokio::select! with your shutdown signal. sleep_until is cancel-safe: a cancelled arm drops the wait cleanly and re-arming the same deadline still fires correctly.

The payoff

Once time is injected, switching to deterministic replay or a test is a single substitution at the root — see Drive virtual time and Deterministic replay. The wallclock gate is what guarantees no component quietly broke the contract.