Skip to main content

qubit_clock/timer/
timer.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 asynchronous timer capability.
9
10use crate::{
11    MonotonicClock,
12    MonotonicInstant,
13    TimeError,
14    TimerFuture,
15};
16use std::time::Duration;
17
18/// Creates asynchronous notifications in one monotonic clock domain.
19///
20/// Calling [`at()`](Self::at) or [`after()`](Self::after) fixes the logical
21/// deadline and cancellation ownership before returning. The returned future
22/// waits for that fixed deadline. If the deadline is reached before the first
23/// poll, that first poll returns ready. As with every [`Future`], callers must
24/// not poll it again after it first returns ready. A backend may defer
25/// enrollment with its native scheduler until the future is polled. Dropping an
26/// incomplete future cancels the outstanding notification.
27///
28/// Every call to [`clock()`](Self::clock) on one Timer must report the same
29/// clock domain for the Timer's lifetime. Implementations must reject deadlines
30/// from a different domain with [`TimeError::ClockDomainMismatch`].
31/// [`MonotonicInstant::validate_domain`] provides the canonical validation and
32/// error construction for custom Timer implementations.
33///
34/// Timer failures have two stages: the outer [`Result`] reports registration
35/// failures, while the returned [`TimerFuture`] reports failures observed after
36/// registration, such as an unavailable scheduler worker or a Tokio runtime
37/// that shut down. Custom implementations may document additional lifecycle
38/// preconditions and panic conditions.
39pub trait Timer: Send + Sync {
40    /// Returns the monotonic clock whose domain this timer uses.
41    ///
42    /// Successive calls may return different handles, but every returned clock
43    /// must report the same domain for this Timer's lifetime.
44    ///
45    /// # Returns
46    ///
47    /// The clock retained by this timer.
48    ///
49    /// # Examples
50    ///
51    /// Discarding the retained clock is diagnosed when unused results are
52    /// denied:
53    ///
54    /// ```compile_fail
55    /// #![deny(unused_must_use)]
56    /// use qubit_clock::{MonotonicClock, StdMonotonicClock, Timer};
57    ///
58    /// let timer = StdMonotonicClock::new().new_timer();
59    /// timer.clock();
60    /// ```
61    #[must_use = "the Timer clock should be used to sample or validate deadlines"]
62    fn clock(&self) -> &dyn MonotonicClock;
63
64    /// Returns the current monotonic instant in this timer's clock domain.
65    ///
66    /// # Returns
67    ///
68    /// The current instant sampled from this timer's clock.
69    #[must_use = "the current timer instant should be used to measure or validate deadlines"]
70    #[inline(always)]
71    fn now(&self) -> MonotonicInstant {
72        self.clock().now()
73    }
74
75    /// Fixes an absolute deadline relative to the current timer instant.
76    ///
77    /// This method samples the timer clock while it runs and returns the
78    /// resulting deadline without registering a Timer waiter. Callers can
79    /// carry the returned deadline through lock acquisition or other
80    /// non-interruptible work while preserving one operation-wide budget.
81    /// It does not guarantee that such work returns before the deadline.
82    ///
83    /// # Parameters
84    ///
85    /// * duration - Duration from the current monotonic instant.
86    ///
87    /// # Returns
88    ///
89    /// The fixed deadline in this timer's clock domain.
90    ///
91    /// # Errors
92    ///
93    /// Returns TimeError::InstantOverflow when the deadline cannot be
94    /// represented.
95    #[inline(always)]
96    fn deadline_after(
97        &self,
98        duration: Duration,
99    ) -> Result<MonotonicInstant, TimeError> {
100        self.now().checked_add(duration)
101    }
102
103    /// Creates a notification for an absolute monotonic deadline.
104    ///
105    /// The deadline is fixed before this method returns. A deadline at or
106    /// before the current time produces a future that is already ready.
107    ///
108    /// # Parameters
109    ///
110    /// * `deadline` - Absolute deadline in this timer's clock domain.
111    ///
112    /// # Returns
113    ///
114    /// A future that returns `Ok(())` when `deadline` is reached. The future
115    /// returns a [`TimeError`] if the backend fails after registration.
116    ///
117    /// # Errors
118    ///
119    /// Returns [`TimeError::ClockDomainMismatch`] when `deadline` belongs to a
120    /// different clock domain. Returns another [`TimeError`] when the
121    /// notification cannot be created.
122    fn at(&self, deadline: MonotonicInstant) -> Result<TimerFuture, TimeError>;
123
124    /// Registers a notification after a relative duration.
125    ///
126    /// The deadline is fixed by sampling [`clock()`](Self::clock) during this
127    /// call, not when the returned future is first polled.
128    ///
129    /// # Parameters
130    ///
131    /// * `duration` - Duration from the current monotonic instant.
132    ///
133    /// # Returns
134    ///
135    /// A future that returns `Ok(())` when the fixed deadline is reached. The
136    /// future returns a [`TimeError`] if the backend later fails.
137    ///
138    /// # Errors
139    ///
140    /// Returns [`TimeError::InstantOverflow`] when the deadline cannot be
141    /// represented. Returns any error produced while creating the notification
142    /// for that deadline.
143    #[inline]
144    fn after(&self, duration: Duration) -> Result<TimerFuture, TimeError> {
145        let deadline = self.deadline_after(duration)?;
146        self.at(deadline)
147    }
148}
149
150impl<T> Timer for std::sync::Arc<T>
151where
152    T: Timer + ?Sized,
153{
154    /// Delegates access to the shared timer's clock.
155    ///
156    /// # Returns
157    ///
158    /// The clock exposed by the wrapped timer.
159    #[inline(always)]
160    fn clock(&self) -> &dyn MonotonicClock {
161        self.as_ref().clock()
162    }
163
164    /// Delegates absolute deadline registration to the shared timer.
165    ///
166    /// # Parameters
167    ///
168    /// * `deadline` - Absolute deadline in the wrapped timer's clock domain.
169    ///
170    /// # Returns
171    ///
172    /// The wrapped timer's cancellation-safe completion future.
173    ///
174    /// # Errors
175    ///
176    /// Returns any registration error reported by the wrapped timer.
177    #[inline(always)]
178    fn at(&self, deadline: MonotonicInstant) -> Result<TimerFuture, TimeError> {
179        self.as_ref().at(deadline)
180    }
181
182    /// Delegates relative deadline registration to the shared timer.
183    ///
184    /// # Parameters
185    ///
186    /// * `duration` - Duration from the wrapped timer's current instant.
187    ///
188    /// # Returns
189    ///
190    /// The wrapped timer's cancellation-safe completion future.
191    ///
192    /// # Errors
193    ///
194    /// Returns any registration error reported by the wrapped timer.
195    #[inline(always)]
196    fn after(&self, duration: Duration) -> Result<TimerFuture, TimeError> {
197        self.as_ref().after(duration)
198    }
199}
200
201impl<T> Timer for Box<T>
202where
203    T: Timer + ?Sized,
204{
205    /// Delegates access to the boxed timer's clock.
206    ///
207    /// # Returns
208    ///
209    /// The clock exposed by the wrapped timer.
210    #[inline(always)]
211    fn clock(&self) -> &dyn MonotonicClock {
212        self.as_ref().clock()
213    }
214
215    /// Delegates absolute deadline registration to the boxed timer.
216    ///
217    /// # Parameters
218    ///
219    /// * `deadline` - Absolute deadline in the wrapped timer's clock domain.
220    ///
221    /// # Returns
222    ///
223    /// The wrapped timer's cancellation-safe completion future.
224    ///
225    /// # Errors
226    ///
227    /// Returns any registration error reported by the wrapped timer.
228    #[inline(always)]
229    fn at(&self, deadline: MonotonicInstant) -> Result<TimerFuture, TimeError> {
230        self.as_ref().at(deadline)
231    }
232
233    /// Delegates relative deadline registration to the boxed timer.
234    ///
235    /// # Parameters
236    ///
237    /// * `duration` - Duration from the wrapped timer's current instant.
238    ///
239    /// # Returns
240    ///
241    /// The wrapped timer's cancellation-safe completion future.
242    ///
243    /// # Errors
244    ///
245    /// Returns any registration error reported by the wrapped timer.
246    #[inline(always)]
247    fn after(&self, duration: Duration) -> Result<TimerFuture, TimeError> {
248        self.as_ref().after(duration)
249    }
250}