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}