1use std::{
2 sync::atomic::{AtomicU64, Ordering},
3 time::{Duration, SystemTime, UNIX_EPOCH},
4};
5
6use sim_kernel::{Error, Result};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct WallTimestamp(u64);
15
16impl WallTimestamp {
17 pub const fn from_unix_millis(unix_millis: u64) -> Self {
19 Self(unix_millis)
20 }
21
22 pub const fn unix_millis(self) -> u64 {
24 self.0
25 }
26
27 pub fn from_system_time(time: SystemTime) -> Result<Self> {
29 let elapsed = time
30 .duration_since(UNIX_EPOCH)
31 .map_err(|err| Error::Eval(format!("system wall clock is before UNIX_EPOCH: {err}")))?;
32 Self::from_epoch_duration(elapsed)
33 }
34
35 fn from_epoch_duration(elapsed: Duration) -> Result<Self> {
36 let unix_millis = u64::try_from(elapsed.as_millis()).map_err(|_| {
37 Error::Eval("system wall-clock timestamp exceeds u64 milliseconds".to_owned())
38 })?;
39 Ok(Self(unix_millis))
40 }
41}
42
43pub trait WallClock: Send + Sync {
49 fn now(&self) -> Result<WallTimestamp>;
51
52 fn now_ms(&self) -> Result<u64> {
54 self.now().map(WallTimestamp::unix_millis)
55 }
56}
57
58#[derive(Clone, Copy, Debug, Default)]
60pub struct SystemWallClock;
61
62impl WallClock for SystemWallClock {
63 fn now(&self) -> Result<WallTimestamp> {
64 WallTimestamp::from_system_time(SystemTime::now())
65 }
66}
67
68#[derive(Debug)]
73pub struct DeterministicWallClock {
74 next_ms: AtomicU64,
75 step_ms: u64,
76}
77
78impl DeterministicWallClock {
79 pub const fn new(start_ms: u64, step_ms: u64) -> Self {
81 Self {
82 next_ms: AtomicU64::new(start_ms),
83 step_ms,
84 }
85 }
86}
87
88impl Clone for DeterministicWallClock {
89 fn clone(&self) -> Self {
90 Self::new(self.next_ms.load(Ordering::Acquire), self.step_ms)
91 }
92}
93
94impl WallClock for DeterministicWallClock {
95 fn now(&self) -> Result<WallTimestamp> {
96 self.next_ms
97 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |current| {
98 current.checked_add(self.step_ms)
99 })
100 .map(WallTimestamp::from_unix_millis)
101 .map_err(|_| Error::Eval("deterministic wall clock overflow".to_owned()))
102 }
103}
104
105#[cfg(test)]
106mod tests;