1use std::time::Duration;
2
3use shadow_shim_helper_rs::{emulated_time::EmulatedTime, simulation_time::SimulationTime};
4
5pub struct Cpu {
9    simulated_frequency: u64,
10    native_frequency: u64,
11    threshold: Option<SimulationTime>,
12    precision: Option<SimulationTime>,
13    now: EmulatedTime,
14    time_cpu_available: EmulatedTime,
15}
16
17impl Cpu {
18    pub fn new(
24        simulated_frequency: u64,
25        native_frequency: u64,
26        threshold: Option<SimulationTime>,
27        precision: Option<SimulationTime>,
28    ) -> Self {
29        if let Some(precision) = precision {
30            assert!(precision > SimulationTime::ZERO)
31        }
32
33        Self {
34            simulated_frequency,
35            native_frequency,
36            threshold,
37            precision,
38            now: EmulatedTime::MIN,
39            time_cpu_available: EmulatedTime::MIN,
40        }
41    }
42
43    pub fn update_time(&mut self, now: EmulatedTime) {
45        self.now = now;
46    }
47
48    pub fn add_delay(&mut self, native_delay: Duration) {
50        let cycles = native_delay
53            .as_nanos()
54            .checked_mul(self.native_frequency as u128)
55            .unwrap();
56        let simulated_delay_nanos = cycles / (self.simulated_frequency as u128);
57        let mut adjusted_delay =
60            SimulationTime::from_nanos(simulated_delay_nanos.try_into().unwrap());
61
62        if let Some(precision) = self.precision {
64            let remainder = adjusted_delay % precision;
65
66            adjusted_delay -= remainder;
68
69            let half_precision = precision / 2;
71            if remainder >= half_precision {
72                adjusted_delay += precision;
74            }
75        }
76
77        self.time_cpu_available += adjusted_delay;
78    }
79
80    pub fn delay(&self) -> SimulationTime {
82        let Some(threshold) = self.threshold else {
83            return SimulationTime::ZERO;
84        };
85        let Some(built_up_delay) = self.time_cpu_available.checked_duration_since(&self.now) else {
86            return SimulationTime::ZERO;
87        };
88        if built_up_delay > threshold {
89            built_up_delay
90        } else {
91            SimulationTime::ZERO
92        }
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    const MHZ: u64 = 1_000_000;
101
102    #[test]
103    fn no_threshold_never_delays() {
104        let mut cpu = Cpu::new(1000 * MHZ, 1000 * MHZ, None, None);
105        assert_eq!(cpu.delay(), SimulationTime::ZERO);
106
107        cpu.add_delay(Duration::from_secs(1));
108        assert_eq!(cpu.delay(), SimulationTime::ZERO);
109    }
110
111    #[test]
112    fn basic_delay() {
113        let mut cpu = Cpu::new(
114            1000 * MHZ,
115            1000 * MHZ,
116            Some(SimulationTime::NANOSECOND),
117            None,
118        );
119        assert_eq!(cpu.delay(), SimulationTime::ZERO);
120
121        cpu.update_time(EmulatedTime::UNIX_EPOCH);
123
124        cpu.add_delay(Duration::from_secs(1));
126
127        assert_eq!(cpu.delay(), SimulationTime::SECOND);
129
130        cpu.update_time(EmulatedTime::UNIX_EPOCH + SimulationTime::from_millis(100));
132        assert_eq!(cpu.delay(), SimulationTime::from_millis(900));
133
134        cpu.update_time(EmulatedTime::UNIX_EPOCH + SimulationTime::from_secs(1));
136        assert_eq!(cpu.delay(), SimulationTime::ZERO);
137
138        cpu.update_time(EmulatedTime::UNIX_EPOCH + SimulationTime::from_secs(2));
140        assert_eq!(cpu.delay(), SimulationTime::ZERO);
141    }
142
143    #[test]
144    fn no_overflow() {
145        let mut cpu = Cpu::new(
147            1_000_000 * MHZ,
148            1_000_000 * MHZ,
149            Some(SimulationTime::NANOSECOND),
150            None,
151        );
152
153        cpu.add_delay(Duration::from_secs(3600));
155
156        assert_eq!(cpu.delay(), SimulationTime::from_secs(3600));
157    }
158
159    #[test]
160    fn faster_native() {
161        let mut cpu = Cpu::new(
162            1000 * MHZ,
163            1100 * MHZ,
164            Some(SimulationTime::NANOSECOND),
165            None,
166        );
167        assert_eq!(cpu.delay(), SimulationTime::ZERO);
168
169        cpu.add_delay(Duration::from_millis(1000));
171        assert_eq!(cpu.delay(), SimulationTime::from_millis(1100));
172    }
173
174    #[test]
175    fn faster_simulated() {
176        let mut cpu = Cpu::new(
177            1100 * MHZ,
178            1000 * MHZ,
179            Some(SimulationTime::NANOSECOND),
180            None,
181        );
182        assert_eq!(cpu.delay(), SimulationTime::ZERO);
183
184        cpu.add_delay(Duration::from_millis(1100));
186        assert_eq!(cpu.delay(), SimulationTime::from_millis(1000));
187    }
188
189    #[test]
190    fn thresholded() {
191        let threshold = SimulationTime::from_millis(100);
192        let mut cpu = Cpu::new(1000 * MHZ, 1000 * MHZ, Some(threshold), None);
193        assert_eq!(cpu.delay(), SimulationTime::ZERO);
194
195        cpu.add_delay(Duration::from_millis(1));
197
198        assert_eq!(cpu.delay(), SimulationTime::ZERO);
200
201        cpu.add_delay(Duration::from_millis(100));
203
204        assert_eq!(cpu.delay(), SimulationTime::from_millis(101));
206    }
207
208    #[test]
209    fn round_lt_half_precision() {
210        let precision = SimulationTime::from_millis(100);
211        let mut cpu = Cpu::new(
212            1000 * MHZ,
213            1000 * MHZ,
214            Some(SimulationTime::NANOSECOND),
215            Some(precision),
216        );
217        cpu.add_delay(Duration::from_millis(149));
218        assert_eq!(cpu.delay(), SimulationTime::from_millis(100));
219    }
220
221    #[test]
222    fn round_half_precision() {
223        let precision = SimulationTime::from_millis(100);
224        let mut cpu = Cpu::new(
225            1000 * MHZ,
226            1000 * MHZ,
227            Some(SimulationTime::NANOSECOND),
228            Some(precision),
229        );
230        cpu.add_delay(Duration::from_millis(150));
231        assert_eq!(cpu.delay(), SimulationTime::from_millis(200));
232    }
233
234    #[test]
235    fn round_gt_half_precision() {
236        let precision = SimulationTime::from_millis(100);
237        let mut cpu = Cpu::new(
238            1000 * MHZ,
239            1000 * MHZ,
240            Some(SimulationTime::NANOSECOND),
241            Some(precision),
242        );
243        cpu.add_delay(Duration::from_millis(151));
244        assert_eq!(cpu.delay(), SimulationTime::from_millis(200));
245    }
246}