Skip to main content

qubit_clock/sleep/
blocking_sleeper.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 blocking adapter over the asynchronous Timer capability.
9
10use super::internal::ThreadWaker;
11use crate::{
12    MonotonicInstant,
13    TimeError,
14    Timer,
15    TimerFuture,
16};
17use std::sync::Arc;
18use std::task::{
19    Context,
20    Poll,
21    Waker,
22};
23use std::time::Duration;
24
25/// Adapts any [`Timer`] into synchronous blocking sleep operations.
26///
27/// This type owns no clock or scheduling policy of its own. It composes a
28/// shared timer and blocks only the calling thread while polling the timer's
29/// future. Clones share that same timer.
30///
31/// # Progress
32///
33/// The timer backend must still make progress after the calling thread parks.
34/// [`StdTimer`](crate::StdTimer) has its own worker. A
35/// [`ManualTimer`](crate::ManualTimer) requires another thread or controller to
36/// advance its clock. A `TokioTimer` requires its retained runtime to remain
37/// alive and be driven independently. Blocking the sole driver thread of a
38/// current-thread Tokio runtime while waiting on that same runtime's timer
39/// prevents the deadline from completing.
40#[derive(Clone)]
41pub struct BlockingSleeper {
42    /// Timer providing eager deadline registration and completion futures.
43    timer: Arc<dyn Timer>,
44}
45
46impl BlockingSleeper {
47    /// Creates a blocking adapter over a shared timer.
48    ///
49    /// # Parameters
50    ///
51    /// * `timer` - Timer used for every blocking deadline. Its backend must
52    ///   progress independently while the calling thread is parked.
53    ///
54    /// # Returns
55    ///
56    /// A cloneable blocking sleeper composing `timer`.
57    #[must_use]
58    #[inline(always)]
59    pub const fn new(timer: Arc<dyn Timer>) -> Self {
60        Self { timer }
61    }
62
63    /// Returns the timer composed by this adapter.
64    ///
65    /// # Returns
66    ///
67    /// The timer used to register blocking sleeps.
68    #[must_use]
69    #[inline(always)]
70    pub fn timer(&self) -> &dyn Timer {
71        self.timer.as_ref()
72    }
73
74    /// Blocks the current thread until an absolute deadline is reached.
75    ///
76    /// # Parameters
77    ///
78    /// * `deadline` - Deadline in the composed timer's clock domain.
79    ///
80    /// # Returns
81    ///
82    /// `Ok(())` after the deadline future completes.
83    ///
84    /// # Errors
85    ///
86    /// Returns any error produced while registering or completing the
87    /// deadline.
88    ///
89    /// # Panics
90    ///
91    /// Panics when the composed timer panics during registration or its
92    /// returned future panics while being polled.
93    ///
94    /// # Blocking
95    ///
96    /// Parks the calling thread while the composed timer is pending. The timer
97    /// backend must be driven independently during that interval.
98    #[inline(always)]
99    pub fn sleep_until(
100        &self,
101        deadline: MonotonicInstant,
102    ) -> Result<(), TimeError> {
103        let future = self.timer.at(deadline)?;
104        Self::block_on(future)
105    }
106
107    /// Blocks the current thread for a relative duration.
108    ///
109    /// The timer fixes the absolute deadline before this method begins polling
110    /// and parking.
111    ///
112    /// # Parameters
113    ///
114    /// * `duration` - Duration measured by the composed timer's clock.
115    ///
116    /// # Returns
117    ///
118    /// `Ok(())` after the deadline future completes.
119    ///
120    /// # Errors
121    ///
122    /// Returns deadline overflow, registration failure, or a backend failure
123    /// reported while waiting.
124    ///
125    /// # Panics
126    ///
127    /// Panics when the composed timer panics during registration or its
128    /// returned future panics while being polled.
129    ///
130    /// # Blocking
131    ///
132    /// Parks the calling thread while the composed timer is pending. The timer
133    /// backend must be driven independently during that interval.
134    #[inline(always)]
135    pub fn sleep_for(&self, duration: Duration) -> Result<(), TimeError> {
136        let future = self.timer.after(duration)?;
137        Self::block_on(future)
138    }
139
140    /// Polls one timer future, parking between incomplete polls.
141    ///
142    /// # Parameters
143    ///
144    /// * `future` - Eagerly registered timer future to drive to completion.
145    ///
146    /// # Returns
147    ///
148    /// The completion result produced by `future`.
149    ///
150    /// # Errors
151    ///
152    /// Returns the completion error reported by `future`.
153    ///
154    /// # Panics
155    ///
156    /// Panics when polling the timer future panics.
157    fn block_on(mut future: TimerFuture) -> Result<(), TimeError> {
158        let thread_waker = Arc::new(ThreadWaker::new(std::thread::current()));
159        let waker = Waker::from(Arc::clone(&thread_waker));
160        let mut context = Context::from_waker(&waker);
161        loop {
162            thread_waker.clear_notification();
163            if let Poll::Ready(result) = future.as_mut().poll(&mut context) {
164                return result;
165            }
166            while !thread_waker.take_notification() {
167                std::thread::park();
168            }
169        }
170    }
171}
172
173impl std::fmt::Debug for BlockingSleeper {
174    /// Formats this adapter without requiring the timer trait object to be
175    /// debug-formattable.
176    ///
177    /// # Parameters
178    ///
179    /// * `formatter` - Destination formatter.
180    ///
181    /// # Returns
182    ///
183    /// `Ok(())` when formatting succeeds.
184    ///
185    /// # Errors
186    ///
187    /// Returns [`std::fmt::Error`] when the destination rejects output.
188    #[inline]
189    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        formatter
191            .debug_struct("BlockingSleeper")
192            .finish_non_exhaustive()
193    }
194}