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 /// Creates a notification for an absolute monotonic deadline.
65 ///
66 /// The deadline is fixed before this method returns. A deadline at or
67 /// before the current time produces a future that is already ready.
68 ///
69 /// # Parameters
70 ///
71 /// * `deadline` - Absolute deadline in this timer's clock domain.
72 ///
73 /// # Returns
74 ///
75 /// A future that returns `Ok(())` when `deadline` is reached. The future
76 /// returns a [`TimeError`] if the backend fails after registration.
77 ///
78 /// # Errors
79 ///
80 /// Returns [`TimeError::ClockDomainMismatch`] when `deadline` belongs to a
81 /// different clock domain. Returns another [`TimeError`] when the
82 /// notification cannot be created.
83 fn at(&self, deadline: MonotonicInstant) -> Result<TimerFuture, TimeError>;
84
85 /// Registers a notification after a relative duration.
86 ///
87 /// The deadline is fixed by sampling [`clock()`](Self::clock) during this
88 /// call, not when the returned future is first polled.
89 ///
90 /// # Parameters
91 ///
92 /// * `duration` - Duration from the current monotonic instant.
93 ///
94 /// # Returns
95 ///
96 /// A future that returns `Ok(())` when the fixed deadline is reached. The
97 /// future returns a [`TimeError`] if the backend later fails.
98 ///
99 /// # Errors
100 ///
101 /// Returns [`TimeError::InstantOverflow`] when the deadline cannot be
102 /// represented. Returns any error produced while creating the notification
103 /// for that deadline.
104 #[inline]
105 fn after(&self, duration: Duration) -> Result<TimerFuture, TimeError> {
106 let deadline = self.clock().deadline_after(duration)?;
107 self.at(deadline)
108 }
109}
110
111impl<T> Timer for std::sync::Arc<T>
112where
113 T: Timer + ?Sized,
114{
115 /// Delegates access to the shared timer's clock.
116 ///
117 /// # Returns
118 ///
119 /// The clock exposed by the wrapped timer.
120 #[inline(always)]
121 fn clock(&self) -> &dyn MonotonicClock {
122 self.as_ref().clock()
123 }
124
125 /// Delegates absolute deadline registration to the shared timer.
126 ///
127 /// # Parameters
128 ///
129 /// * `deadline` - Absolute deadline in the wrapped timer's clock domain.
130 ///
131 /// # Returns
132 ///
133 /// The wrapped timer's cancellation-safe completion future.
134 ///
135 /// # Errors
136 ///
137 /// Returns any registration error reported by the wrapped timer.
138 #[inline(always)]
139 fn at(&self, deadline: MonotonicInstant) -> Result<TimerFuture, TimeError> {
140 self.as_ref().at(deadline)
141 }
142
143 /// Delegates relative deadline registration to the shared timer.
144 ///
145 /// # Parameters
146 ///
147 /// * `duration` - Duration from the wrapped timer's current instant.
148 ///
149 /// # Returns
150 ///
151 /// The wrapped timer's cancellation-safe completion future.
152 ///
153 /// # Errors
154 ///
155 /// Returns any registration error reported by the wrapped timer.
156 #[inline(always)]
157 fn after(&self, duration: Duration) -> Result<TimerFuture, TimeError> {
158 self.as_ref().after(duration)
159 }
160}
161
162impl<T> Timer for Box<T>
163where
164 T: Timer + ?Sized,
165{
166 /// Delegates access to the boxed timer's clock.
167 ///
168 /// # Returns
169 ///
170 /// The clock exposed by the wrapped timer.
171 #[inline(always)]
172 fn clock(&self) -> &dyn MonotonicClock {
173 self.as_ref().clock()
174 }
175
176 /// Delegates absolute deadline registration to the boxed timer.
177 ///
178 /// # Parameters
179 ///
180 /// * `deadline` - Absolute deadline in the wrapped timer's clock domain.
181 ///
182 /// # Returns
183 ///
184 /// The wrapped timer's cancellation-safe completion future.
185 ///
186 /// # Errors
187 ///
188 /// Returns any registration error reported by the wrapped timer.
189 #[inline(always)]
190 fn at(&self, deadline: MonotonicInstant) -> Result<TimerFuture, TimeError> {
191 self.as_ref().at(deadline)
192 }
193
194 /// Delegates relative deadline registration to the boxed timer.
195 ///
196 /// # Parameters
197 ///
198 /// * `duration` - Duration from the wrapped timer's current instant.
199 ///
200 /// # Returns
201 ///
202 /// The wrapped timer's cancellation-safe completion future.
203 ///
204 /// # Errors
205 ///
206 /// Returns any registration error reported by the wrapped timer.
207 #[inline(always)]
208 fn after(&self, duration: Duration) -> Result<TimerFuture, TimeError> {
209 self.as_ref().after(duration)
210 }
211}