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