Skip to main content

reifydb_runtime/clock/
native.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4//! Native clock implementation.
5
6use std::{
7	sync::{
8		Arc,
9		atomic::{AtomicU64, Ordering},
10	},
11	time::{Duration, SystemTime, UNIX_EPOCH},
12};
13
14#[inline(always)]
15fn platform_now_nanos() -> u128 {
16	SystemTime::now().duration_since(UNIX_EPOCH).expect("System time is before Unix epoch").as_nanos()
17}
18
19/// A clock that provides time - either real system time or mock time for testing.
20#[derive(Clone)]
21pub enum Clock {
22	/// Real system clock - delegates to platform time
23	Real,
24	/// Mock clock with controllable time
25	Mock(MockClock),
26}
27
28impl Clock {
29	/// Get current time in nanoseconds since Unix epoch
30	pub fn now_nanos(&self) -> u128 {
31		match self {
32			Clock::Real => platform_now_nanos(),
33			Clock::Mock(mock) => mock.now_nanos(),
34		}
35	}
36
37	/// Get current time in microseconds since Unix epoch
38	pub fn now_micros(&self) -> u64 {
39		(self.now_nanos() / 1_000) as u64
40	}
41
42	/// Get current time in milliseconds since Unix epoch
43	pub fn now_millis(&self) -> u64 {
44		(self.now_nanos() / 1_000_000) as u64
45	}
46
47	/// Get current time in seconds since Unix epoch
48	pub fn now_secs(&self) -> u64 {
49		(self.now_nanos() / 1_000_000_000) as u64
50	}
51
52	pub fn instant(&self) -> Instant {
53		match self {
54			Clock::Real => Instant {
55				inner: InstantInner::Real(std::time::Instant::now()),
56			},
57			Clock::Mock(mock) => Instant {
58				inner: InstantInner::Mock {
59					captured_nanos: mock.now_nanos(),
60					clock: mock.clone(),
61				},
62			},
63		}
64	}
65}
66
67impl Default for Clock {
68	fn default() -> Self {
69		Clock::Real
70	}
71}
72
73/// Mock clock with atomic time storage for thread-safe access.
74#[derive(Clone)]
75pub struct MockClock {
76	inner: Arc<MockClockInner>,
77}
78
79struct MockClockInner {
80	// Split u128 into two u64s for atomic access
81	time_high: AtomicU64,
82	time_low: AtomicU64,
83}
84
85impl MockClock {
86	/// Create a new mock clock starting at the given nanoseconds
87	pub fn new(initial_nanos: u128) -> Self {
88		Self {
89			inner: Arc::new(MockClockInner {
90				time_high: AtomicU64::new((initial_nanos >> 64) as u64),
91				time_low: AtomicU64::new(initial_nanos as u64),
92			}),
93		}
94	}
95
96	/// Create a new mock clock starting at the given milliseconds
97	pub fn from_millis(millis: u64) -> Self {
98		Self::new(millis as u128 * 1_000_000)
99	}
100
101	/// Get current time in nanoseconds
102	pub fn now_nanos(&self) -> u128 {
103		let high = self.inner.time_high.load(Ordering::Acquire) as u128;
104		let low = self.inner.time_low.load(Ordering::Acquire) as u128;
105		(high << 64) | low
106	}
107
108	/// Get current time in microseconds
109	pub fn now_micros(&self) -> u64 {
110		(self.now_nanos() / 1_000) as u64
111	}
112
113	/// Get current time in milliseconds
114	pub fn now_millis(&self) -> u64 {
115		(self.now_nanos() / 1_000_000) as u64
116	}
117
118	/// Get current time in seconds
119	pub fn now_secs(&self) -> u64 {
120		(self.now_nanos() / 1_000_000_000) as u64
121	}
122
123	/// Set time to specific nanoseconds
124	pub fn set_nanos(&self, nanos: u128) {
125		self.inner.time_high.store((nanos >> 64) as u64, Ordering::Release);
126		self.inner.time_low.store(nanos as u64, Ordering::Release);
127	}
128
129	/// Set time to specific microseconds
130	pub fn set_micros(&self, micros: u64) {
131		self.set_nanos(micros as u128 * 1_000);
132	}
133
134	/// Set time to specific milliseconds
135	pub fn set_millis(&self, millis: u64) {
136		self.set_nanos(millis as u128 * 1_000_000);
137	}
138
139	/// Advance time by nanoseconds
140	pub fn advance_nanos(&self, nanos: u128) {
141		self.set_nanos(self.now_nanos() + nanos);
142	}
143
144	/// Advance time by microseconds
145	pub fn advance_micros(&self, micros: u64) {
146		self.advance_nanos(micros as u128 * 1_000);
147	}
148
149	/// Advance time by milliseconds
150	pub fn advance_millis(&self, millis: u64) {
151		self.advance_nanos(millis as u128 * 1_000_000);
152	}
153}
154
155#[derive(Clone)]
156enum InstantInner {
157	Real(std::time::Instant),
158	Mock {
159		captured_nanos: u128,
160		clock: MockClock,
161	},
162}
163
164#[derive(Clone)]
165pub struct Instant {
166	inner: InstantInner,
167}
168
169impl Instant {
170	#[inline]
171	pub fn elapsed(&self) -> Duration {
172		match &self.inner {
173			InstantInner::Real(instant) => instant.elapsed(),
174			InstantInner::Mock {
175				captured_nanos,
176				clock,
177			} => {
178				let now = clock.now_nanos();
179				let elapsed_nanos = now.saturating_sub(*captured_nanos);
180				Duration::from_nanos(elapsed_nanos as u64)
181			}
182		}
183	}
184
185	#[inline]
186	pub fn duration_since(&self, earlier: Instant) -> Duration {
187		match (&self.inner, &earlier.inner) {
188			(InstantInner::Real(this), InstantInner::Real(other)) => this.duration_since(*other),
189			(
190				InstantInner::Mock {
191					captured_nanos: this_nanos,
192					..
193				},
194				InstantInner::Mock {
195					captured_nanos: other_nanos,
196					..
197				},
198			) => {
199				let elapsed = this_nanos.saturating_sub(*other_nanos);
200				Duration::from_nanos(elapsed as u64)
201			}
202			_ => panic!("Cannot compare instants from different clock types"),
203		}
204	}
205}
206
207impl std::fmt::Debug for Instant {
208	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209		match &self.inner {
210			InstantInner::Real(instant) => f.debug_tuple("Instant::Real").field(instant).finish(),
211			InstantInner::Mock {
212				captured_nanos,
213				..
214			} => f.debug_tuple("Instant::Mock").field(captured_nanos).finish(),
215		}
216	}
217}