Skip to main content

areev_run/
clock.rs

1//! The ONE place the runtime reads the wall clock. Every reading flows into
2//! the scheduler as `EventIn::ClockReading` and is journaled in decision
3//! records — the scheduler core cannot tell the time (its clippy.toml and
4//! the CI dep-tree check enforce it), so this seam is the whole clock
5//! surface, and replay/verify substitute journaled readings here.
6
7/// A clock the driver reads. `SystemClock` for live runs; tests and
8/// verify-mode inject fixed sequences.
9pub trait Clock: Send + Sync {
10    fn now_ms(&self) -> u64;
11}
12
13/// Live wall clock.
14pub struct SystemClock;
15
16impl Clock for SystemClock {
17    fn now_ms(&self) -> u64 {
18        std::time::SystemTime::now()
19            .duration_since(std::time::UNIX_EPOCH)
20            .map(|d| d.as_millis() as u64)
21            .unwrap_or(0)
22    }
23}
24
25/// A scripted clock for tests and replay: returns the given readings in
26/// order, then keeps returning the last (a run that reads more often than
27/// the script anticipated must not go backwards).
28pub struct ScriptedClock {
29    readings: std::sync::Mutex<(Vec<u64>, usize)>,
30}
31
32impl ScriptedClock {
33    pub fn new(readings: Vec<u64>) -> Self {
34        ScriptedClock { readings: std::sync::Mutex::new((readings, 0)) }
35    }
36}
37
38impl Clock for ScriptedClock {
39    fn now_ms(&self) -> u64 {
40        let mut g = self.readings.lock().unwrap();
41        let (readings, idx) = &mut *g;
42        let v = readings.get(*idx).copied().or_else(|| readings.last().copied()).unwrap_or(0);
43        if *idx < readings.len() {
44            *idx += 1;
45        }
46        v
47    }
48}