rust_automata/
clock.rs

1//! Provide structs for measuring time.
2
3use crate::timestamp::Timestamp;
4use crate::timestamp::TimestampDelta;
5use std::fmt;
6
7/// A trait for providing the current time.
8pub trait Clock: Send + Sync {
9    fn now(&self) -> Timestamp;
10    fn clone_box(&self) -> Box<dyn Clock>;
11}
12
13/// A time provider that uses the system's clock.
14#[derive(Clone, Debug)]
15pub struct SystemClock;
16
17impl Clock for SystemClock {
18    fn now(&self) -> Timestamp {
19        // We use chrono here as it is platform agnostic.
20        Timestamp::from(chrono::Utc::now())
21    }
22    fn clone_box(&self) -> Box<dyn Clock> {
23        Box::new(self.clone())
24    }
25}
26
27use std::sync::{Arc, Mutex};
28
29/// A time provider that can be mocked to advance time.
30#[derive(Clone, Debug, Default)]
31pub struct ManualClock {
32    current_time: Arc<Mutex<Timestamp>>,
33}
34
35impl ManualClock {
36    pub fn new() -> Self {
37        let zero_time = Timestamp::zero();
38        Self {
39            current_time: Arc::new(Mutex::new(zero_time)),
40        }
41    }
42
43    pub fn advance_by(&self, duration: TimestampDelta) {
44        assert!(duration > TimestampDelta::zero());
45        let mut time = self.current_time.lock().unwrap();
46        *time = *time + duration;
47    }
48
49    pub fn advance_to(&self, time: Timestamp) {
50        let mut current_time = self.current_time.lock().unwrap();
51        *current_time = time;
52    }
53}
54
55impl Clock for ManualClock {
56    fn now(&self) -> Timestamp {
57        *self.current_time.lock().unwrap()
58    }
59
60    fn clone_box(&self) -> Box<dyn Clock> {
61        Box::new(self.clone())
62    }
63}
64
65#[cfg(test)]
66pub mod tests {
67    use super::*;
68
69    #[test]
70    fn advance_time_in_mocked_time_provider() {
71        struct Component {
72            times: Vec<Timestamp>,
73            provider: Box<dyn Clock>,
74        }
75
76        impl Component {
77            fn new(provider: Box<dyn Clock>) -> Self {
78                Self {
79                    times: Vec::new(),
80                    provider,
81                }
82            }
83            fn append_now(&mut self) {
84                self.times.push(self.provider.now());
85            }
86        }
87
88        let clock = ManualClock::new();
89        let mut a = Component::new(clock.clone_box());
90        let mut b = Component::new(clock.clone_box());
91
92        a.append_now(); // t=0
93        clock.advance_by(TimestampDelta::from_secs(1)); // t=0 -> t=1
94        a.append_now(); // t=1
95        b.append_now(); // t=1
96        clock.advance_by(TimestampDelta::from_secs(2)); // t=1 -> t=3
97        a.append_now(); // t=3
98        clock.advance_by(TimestampDelta::from_secs(1)); // t=3 -> t=4
99        a.append_now(); // t=4
100        b.append_now(); // t=4
101
102        assert_eq!(
103            a.times.iter().map(|t| t.as_secs()).collect::<Vec<_>>(),
104            vec![0, 1, 3, 4]
105        );
106        assert_eq!(
107            b.times.iter().map(|t| t.as_secs()).collect::<Vec<_>>(),
108            vec![1, 4]
109        );
110    }
111
112    #[test]
113    fn advance_time_across_threads_simplified() {
114        use std::sync::{mpsc::sync_channel, Arc, Barrier};
115
116        let clock = ManualClock::new();
117        let worker_count = 4;
118        let steps = 4;
119
120        // A barrier that synchronizes the main thread plus all worker threads.
121        // Use two-phase barrier synchronization.
122        let barrier = Arc::new(Barrier::new(worker_count + 1));
123        let (sender, receiver) = sync_channel(worker_count);
124        for _ in 0..worker_count {
125            let clock = clock.clone_box();
126            let barrier = barrier.clone();
127            let sender = sender.clone();
128
129            std::thread::spawn(move || {
130                let mut times = Vec::with_capacity(steps);
131                for _ in 0..steps {
132                    barrier.wait(); // phase one: ensure that the shared state is ready.
133                    times.push(clock.now()); // record the current time
134                    barrier.wait(); // phase two: ensure that all threads have completed their work.
135                }
136                sender.send(times).unwrap();
137            });
138        }
139
140        for _ in 0..steps {
141            clock.advance_by(TimestampDelta::from_secs(1));
142            barrier.wait(); // let worker threads read the updated time
143            barrier.wait(); // wait until they finish recording before next iteration
144        }
145
146        // Collect and check results.
147        let results: Vec<Vec<Timestamp>> = (0..worker_count)
148            .map(|_| receiver.recv().unwrap().into_iter().collect())
149            .collect();
150
151        for res in results {
152            assert_eq!(
153                res,
154                (1..steps + 1)
155                    .map(|i| Timestamp::from_secs(i as i64))
156                    .collect::<Vec<_>>(),
157                "All thread components should show consistent time steps"
158            );
159        }
160    }
161}
162
163/// Measure elapsed time.
164pub struct Stopwatch {
165    clock: Box<dyn Clock>,
166    start_time: Timestamp,
167}
168
169impl fmt::Debug for Stopwatch {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        f.debug_struct("Stopwatch")
172            .field("clock", &self.clock.now()) // Just show a placeholder
173            .field("start_time", &self.start_time)
174            .finish()
175    }
176}
177
178impl Stopwatch {
179    pub fn new(clock: Box<dyn Clock>) -> Self {
180        Self {
181            start_time: clock.now(),
182            clock,
183        }
184    }
185
186    pub fn elapsed(&self) -> TimestampDelta {
187        self.clock.now() - self.start_time
188    }
189
190    pub fn reset(&mut self) {
191        self.start_time = self.clock.now();
192    }
193}
194
195/// A timer that can be used to measure the elapsed time and check if timeout has occurred.
196#[derive(Debug)]
197pub struct Timer {
198    stopwatch: Stopwatch,
199    delay: TimestampDelta,
200}
201
202impl Timer {
203    pub fn new(clock: Box<dyn Clock>, delay: TimestampDelta) -> Self {
204        Self {
205            delay,
206            stopwatch: Stopwatch::new(clock),
207        }
208    }
209
210    pub fn is_timeout(&self) -> bool {
211        self.stopwatch.elapsed() >= self.delay
212    }
213
214    pub fn elapsed(&self) -> TimestampDelta {
215        self.stopwatch.elapsed()
216    }
217
218    pub fn reset(&mut self) {
219        self.stopwatch.reset();
220    }
221}