reifydb_runtime/context/clock/
native.rs1use std::{
5 cmp, fmt, ops,
6 sync::{
7 Arc,
8 atomic::{AtomicU64, Ordering},
9 },
10 time,
11 time::{Duration, SystemTime, UNIX_EPOCH},
12};
13
14#[allow(clippy::disallowed_methods)]
15#[inline(always)]
16fn platform_now_nanos() -> u64 {
17 SystemTime::now().duration_since(UNIX_EPOCH).expect("System time is before Unix epoch").as_nanos() as u64
18}
19
20#[derive(Clone)]
22pub enum Clock {
23 Real,
25 Mock(MockClock),
27}
28
29impl Clock {
30 pub fn now_nanos(&self) -> u64 {
32 match self {
33 Clock::Real => platform_now_nanos(),
34 Clock::Mock(mock) => mock.now_nanos(),
35 }
36 }
37
38 pub fn now_micros(&self) -> u64 {
40 self.now_nanos() / 1_000
41 }
42
43 pub fn now_millis(&self) -> u64 {
45 self.now_nanos() / 1_000_000
46 }
47
48 pub fn now_secs(&self) -> u64 {
50 self.now_nanos() / 1_000_000_000
51 }
52
53 #[allow(clippy::disallowed_methods)]
54 pub fn instant(&self) -> Instant {
55 match self {
56 Clock::Real => Instant {
57 inner: InstantInner::Real(time::Instant::now()),
58 },
59 Clock::Mock(mock) => Instant {
60 inner: InstantInner::Mock {
61 captured_nanos: mock.now_nanos(),
62 clock: mock.clone(),
63 },
64 },
65 }
66 }
67}
68
69#[derive(Clone)]
71pub struct MockClock {
72 inner: Arc<MockClockInner>,
73}
74
75struct MockClockInner {
76 time_nanos: AtomicU64,
77}
78
79impl MockClock {
80 pub fn new(initial_nanos: u64) -> Self {
82 Self {
83 inner: Arc::new(MockClockInner {
84 time_nanos: AtomicU64::new(initial_nanos),
85 }),
86 }
87 }
88
89 pub fn from_millis(millis: u64) -> Self {
91 Self::new(millis * 1_000_000)
92 }
93
94 pub fn now_nanos(&self) -> u64 {
96 self.inner.time_nanos.load(Ordering::Acquire)
97 }
98
99 pub fn now_micros(&self) -> u64 {
101 self.now_nanos() / 1_000
102 }
103
104 pub fn now_millis(&self) -> u64 {
106 self.now_nanos() / 1_000_000
107 }
108
109 pub fn now_secs(&self) -> u64 {
111 self.now_nanos() / 1_000_000_000
112 }
113
114 pub fn set_nanos(&self, nanos: u64) {
116 self.inner.time_nanos.store(nanos, Ordering::Release);
117 }
118
119 pub fn set_micros(&self, micros: u64) {
121 self.set_nanos(micros * 1_000);
122 }
123
124 pub fn set_millis(&self, millis: u64) {
126 self.set_nanos(millis * 1_000_000);
127 }
128
129 pub fn advance_nanos(&self, nanos: u64) {
131 self.set_nanos(self.now_nanos().saturating_add(nanos));
132 }
133
134 pub fn advance_micros(&self, micros: u64) {
136 self.advance_nanos(micros * 1_000);
137 }
138
139 pub fn advance_millis(&self, millis: u64) {
141 self.advance_nanos(millis * 1_000_000);
142 }
143
144 pub fn advance_secs(&self, secs: u64) {
146 self.advance_nanos(secs * 1_000_000_000);
147 }
148
149 pub fn advance_minutes(&self, minutes: u64) {
151 self.advance_secs(minutes * 60);
152 }
153
154 pub fn advance_hours(&self, hours: u64) {
156 self.advance_secs(hours * 3600);
157 }
158
159 pub fn advance_days(&self, days: u64) {
161 self.advance_secs(days * 86400);
162 }
163}
164
165#[derive(Clone)]
166enum InstantInner {
167 Real(time::Instant),
168 Mock {
169 captured_nanos: u64,
170 clock: MockClock,
171 },
172}
173
174#[derive(Clone)]
175pub struct Instant {
176 inner: InstantInner,
177}
178
179impl Instant {
180 #[inline]
181 pub fn elapsed(&self) -> Duration {
182 match &self.inner {
183 InstantInner::Real(instant) => instant.elapsed(),
184 InstantInner::Mock {
185 captured_nanos,
186 clock,
187 } => {
188 let now = clock.now_nanos();
189 let elapsed_nanos = now.saturating_sub(*captured_nanos);
190 Duration::from_nanos(elapsed_nanos)
191 }
192 }
193 }
194
195 #[inline]
196 pub fn duration_since(&self, earlier: &Instant) -> Duration {
197 match (&self.inner, &earlier.inner) {
198 (InstantInner::Real(this), InstantInner::Real(other)) => this.duration_since(*other),
199 (
200 InstantInner::Mock {
201 captured_nanos: this_nanos,
202 ..
203 },
204 InstantInner::Mock {
205 captured_nanos: other_nanos,
206 ..
207 },
208 ) => {
209 let elapsed = this_nanos.saturating_sub(*other_nanos);
210 Duration::from_nanos(elapsed)
211 }
212 _ => panic!("Cannot compare instants from different clock types"),
213 }
214 }
215}
216
217impl PartialEq for Instant {
218 fn eq(&self, other: &Self) -> bool {
219 match (&self.inner, &other.inner) {
220 (InstantInner::Real(a), InstantInner::Real(b)) => a == b,
221 (
222 InstantInner::Mock {
223 captured_nanos: a,
224 ..
225 },
226 InstantInner::Mock {
227 captured_nanos: b,
228 ..
229 },
230 ) => a == b,
231 _ => panic!("Cannot compare instants from different clock types"),
232 }
233 }
234}
235
236impl Eq for Instant {}
237
238impl PartialOrd for Instant {
239 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
240 Some(self.cmp(other))
241 }
242}
243
244impl Ord for Instant {
245 fn cmp(&self, other: &Self) -> cmp::Ordering {
246 match (&self.inner, &other.inner) {
247 (InstantInner::Real(a), InstantInner::Real(b)) => a.cmp(b),
248 (
249 InstantInner::Mock {
250 captured_nanos: a,
251 ..
252 },
253 InstantInner::Mock {
254 captured_nanos: b,
255 ..
256 },
257 ) => a.cmp(b),
258 _ => panic!("Cannot compare instants from different clock types"),
259 }
260 }
261}
262
263impl ops::Add<Duration> for Instant {
264 type Output = Instant;
265
266 fn add(self, duration: Duration) -> Instant {
267 match self.inner {
268 InstantInner::Real(instant) => Instant {
269 inner: InstantInner::Real(instant + duration),
270 },
271 InstantInner::Mock {
272 captured_nanos,
273 clock,
274 } => Instant {
275 inner: InstantInner::Mock {
276 captured_nanos: captured_nanos.saturating_add(duration.as_nanos() as u64),
277 clock,
278 },
279 },
280 }
281 }
282}
283
284impl ops::Sub for &Instant {
285 type Output = Duration;
286
287 fn sub(self, other: &Instant) -> Duration {
288 self.duration_since(other)
289 }
290}
291
292impl fmt::Debug for Instant {
293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 match &self.inner {
295 InstantInner::Real(instant) => f.debug_tuple("Instant::Real").field(instant).finish(),
296 InstantInner::Mock {
297 captured_nanos,
298 ..
299 } => f.debug_tuple("Instant::Mock").field(captured_nanos).finish(),
300 }
301 }
302}