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}