qubit_clock/monotonic/monotonic_clock.rs
1// =============================================================================
2// Copyright (c) 2025 - 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Defines the monotonic clock capability.
9//!
10//! The [`MonotonicClock`] trait is the injectable source of
11//! [`MonotonicInstant`] values used for timeouts, deadlines, and elapsed-time
12//! measurements. Wall time belongs on [`WallClock`](crate::WallClock) instead.
13
14use std::sync::Arc;
15use std::time::Duration;
16
17use crate::ClockDomain;
18use crate::MonotonicInstant;
19use crate::TimeError;
20use crate::Timer;
21
22/// Provides the current instant in a stable, non-decreasing clock domain.
23///
24/// Implementations return [`MonotonicInstant`] values that never move backward
25/// within one domain. Prefer this trait for timeouts, deadlines, and measuring
26/// elapsed durations. Use [`WallClock`](crate::WallClock) for civil timestamps
27/// that must align with an external calendar clock, which may jump forward or
28/// backward after a system adjustment.
29///
30/// The crate ships several implementations:
31///
32/// - [`StdMonotonicClock`](crate::StdMonotonicClock) — production clock backed
33/// by [`std::time::Instant`]
34/// - [`ManualMonotonicClock`](crate::ManualMonotonicClock) — explicitly
35/// advanced clock for deterministic tests
36/// - `TokioMonotonicClock` — Tokio time-driver clock (requires the `tokio`
37/// feature)
38///
39/// `&T`, `Arc<T>`, and `Box<T>` implement this trait when `T:
40/// MonotonicClock + ?Sized`, so borrowed, shared, and owned trait objects need
41/// no extra adapter.
42///
43/// # Examples
44///
45/// Sample a standard monotonic clock and form a deadline from its instant:
46///
47/// ```
48/// use qubit_clock::{MonotonicClock, StdMonotonicClock};
49/// use std::time::Duration;
50///
51/// let clock = StdMonotonicClock::new();
52/// let start = clock.now();
53/// let deadline = start
54/// .checked_add(Duration::from_millis(10))
55/// .expect("duration should fit");
56/// assert!(deadline.elapsed_since_origin() > start.elapsed_since_origin());
57/// ```
58///
59/// Drive logical time with a manual clock in tests without waiting for real
60/// time:
61///
62/// ```
63/// use qubit_clock::{ManualMonotonicClock, MonotonicClock};
64/// use std::time::Duration;
65///
66/// let clock = ManualMonotonicClock::new();
67/// assert_eq!(Duration::ZERO, clock.now().elapsed_since_origin());
68///
69/// clock
70/// .advance(Duration::from_secs(2))
71/// .expect("manual time should advance");
72/// assert_eq!(Duration::from_secs(2), clock.now().elapsed_since_origin());
73/// ```
74///
75/// Share one clock through a trait object:
76///
77/// ```
78/// use qubit_clock::{ManualMonotonicClock, MonotonicClock};
79/// use std::sync::Arc;
80/// use std::time::Duration;
81///
82/// let clock: Arc<dyn MonotonicClock> = Arc::new(ManualMonotonicClock::new());
83/// let first = clock.now();
84/// let second = clock.now();
85/// assert_eq!(first.domain(), second.domain());
86/// assert_eq!(Duration::ZERO, second.elapsed_since_origin());
87/// ```
88pub trait MonotonicClock: Send + Sync {
89 /// Returns this clock's stable monotonic domain identity.
90 ///
91 /// Unlike [`now()`](Self::now), this method does not sample the current
92 /// time. The returned domain remains unchanged for this clock's lifetime.
93 ///
94 /// # Returns
95 ///
96 /// The domain carried by every instant sampled from this clock.
97 #[must_use = "the clock domain should be used to validate monotonic instants"]
98 fn domain(&self) -> ClockDomain;
99
100 /// Returns the current instant in this clock's domain.
101 ///
102 /// Successive calls on the same clock never return an earlier instant.
103 /// Instants from independently created clock domains must not be mixed.
104 /// Cloned or derived same-domain handles may intentionally report the same
105 /// [`ClockDomain`](crate::ClockDomain).
106 ///
107 /// # Returns
108 ///
109 /// The current domain-scoped monotonic instant.
110 fn now(&self) -> MonotonicInstant;
111
112 /// Fixes a deadline after a relative duration.
113 ///
114 /// This method samples [`now()`](Self::now) exactly once while it runs,
115 /// then adds `duration` to that sampled instant. It does not create a
116 /// timer registration.
117 ///
118 /// # Parameters
119 ///
120 /// * `duration` - Duration from the sampled current instant.
121 ///
122 /// # Returns
123 ///
124 /// A fixed deadline in this clock's domain.
125 ///
126 /// # Errors
127 ///
128 /// Returns [`TimeError::InstantOverflow`] when the resulting deadline
129 /// cannot be represented by [`Duration`].
130 #[inline]
131 fn deadline_after(&self, duration: Duration) -> Result<MonotonicInstant, TimeError> {
132 self.now().checked_add(duration)
133 }
134
135 /// Creates a timer in this clock's exact monotonic domain.
136 ///
137 /// The call borrows rather than consumes the clock. The returned timer
138 /// retains an independent same-domain handle, so callers do not need to
139 /// clone an `Arc` before invoking this method and may continue using or
140 /// drop the original clock afterward.
141 ///
142 /// # Returns
143 ///
144 /// A shared timer whose [`Timer::clock`] reports this clock's domain.
145 #[must_use = "the timer should be retained to register deadlines"]
146 fn new_timer(&self) -> Arc<dyn Timer>;
147}
148
149impl<T> MonotonicClock for &T
150where
151 T: MonotonicClock + ?Sized,
152{
153 /// Delegates the stable domain identity to the borrowed clock.
154 ///
155 /// # Returns
156 ///
157 /// The domain returned by the borrowed clock.
158 #[inline(always)]
159 fn domain(&self) -> ClockDomain {
160 <T as MonotonicClock>::domain(*self)
161 }
162
163 /// Delegates the current instant to the borrowed clock.
164 ///
165 /// # Returns
166 ///
167 /// The current instant returned by the borrowed clock.
168 #[inline(always)]
169 fn now(&self) -> MonotonicInstant {
170 <T as MonotonicClock>::now(*self)
171 }
172
173 /// Delegates relative deadline calculation to the borrowed clock.
174 ///
175 /// # Parameters
176 ///
177 /// * `duration` - Duration from the borrowed clock's sampled current time.
178 ///
179 /// # Returns
180 ///
181 /// The deadline returned by the borrowed clock.
182 ///
183 /// # Errors
184 ///
185 /// Returns any overflow error reported by the borrowed clock.
186 #[inline(always)]
187 fn deadline_after(&self, duration: Duration) -> Result<MonotonicInstant, TimeError> {
188 <T as MonotonicClock>::deadline_after(*self, duration)
189 }
190
191 /// Delegates timer creation without consuming the borrowed clock.
192 ///
193 /// # Returns
194 ///
195 /// A timer in the borrowed clock's exact monotonic domain.
196 #[inline(always)]
197 fn new_timer(&self) -> Arc<dyn Timer> {
198 <T as MonotonicClock>::new_timer(*self)
199 }
200}
201
202impl<T> MonotonicClock for std::sync::Arc<T>
203where
204 T: MonotonicClock + ?Sized,
205{
206 /// Delegates the stable domain identity to the shared clock object.
207 ///
208 /// # Returns
209 ///
210 /// The domain returned by the wrapped clock.
211 #[inline(always)]
212 fn domain(&self) -> ClockDomain {
213 self.as_ref().domain()
214 }
215
216 /// Delegates the current instant to the shared clock object.
217 ///
218 /// # Returns
219 ///
220 /// The current instant returned by the wrapped clock.
221 #[inline(always)]
222 fn now(&self) -> MonotonicInstant {
223 self.as_ref().now()
224 }
225
226 /// Delegates relative deadline calculation to the shared clock object.
227 ///
228 /// # Parameters
229 ///
230 /// * `duration` - Duration from the wrapped clock's sampled current time.
231 ///
232 /// # Returns
233 ///
234 /// The deadline returned by the wrapped clock.
235 ///
236 /// # Errors
237 ///
238 /// Returns any overflow error reported by the wrapped clock.
239 #[inline(always)]
240 fn deadline_after(&self, duration: Duration) -> Result<MonotonicInstant, TimeError> {
241 self.as_ref().deadline_after(duration)
242 }
243
244 /// Delegates timer creation without consuming the shared clock pointer.
245 ///
246 /// # Returns
247 ///
248 /// A timer in the wrapped clock's exact monotonic domain.
249 #[inline(always)]
250 fn new_timer(&self) -> Arc<dyn Timer> {
251 self.as_ref().new_timer()
252 }
253}
254
255impl<T> MonotonicClock for Box<T>
256where
257 T: MonotonicClock + ?Sized,
258{
259 /// Delegates the stable domain identity to the boxed clock object.
260 ///
261 /// # Returns
262 ///
263 /// The domain returned by the wrapped clock.
264 #[inline(always)]
265 fn domain(&self) -> ClockDomain {
266 self.as_ref().domain()
267 }
268
269 /// Delegates the current instant to the boxed clock object.
270 ///
271 /// # Returns
272 ///
273 /// The current instant returned by the wrapped clock.
274 #[inline(always)]
275 fn now(&self) -> MonotonicInstant {
276 self.as_ref().now()
277 }
278
279 /// Delegates relative deadline calculation to the boxed clock object.
280 ///
281 /// # Parameters
282 ///
283 /// * `duration` - Duration from the wrapped clock's sampled current time.
284 ///
285 /// # Returns
286 ///
287 /// The deadline returned by the wrapped clock.
288 ///
289 /// # Errors
290 ///
291 /// Returns any overflow error reported by the wrapped clock.
292 #[inline(always)]
293 fn deadline_after(&self, duration: Duration) -> Result<MonotonicInstant, TimeError> {
294 self.as_ref().deadline_after(duration)
295 }
296
297 /// Delegates timer creation without consuming the boxed clock.
298 ///
299 /// # Returns
300 ///
301 /// A timer in the wrapped clock's exact monotonic domain.
302 #[inline(always)]
303 fn new_timer(&self) -> Arc<dyn Timer> {
304 self.as_ref().new_timer()
305 }
306}