Skip to main content

custom_clock/
custom_clock.rs

1//! Plugging in a custom [`Clock`] so the *policy* measures elapsed time against
2//! a host-controlled source instead of the wall clock. Here the "sleep" simply
3//! advances the same virtual clock, so the whole retry session is deterministic
4//! and runs instantly — exactly the pattern you'd use in a `no_std` engine, a
5//! simulation, or a consensus test.
6//!
7//! Run with:
8//!
9//! ```text
10//! cargo run --example custom_clock
11//! ```
12
13use std::cell::Cell;
14use std::ops::ControlFlow;
15use std::time::Duration;
16
17use sisyphus::{retry_sync, Clock, ExponentialBackoff, PolicyExt, RetryError};
18
19/// A fully deterministic virtual clock measured in milliseconds.
20struct VirtualClock {
21    now_ms: Cell<u64>,
22}
23
24impl VirtualClock {
25    fn new() -> Self {
26        Self {
27            now_ms: Cell::new(0),
28        }
29    }
30    fn advance(&self, by: Duration) {
31        self.now_ms.set(self.now_ms.get() + by.as_millis() as u64);
32    }
33}
34
35impl Clock for VirtualClock {
36    type Instant = u64;
37
38    fn now(&self) -> u64 {
39        self.now_ms.get()
40    }
41
42    fn duration_since(&self, earlier: u64, now: u64) -> Duration {
43        Duration::from_millis(now.saturating_sub(earlier))
44    }
45}
46
47fn main() {
48    let clock = VirtualClock::new();
49
50    // Give up once 2 seconds of *virtual* time have elapsed.
51    let policy = ExponentialBackoff::new(Duration::from_millis(100), 2.0)
52        .max_elapsed_time(&clock, Duration::from_secs(2));
53
54    let mut attempt = 0u32;
55    let outcome: Result<(), RetryError<u32>> = retry_sync(
56        policy,
57        || {
58            attempt += 1;
59            // This operation never succeeds, so we lean on the time budget.
60            ControlFlow::Continue(attempt)
61        },
62        // "Sleeping" just moves virtual time forward — no real waiting.
63        |delay| {
64            clock.advance(delay);
65            println!("slept {delay:?}, virtual t = {}ms", clock.now());
66        },
67    );
68
69    match outcome {
70        Ok(()) => unreachable!("operation never breaks"),
71        Err(RetryError::Exhausted(last)) => {
72            println!(
73                "budget exhausted after {last} attempts at virtual t = {}ms",
74                clock.now()
75            );
76        }
77    }
78}