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