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