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