shadow_shim_helper_rs/
emulated_time.rs

1/*!
2Deals with instances of time in a Shadow simulation.
3*/
4
5use std::sync::atomic::{AtomicU64, Ordering};
6
7use vasi::VirtualAddressSpaceIndependent;
8
9use crate::simulation_time::{self, CSimulationTime, SimulationTime};
10
11/// An instant in time (analagous to std::time::Instant) in the Shadow
12/// simulation.
13// Internally represented as Duration since the Unix Epoch.
14#[derive(
15    Copy, Clone, Eq, PartialEq, Debug, PartialOrd, Ord, Hash, VirtualAddressSpaceIndependent,
16)]
17#[repr(C)]
18pub struct EmulatedTime(CEmulatedTime);
19
20/// Emulation time in nanoseconds.
21///
22/// Allows for a consistent representation of time throughput the simulator.
23/// Emulation time is the simulation time plus the EMULATION_TIME_OFFSET. This
24/// type allows us to explicitly distinguish each type of time in the code.
25pub type CEmulatedTime = u64;
26
27// Duplicated from the EMULATED_TIME_OFFSET macro in definitions.h.
28pub const SIMULATION_START_SEC: u64 = 946684800u64;
29pub const EMUTIME_INVALID: CEmulatedTime = u64::MAX;
30pub const EMUTIME_MAX: CEmulatedTime = u64::MAX - 1;
31pub const EMUTIME_MIN: CEmulatedTime = 0u64;
32
33/// The number of nanoseconds from the epoch to January 1st, 2000 at 12:00am UTC.
34/// This is used to emulate to applications that we are in a recent time.
35// cbindgen won't do the constant propagation here. We use the static assertion below
36// to ensure this definition is equal to the intended canonical definition.
37pub const EMUTIME_SIMULATION_START: CEmulatedTime = 946684800u64 * 1_000_000_000u64;
38const _: () =
39    assert!(EMUTIME_SIMULATION_START == SIMULATION_START_SEC * simulation_time::SIMTIME_ONE_SECOND);
40
41/// Duplicated as EmulatedTime::UNIX_EPOCH
42pub const EMUTIME_UNIX_EPOCH: CEmulatedTime = 0u64;
43
44impl EmulatedTime {
45    /// The start time of the simulation - 00:00:00 UTC on 1 January, 2000.
46    pub const SIMULATION_START: Self = Self(EMUTIME_SIMULATION_START);
47    /// The  Unix epoch (00:00:00 UTC on 1 January 1970)
48    pub const UNIX_EPOCH: Self = Self(0);
49
50    pub const MAX: Self = Self(EMUTIME_MAX);
51    pub const MIN: Self = Self(0);
52
53    /// Get the instance corresponding to `val` SimulationTime units since the Unix Epoch.
54    pub const fn from_c_emutime(val: CEmulatedTime) -> Option<Self> {
55        if val == EMUTIME_INVALID || val > EMUTIME_MAX {
56            None
57        } else {
58            Some(Self(val))
59        }
60    }
61
62    /// Convert to number of SimulationTime units since the Unix Epoch.
63    pub const fn to_c_emutime(val: Option<Self>) -> CEmulatedTime {
64        match val {
65            Some(v) => v.0,
66            None => EMUTIME_INVALID,
67        }
68    }
69
70    /// Get the instant corresponding to `val` time units since the simulation began.
71    pub fn from_abs_simtime(val: SimulationTime) -> Self {
72        Self::SIMULATION_START + val
73    }
74
75    /// Convert to the SimulationTime since the simulation began.
76    pub fn to_abs_simtime(self) -> SimulationTime {
77        self.duration_since(&Self::SIMULATION_START)
78    }
79
80    /// Returns the duration since `earlier`, or panics if `earlier` is after `self`, or
81    /// if the difference can't be represented as SimulationTime.
82    pub fn duration_since(&self, earlier: &EmulatedTime) -> SimulationTime {
83        self.checked_duration_since(earlier).unwrap()
84    }
85
86    /// Returns the duration since `earlier`, or `None` if `earlier` is after `self`.
87    pub fn checked_duration_since(&self, earlier: &EmulatedTime) -> Option<SimulationTime> {
88        let d = self.0.checked_sub(earlier.0)?;
89        SimulationTime::from_c_simtime(d)
90    }
91
92    /// Returns the duration since `earlier`, or 0 if `earlier` is after `self`.
93    pub fn saturating_duration_since(&self, earlier: &EmulatedTime) -> SimulationTime {
94        self.checked_duration_since(earlier)
95            .unwrap_or(SimulationTime::ZERO)
96    }
97
98    pub fn checked_add(&self, duration: SimulationTime) -> Option<EmulatedTime> {
99        EmulatedTime::from_c_emutime(self.0.checked_add(CSimulationTime::from(duration))?)
100    }
101
102    pub fn checked_sub(&self, duration: SimulationTime) -> Option<EmulatedTime> {
103        EmulatedTime::from_c_emutime(self.0.checked_sub(CSimulationTime::from(duration))?)
104    }
105
106    pub fn saturating_add(&self, duration: SimulationTime) -> EmulatedTime {
107        match self.checked_add(duration) {
108            Some(later) => later,
109            None => EmulatedTime::MAX,
110        }
111    }
112
113    pub fn saturating_sub(&self, duration: SimulationTime) -> EmulatedTime {
114        match self.checked_sub(duration) {
115            Some(earlier) => earlier,
116            None => EmulatedTime::SIMULATION_START,
117        }
118    }
119}
120
121impl std::ops::Add<SimulationTime> for EmulatedTime {
122    type Output = EmulatedTime;
123
124    fn add(self, other: SimulationTime) -> Self {
125        self.checked_add(other).unwrap()
126    }
127}
128
129impl std::ops::AddAssign<SimulationTime> for EmulatedTime {
130    fn add_assign(&mut self, rhs: SimulationTime) {
131        *self = *self + rhs;
132    }
133}
134
135impl std::ops::Sub<SimulationTime> for EmulatedTime {
136    type Output = EmulatedTime;
137
138    fn sub(self, other: SimulationTime) -> Self {
139        self.checked_sub(other).unwrap()
140    }
141}
142
143impl std::ops::Sub<EmulatedTime> for EmulatedTime {
144    type Output = SimulationTime;
145
146    fn sub(self, other: EmulatedTime) -> Self::Output {
147        self.duration_since(&other)
148    }
149}
150
151impl std::ops::SubAssign<SimulationTime> for EmulatedTime {
152    fn sub_assign(&mut self, rhs: SimulationTime) {
153        *self = self.checked_sub(rhs).unwrap();
154    }
155}
156
157impl tcp::util::time::Instant for EmulatedTime {
158    type Duration = SimulationTime;
159
160    #[inline]
161    fn duration_since(&self, earlier: Self) -> Self::Duration {
162        self.duration_since(&earlier)
163    }
164
165    #[inline]
166    fn saturating_duration_since(&self, earlier: Self) -> Self::Duration {
167        self.saturating_duration_since(&earlier)
168    }
169
170    #[inline]
171    fn checked_duration_since(&self, earlier: Self) -> Option<Self::Duration> {
172        self.checked_duration_since(&earlier)
173    }
174
175    #[inline]
176    fn checked_add(&self, duration: Self::Duration) -> Option<Self> {
177        self.checked_add(duration)
178    }
179
180    #[inline]
181    fn checked_sub(&self, duration: Self::Duration) -> Option<Self> {
182        self.checked_sub(duration)
183    }
184}
185
186pub mod export {
187    use super::*;
188
189    #[unsafe(no_mangle)]
190    pub extern "C-unwind" fn emutime_add_simtime(
191        lhs: CEmulatedTime,
192        rhs: CSimulationTime,
193    ) -> CEmulatedTime {
194        let Some(lhs) = EmulatedTime::from_c_emutime(lhs) else {
195            return EmulatedTime::to_c_emutime(None);
196        };
197        let Some(rhs) = SimulationTime::from_c_simtime(rhs) else {
198            return EmulatedTime::to_c_emutime(None);
199        };
200        let sum = lhs.checked_add(rhs);
201        EmulatedTime::to_c_emutime(sum)
202    }
203
204    #[unsafe(no_mangle)]
205    pub extern "C-unwind" fn emutime_sub_emutime(
206        lhs: CEmulatedTime,
207        rhs: CEmulatedTime,
208    ) -> CSimulationTime {
209        let Some(lhs) = EmulatedTime::from_c_emutime(lhs) else {
210            return EmulatedTime::to_c_emutime(None);
211        };
212        let Some(rhs) = EmulatedTime::from_c_emutime(rhs) else {
213            return EmulatedTime::to_c_emutime(None);
214        };
215        let diff = lhs.checked_duration_since(&rhs);
216        SimulationTime::to_c_simtime(diff)
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223    use crate::simulation_time;
224
225    #[test]
226    fn test_from_emu_time() {
227        let emu_time =
228            5 * simulation_time::SIMTIME_ONE_MINUTE + 7 * simulation_time::SIMTIME_ONE_MILLISECOND;
229        let rust_time = EmulatedTime::from_c_emutime(emu_time).unwrap();
230
231        assert_eq!(
232            rust_time
233                .duration_since(&EmulatedTime::UNIX_EPOCH)
234                .as_secs(),
235            5 * 60
236        );
237        assert_eq!(
238            rust_time
239                .duration_since(&EmulatedTime::UNIX_EPOCH)
240                .as_millis(),
241            5 * 60 * 1_000 + 7
242        );
243    }
244
245    #[test]
246    fn test_to_emu_time() {
247        let rust_time = EmulatedTime::UNIX_EPOCH
248            + SimulationTime::SECOND * 60 * 5
249            + SimulationTime::MILLISECOND * 7;
250        let sim_time =
251            5 * simulation_time::SIMTIME_ONE_MINUTE + 7 * simulation_time::SIMTIME_ONE_MILLISECOND;
252
253        assert_eq!(EmulatedTime::to_c_emutime(Some(rust_time)), sim_time);
254        assert_eq!(EmulatedTime::to_c_emutime(None), EMUTIME_INVALID);
255    }
256
257    #[test]
258    fn test_from_abs_simtime() {
259        assert_eq!(
260            EmulatedTime::from_abs_simtime(SimulationTime::ZERO),
261            EmulatedTime::SIMULATION_START
262        );
263
264        assert_eq!(
265            EmulatedTime::from_abs_simtime(SimulationTime::SECOND),
266            EmulatedTime::SIMULATION_START + SimulationTime::SECOND
267        );
268    }
269
270    #[test]
271    fn test_to_abs_simtime() {
272        assert_eq!(
273            EmulatedTime::SIMULATION_START.to_abs_simtime(),
274            SimulationTime::ZERO
275        );
276
277        assert_eq!(
278            (EmulatedTime::SIMULATION_START + SimulationTime::SECOND).to_abs_simtime(),
279            SimulationTime::SECOND
280        );
281    }
282}
283
284#[derive(VirtualAddressSpaceIndependent)]
285#[repr(C)]
286pub struct AtomicEmulatedTime(AtomicU64);
287
288impl AtomicEmulatedTime {
289    pub fn new(t: EmulatedTime) -> Self {
290        Self(AtomicU64::new(t.0))
291    }
292
293    pub fn load(&self, order: Ordering) -> EmulatedTime {
294        EmulatedTime(self.0.load(order))
295    }
296
297    pub fn store(&self, val: EmulatedTime, order: Ordering) {
298        self.0.store(val.0, order)
299    }
300}