reifydb_runtime/context/clock/
host.rs1#![allow(clippy::disallowed_types)]
5
6use std::{
7 cmp, fmt, ops,
8 sync::{
9 Arc,
10 atomic::{AtomicU64, Ordering},
11 },
12 time,
13 time::{Duration, SystemTime, UNIX_EPOCH},
14};
15
16use reifydb_value::value::{datetime::DateTime, duration::Duration as RvDuration};
17
18use crate::{
19 context::clock::{TimerId, TimerWake},
20 sync::mutex::Mutex,
21};
22
23#[allow(clippy::disallowed_methods)]
24#[inline(always)]
25fn platform_now_nanos() -> u64 {
26 SystemTime::now().duration_since(UNIX_EPOCH).expect("System time is before Unix epoch").as_nanos() as u64
27}
28
29#[derive(Clone)]
30pub enum Clock {
31 Real,
32
33 Mock(MockClock),
34}
35
36impl Clock {
37 pub fn now(&self) -> DateTime {
38 match self {
39 Clock::Real => DateTime::from_nanos(
40 i64::try_from(platform_now_nanos()).expect("system clock past 2262-04-11"),
41 ),
42 Clock::Mock(mock) => mock.now(),
43 }
44 }
45
46 #[allow(clippy::disallowed_methods)]
47 pub fn instant(&self) -> Instant {
48 match self {
49 Clock::Real => Instant {
50 inner: InstantInner::Real(time::Instant::now()),
51 },
52 Clock::Mock(mock) => Instant {
53 inner: InstantInner::Mock {
54 captured_nanos: u64::try_from(mock.now().to_nanos())
55 .expect("mock clock is never before the Unix epoch"),
56 clock: mock.clone(),
57 },
58 },
59 }
60 }
61
62 pub fn testing() -> Self {
63 #[cfg(reifydb_dst)]
64 return Clock::Mock(MockClock::from_millis(0));
65 #[cfg(not(reifydb_dst))]
66 return Clock::Real;
67 }
68
69 pub fn is_mock(&self) -> bool {
70 matches!(self, Clock::Mock(_))
71 }
72
73 pub fn as_mock(&self) -> Option<&MockClock> {
74 match self {
75 Clock::Mock(mock) => Some(mock),
76 Clock::Real => None,
77 }
78 }
79}
80
81#[derive(Clone)]
82pub struct MockClock {
83 inner: Arc<MockClockInner>,
84}
85
86struct MockClockInner {
87 time_nanos: AtomicU64,
88 timers: Mutex<Vec<Timer>>,
89 next_timer_id: AtomicU64,
90}
91
92struct Timer {
93 id: u64,
94 deadline_nanos: u64,
95 wake: Arc<dyn TimerWake>,
96}
97
98impl MockClock {
99 pub fn new(initial_nanos: u64) -> Self {
100 Self {
101 inner: Arc::new(MockClockInner {
102 time_nanos: AtomicU64::new(initial_nanos),
103 timers: Mutex::new(Vec::new()),
104 next_timer_id: AtomicU64::new(0),
105 }),
106 }
107 }
108
109 pub fn from_millis(millis: u64) -> Self {
110 Self::new(millis * 1_000_000)
111 }
112
113 pub fn now(&self) -> DateTime {
114 DateTime::from_nanos(
115 i64::try_from(self.inner.time_nanos.load(Ordering::Acquire))
116 .expect("mock clock past 2262-04-11"),
117 )
118 }
119
120 pub fn set_nanos(&self, nanos: u64) {
121 self.inner.time_nanos.store(nanos, Ordering::Release);
122 self.fire_due(nanos);
123 }
124
125 pub fn register_timer(&self, deadline_nanos: u64, wake: Arc<dyn TimerWake>) -> TimerId {
126 let id = self.inner.next_timer_id.fetch_add(1, Ordering::Relaxed);
127 self.inner.timers.lock().push(Timer {
128 id,
129 deadline_nanos,
130 wake,
131 });
132 TimerId(id)
133 }
134
135 pub fn cancel_timer(&self, timer: TimerId) {
136 self.inner.timers.lock().retain(|entry| entry.id != timer.0);
137 }
138
139 fn fire_due(&self, now_nanos: u64) {
140 let due = {
141 let mut timers = self.inner.timers.lock();
142 let mut due: Vec<Arc<dyn TimerWake>> = Vec::new();
143 timers.retain(|entry| {
144 if entry.deadline_nanos <= now_nanos {
145 due.push(entry.wake.clone());
146 return false;
147 }
148 true
149 });
150 due
151 };
152
153 for wake in due {
154 wake.wake();
155 }
156 }
157
158 pub fn set_micros(&self, micros: u64) {
159 self.set_nanos(micros * 1_000);
160 }
161
162 pub fn set_millis(&self, millis: u64) {
163 self.set_nanos(millis * 1_000_000);
164 }
165
166 pub fn advance_nanos(&self, nanos: u64) {
167 let current = u64::try_from(self.now().to_nanos()).expect("mock clock is never before the Unix epoch");
168 self.set_nanos(current.saturating_add(nanos));
169 }
170
171 pub fn advance_micros(&self, micros: u64) {
172 self.advance_nanos(micros * 1_000);
173 }
174
175 pub fn advance_millis(&self, millis: u64) {
176 self.advance_nanos(millis * 1_000_000);
177 }
178
179 pub fn advance_secs(&self, secs: u64) {
180 self.advance_nanos(secs * 1_000_000_000);
181 }
182
183 pub fn advance_minutes(&self, minutes: u64) {
184 self.advance_secs(minutes * 60);
185 }
186
187 pub fn advance_hours(&self, hours: u64) {
188 self.advance_secs(hours * 3600);
189 }
190
191 pub fn advance_days(&self, days: u64) {
192 self.advance_secs(days * 86400);
193 }
194}
195
196#[derive(Clone)]
197enum InstantInner {
198 Real(time::Instant),
199 Mock {
200 captured_nanos: u64,
201 clock: MockClock,
202 },
203}
204
205#[derive(Clone)]
206pub struct Instant {
207 inner: InstantInner,
208}
209
210impl Instant {
211 #[inline]
212 pub fn elapsed(&self) -> Duration {
213 match &self.inner {
214 InstantInner::Real(instant) => instant.elapsed(),
215 InstantInner::Mock {
216 captured_nanos,
217 clock,
218 } => {
219 let now = u64::try_from(clock.now().to_nanos())
220 .expect("mock clock is never before the Unix epoch");
221 let elapsed_nanos = now.saturating_sub(*captured_nanos);
222 Duration::from_nanos(elapsed_nanos)
223 }
224 }
225 }
226
227 #[inline]
228 pub fn duration_since(&self, earlier: &Instant) -> Duration {
229 match (&self.inner, &earlier.inner) {
230 (InstantInner::Real(this), InstantInner::Real(other)) => this.duration_since(*other),
231 (
232 InstantInner::Mock {
233 captured_nanos: this_nanos,
234 ..
235 },
236 InstantInner::Mock {
237 captured_nanos: other_nanos,
238 ..
239 },
240 ) => {
241 let elapsed = this_nanos.saturating_sub(*other_nanos);
242 Duration::from_nanos(elapsed)
243 }
244 _ => panic!("Cannot compare instants from different clock types"),
245 }
246 }
247}
248
249impl PartialEq for Instant {
250 fn eq(&self, other: &Self) -> bool {
251 match (&self.inner, &other.inner) {
252 (InstantInner::Real(a), InstantInner::Real(b)) => a == b,
253 (
254 InstantInner::Mock {
255 captured_nanos: a,
256 ..
257 },
258 InstantInner::Mock {
259 captured_nanos: b,
260 ..
261 },
262 ) => a == b,
263 _ => panic!("Cannot compare instants from different clock types"),
264 }
265 }
266}
267
268impl Eq for Instant {}
269
270impl PartialOrd for Instant {
271 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
272 Some(self.cmp(other))
273 }
274}
275
276impl Ord for Instant {
277 fn cmp(&self, other: &Self) -> cmp::Ordering {
278 match (&self.inner, &other.inner) {
279 (InstantInner::Real(a), InstantInner::Real(b)) => a.cmp(b),
280 (
281 InstantInner::Mock {
282 captured_nanos: a,
283 ..
284 },
285 InstantInner::Mock {
286 captured_nanos: b,
287 ..
288 },
289 ) => a.cmp(b),
290 _ => panic!("Cannot compare instants from different clock types"),
291 }
292 }
293}
294
295impl ops::Add<Duration> for Instant {
296 type Output = Instant;
297
298 fn add(self, duration: Duration) -> Instant {
299 match self.inner {
300 InstantInner::Real(instant) => Instant {
301 inner: InstantInner::Real(instant + duration),
302 },
303 InstantInner::Mock {
304 captured_nanos,
305 clock,
306 } => Instant {
307 inner: InstantInner::Mock {
308 captured_nanos: captured_nanos.saturating_add(duration.as_nanos() as u64),
309 clock,
310 },
311 },
312 }
313 }
314}
315
316impl ops::Add<RvDuration> for Instant {
317 type Output = Instant;
318
319 fn add(self, duration: RvDuration) -> Instant {
320 self + duration.to_std()
321 }
322}
323
324impl ops::Sub for &Instant {
325 type Output = Duration;
326
327 fn sub(self, other: &Instant) -> Duration {
328 self.duration_since(other)
329 }
330}
331
332impl fmt::Debug for Instant {
333 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
334 match &self.inner {
335 InstantInner::Real(instant) => f.debug_tuple("Instant::Real").field(instant).finish(),
336 InstantInner::Mock {
337 captured_nanos,
338 ..
339 } => f.debug_tuple("Instant::Mock").field(captured_nanos).finish(),
340 }
341 }
342}