Skip to main content

sim_host_core/
time.rs

1use sim_kernel::{Error, Result};
2use std::sync::{
3    Arc,
4    atomic::{AtomicU64, Ordering},
5};
6/// One observed wall-clock instant in Unix milliseconds.
7#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub struct WallTimestamp(u64);
9impl WallTimestamp {
10    /// Constructs an explicit Unix-millisecond observation.
11    pub const fn from_unix_millis(value: u64) -> Self {
12        Self(value)
13    }
14    /// Returns Unix milliseconds.
15    pub const fn unix_millis(self) -> u64 {
16        self.0
17    }
18}
19/// One process-local monotonic observation in nanoseconds from an injected epoch.
20#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
21pub struct MonotonicTimestamp(u64);
22impl MonotonicTimestamp {
23    /// Constructs an explicit monotonic nanosecond value.
24    pub const fn from_nanos(value: u64) -> Self {
25        Self(value)
26    }
27    /// Returns monotonic nanoseconds.
28    pub const fn nanos(self) -> u64 {
29        self.0
30    }
31}
32/// Object-safe source of optional human wall-time evidence.
33pub trait WallClock: Send + Sync {
34    /// Observes wall time, which may move backward.
35    fn now(&self) -> Result<WallTimestamp>;
36    /// Observes Unix milliseconds.
37    fn now_ms(&self) -> Result<u64> {
38        self.now().map(WallTimestamp::unix_millis)
39    }
40}
41/// Object-safe source of correctness-safe elapsed-time observations.
42pub trait MonotonicClock: Send + Sync {
43    /// Observes a monotonic timestamp.
44    fn now_monotonic(&self) -> Result<MonotonicTimestamp>;
45}
46/// Executor-neutral platform timer binding.
47pub trait Timer: Send + Sync {
48    /// Advances or waits until the supplied deadline.
49    fn wait_until(&self, deadline: MonotonicTimestamp) -> Result<()>;
50}
51/// Complete explicitly supplied platform-time binding.
52#[derive(Clone)]
53pub struct PlatformTime {
54    /// Human-facing wall observations.
55    pub wall: Arc<dyn WallClock>,
56    /// Correctness-safe monotonic observations.
57    pub monotonic: Arc<dyn MonotonicClock>,
58    /// Timer paired with the monotonic clock.
59    pub timer: Arc<dyn Timer>,
60}
61/// Legacy zero-valued model wall clock retained for source compatibility; performs no host observation.
62#[derive(Clone, Copy, Debug, Default)]
63pub struct SystemWallClock;
64impl WallClock for SystemWallClock {
65    fn now(&self) -> Result<WallTimestamp> {
66        Ok(WallTimestamp::from_unix_millis(0))
67    }
68}
69/// Deterministic wall/monotonic/timer model with one shared timeline.
70#[derive(Debug)]
71pub struct DeterministicTime {
72    next_wall_ms: AtomicU64,
73    now_ns: AtomicU64,
74    wall_step_ns: u64,
75}
76impl DeterministicTime {
77    /// Creates a deterministic timeline.
78    pub const fn new(wall_epoch_ms: u64, wall_step_ms: u64) -> Self {
79        Self {
80            next_wall_ms: AtomicU64::new(wall_epoch_ms),
81            now_ns: AtomicU64::new(0),
82            wall_step_ns: wall_step_ms.saturating_mul(1_000_000),
83        }
84    }
85}
86impl Clone for DeterministicTime {
87    fn clone(&self) -> Self {
88        Self {
89            next_wall_ms: AtomicU64::new(self.next_wall_ms.load(Ordering::Acquire)),
90            now_ns: AtomicU64::new(self.now_ns.load(Ordering::Acquire)),
91            wall_step_ns: self.wall_step_ns,
92        }
93    }
94}
95impl WallClock for DeterministicTime {
96    fn now(&self) -> Result<WallTimestamp> {
97        self.next_wall_ms
98            .fetch_update(Ordering::AcqRel, Ordering::Acquire, |value| {
99                value.checked_add(self.wall_step_ns / 1_000_000)
100            })
101            .map(WallTimestamp::from_unix_millis)
102            .map_err(|_| Error::Eval("deterministic wall clock overflow".into()))
103    }
104}
105impl MonotonicClock for DeterministicTime {
106    fn now_monotonic(&self) -> Result<MonotonicTimestamp> {
107        Ok(MonotonicTimestamp::from_nanos(
108            self.now_ns.load(Ordering::Acquire),
109        ))
110    }
111}
112impl Timer for DeterministicTime {
113    fn wait_until(&self, deadline: MonotonicTimestamp) -> Result<()> {
114        self.now_ns.fetch_max(deadline.nanos(), Ordering::AcqRel);
115        Ok(())
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn deterministic_binding_preserves_wall_behavior_and_composes_timer() {
125        let time = Arc::new(DeterministicTime::new(1_000, 25));
126        let binding = PlatformTime {
127            wall: time.clone(),
128            monotonic: time.clone(),
129            timer: time,
130        };
131        assert_eq!(binding.wall.now_ms().unwrap(), 1_000);
132        assert_eq!(binding.wall.now_ms().unwrap(), 1_025);
133        binding
134            .timer
135            .wait_until(MonotonicTimestamp::from_nanos(50_000_000))
136            .unwrap();
137        assert_eq!(
138            binding.monotonic.now_monotonic().unwrap(),
139            MonotonicTimestamp::from_nanos(50_000_000)
140        );
141    }
142
143    #[test]
144    fn deterministic_wall_overflow_remains_fail_closed() {
145        let time = DeterministicTime::new(u64::MAX, 1);
146        assert!(time.now().is_err());
147    }
148}