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    /// 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}