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