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