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}