cli/
clock.rs

1//! Simulation of elapsed time in the simulated system.
2use cpu::Context;
3use std::time::{Duration, SystemTime};
4use tracing::{Level, event};
5
6/// Clock is a simulated system clock.  Its run rate may be real-time
7/// (i.e. one simulated second per actual wall-clock second) or it may
8/// run faster or slower than real-time.
9///
10/// The clock keeps track of how many of its cycles have been
11/// "consumed" by callers.  Callers requiring more clock cycles will
12/// find that their calls to [`Clock::consume`] block so that their
13/// average consumption of cycles matches the simulated clock rate.
14///
15/// On the other hand, if the simulated clock produces ticks very
16/// rapidly (for example because it is set up to run 1,000,000x "real"
17/// time) then callers will never block and hence can proceed as fast
18/// as they are able.
19pub trait Clock {
20    /// Retrieves the current (simulated) time.
21    fn now(&self) -> Duration;
22
23    /// The caller calls `consume` to simulate the passing of a
24    /// duration `interval`.  The returned value is the interval
25    /// after which it is next OK to call `consume`.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// use std::thread::sleep;
31    /// use std::time::Duration;
32    /// use cpu::Clock;
33    ///
34    /// fn g<C: Clock>(clk: &mut C) {
35    ///   // We just performed an action which would have taken
36    ///   // one millisecond on the simulated machine.
37    ///   clk.consume(&Duration::from_millis(1));
38    /// }
39    /// ```
40    fn consume(&mut self, inteval: &Duration);
41}
42
43/// BasicClock provides a simulated clock.
44///
45/// # Examples
46/// ```
47/// use std::time::Duration;
48/// use cpu::BasicClock;
49/// use cpu::Clock;
50/// let mut clk = BasicClock::new();
51/// clk.consume(&Duration::from_micros(12));
52/// ```
53///
54///
55#[derive(Debug)]
56pub struct BasicClock {
57    /// Elapsed time as measured by the simulated clock.
58    simulator_elapsed: Duration,
59
60    /// Origin time for the simulated RTC.  When the SystemTime
61    /// implementation appears to have advanced non-monotonically, we
62    /// update wall_clock_time_origin.
63    wall_clock_time_origin: SystemTime,
64}
65
66impl BasicClock {
67    pub fn new() -> BasicClock {
68        BasicClock {
69            simulator_elapsed: Duration::new(0, 0),
70            wall_clock_time_origin: SystemTime::now(),
71        }
72    }
73
74    pub fn advance_to_simulated_time(&mut self, when: Duration) {
75        self.simulator_elapsed = when;
76    }
77
78    pub fn make_fresh_context(&mut self) -> Context {
79        let real_now = SystemTime::now();
80        let elapsed = match real_now.duration_since(self.wall_clock_time_origin) {
81            Ok(delta) => delta,
82            Err(_) => {
83                // simulate an RTC reset.
84                event!(
85                    Level::WARN,
86                    "System time went backward, simulating RTC reset"
87                );
88                self.wall_clock_time_origin = real_now;
89                Duration::new(0, 0)
90            }
91        };
92        Context {
93            simulated_time: self.simulator_elapsed,
94            real_elapsed_time: elapsed,
95        }
96    }
97}
98
99impl Default for BasicClock {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl Clock for BasicClock {
106    fn now(&self) -> Duration {
107        self.simulator_elapsed
108    }
109
110    fn consume(&mut self, interval: &Duration) {
111        self.simulator_elapsed += *interval;
112    }
113}