Skip to main content

qubit_clock/timer/
std_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 a standard-library timer with one shared scheduler worker.
9
10use crate::timer::internal::std_timer_future::StdTimerFuture;
11use crate::timer::internal::std_timer_scheduler::StdTimerScheduler;
12use crate::timer::internal::std_timer_waiter::StdTimerWaiter;
13use crate::{
14    MonotonicClock,
15    MonotonicInstant,
16    StdMonotonicClock,
17    TimeError,
18    Timer,
19    TimerFuture,
20};
21use std::sync::Arc;
22use std::time::{
23    Duration,
24    Instant,
25};
26
27/// A real-time asynchronous timer backed by [`std::time::Instant`].
28///
29/// Every standard Timer in the process shares one scheduler worker. The worker
30/// starts lazily with the first future registration and remains parked while
31/// idle so later registrations do not need to create another native thread.
32/// If that worker exits unexpectedly, its active futures are awakened and
33/// return [`TimeError::TimerUnavailable`] with
34/// [`TimerUnavailableError::SchedulerWorkerTerminated`](crate::TimerUnavailableError::SchedulerWorkerTerminated)
35/// instead of remaining pending or reporting false deadline completion. A
36/// later registration starts a replacement worker generation.
37pub struct StdTimer {
38    /// Private clock handle retaining the source domain and native origin.
39    clock: StdMonotonicClock,
40    /// Process-wide scheduler shared by every standard Timer registration.
41    scheduler: Arc<StdTimerScheduler>,
42}
43
44impl StdTimer {
45    /// Creates a timer backed by a new standard monotonic clock.
46    ///
47    /// # Returns
48    ///
49    /// A timer with a fresh clock domain and the process-wide scheduler.
50    #[must_use]
51    #[inline]
52    pub fn new() -> Self {
53        let clock = StdMonotonicClock::new();
54        Self::from_clock(&clock)
55    }
56
57    /// Creates a timer sharing the supplied standard clock's exact domain.
58    ///
59    /// # Parameters
60    ///
61    /// * `clock` - Standard clock whose domain and origin drive this timer.
62    ///
63    /// # Returns
64    ///
65    /// A timer retaining the process-wide lazy scheduler.
66    #[must_use]
67    #[inline]
68    pub fn from_clock(clock: &StdMonotonicClock) -> Self {
69        Self {
70            clock: clock.same_domain_handle(),
71            scheduler: StdTimerScheduler::shared(),
72        }
73    }
74
75    /// Converts a domain-scoped deadline to its native standard instant.
76    ///
77    /// # Parameters
78    ///
79    /// * `deadline` - Absolute deadline in the source clock domain.
80    ///
81    /// # Returns
82    ///
83    /// The corresponding native instant.
84    ///
85    /// # Errors
86    ///
87    /// Returns [`TimeError::ClockDomainMismatch`] for a foreign deadline and
88    /// [`TimeError::InstantOverflow`] when conversion overflows.
89    fn native_deadline(
90        &self,
91        deadline: MonotonicInstant,
92    ) -> Result<Instant, TimeError> {
93        deadline.validate_domain(self.clock.domain())?;
94        self.clock
95            .origin()
96            .checked_add(deadline.elapsed_since_origin())
97            .ok_or(TimeError::InstantOverflow)
98    }
99
100    /// Registers a native deadline with the shared scheduler.
101    ///
102    /// # Parameters
103    ///
104    /// * `deadline` - Native deadline to register.
105    /// * `now` - Native instant sampled for this registration.
106    ///
107    /// # Returns
108    ///
109    /// A cancellation-safe future whose registration is already active, or an
110    /// immediately ready future when the deadline has been reached.
111    ///
112    /// # Errors
113    ///
114    /// Returns a scheduler startup error before returning a future.
115    ///
116    /// # Panics
117    ///
118    /// Panics when scheduler registration identifiers or worker generations
119    /// are exhausted, or an internal scheduler index invariant is violated.
120    #[inline]
121    fn schedule(
122        &self,
123        deadline: Instant,
124        now: Instant,
125    ) -> Result<TimerFuture, TimeError> {
126        if deadline <= now {
127            return Ok(Box::pin(std::future::ready(Ok(()))));
128        }
129        let waiter = Arc::new(StdTimerWaiter::new());
130        let waiter_id =
131            self.scheduler.register(deadline, Arc::clone(&waiter))?;
132        Ok(Box::pin(StdTimerFuture::new(
133            Arc::clone(&self.scheduler),
134            waiter_id,
135            waiter,
136        )))
137    }
138}
139
140impl Default for StdTimer {
141    /// Creates a standard timer with a fresh clock domain.
142    ///
143    /// # Returns
144    ///
145    /// A standard timer with a newly allocated clock domain.
146    ///
147    /// # Panics
148    ///
149    /// Panics if all process-wide clock-domain identifiers are exhausted.
150    #[inline(always)]
151    fn default() -> Self {
152        Self::new()
153    }
154}
155
156impl std::fmt::Debug for StdTimer {
157    /// Formats the retained clock without exposing scheduler internals.
158    ///
159    /// # Parameters
160    ///
161    /// * `formatter` - Destination formatter.
162    ///
163    /// # Returns
164    ///
165    /// `Ok(())` when formatting succeeds.
166    ///
167    /// # Errors
168    ///
169    /// Returns [`std::fmt::Error`] when the destination rejects output.
170    #[inline]
171    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        formatter
173            .debug_struct("StdTimer")
174            .field("clock", &self.clock)
175            .finish_non_exhaustive()
176    }
177}
178
179impl Timer for StdTimer {
180    /// Returns the private same-domain standard clock handle.
181    ///
182    /// # Returns
183    ///
184    /// The monotonic clock driving this timer.
185    #[inline(always)]
186    fn clock(&self) -> &dyn MonotonicClock {
187        &self.clock
188    }
189
190    /// Eagerly registers an absolute deadline with the shared scheduler.
191    ///
192    /// # Parameters
193    ///
194    /// * `deadline` - Deadline in this timer's clock domain.
195    ///
196    /// # Returns
197    ///
198    /// A cancellation-safe future whose registration is already active, or an
199    /// immediately ready future for a reached deadline. The future returns
200    /// [`TimeError::TimerUnavailable`] with
201    /// [`TimerUnavailableError::SchedulerWorkerTerminated`](crate::TimerUnavailableError::SchedulerWorkerTerminated)
202    /// when its scheduler worker exits unexpectedly.
203    ///
204    /// # Errors
205    ///
206    /// Returns a domain mismatch, native-instant overflow, or scheduler startup
207    /// error before returning a future.
208    ///
209    /// # Panics
210    ///
211    /// Panics when scheduler registration identifiers or worker generations
212    /// are exhausted, or an internal scheduler index invariant is violated.
213    fn at(&self, deadline: MonotonicInstant) -> Result<TimerFuture, TimeError> {
214        let deadline = self.native_deadline(deadline)?;
215        self.schedule(deadline, Instant::now())
216    }
217
218    /// Eagerly registers a relative deadline with the shared scheduler.
219    ///
220    /// This implementation samples the native instant once, avoiding the
221    /// monotonic-domain conversion required by the default trait method.
222    ///
223    /// # Parameters
224    ///
225    /// * `duration` - Delay before the future becomes ready.
226    ///
227    /// # Returns
228    ///
229    /// A cancellation-safe future whose registration is already active, or an
230    /// immediately ready future for a zero duration.
231    ///
232    /// # Errors
233    ///
234    /// Returns [`TimeError::InstantOverflow`] when the native deadline cannot
235    /// be represented, or a scheduler startup error before returning a future.
236    ///
237    /// # Panics
238    ///
239    /// Panics when scheduler registration identifiers or worker generations
240    /// are exhausted, or an internal scheduler index invariant is violated.
241    fn after(&self, duration: Duration) -> Result<TimerFuture, TimeError> {
242        let now = Instant::now();
243        let deadline = now
244            .checked_add(duration)
245            .ok_or(TimeError::InstantOverflow)?;
246        self.schedule(deadline, now)
247    }
248}