maitake/time.rs
1//! Utilities for tracking time and constructing system timers.
2//!
3//! # Futures
4//!
5//! This module contains the following [`Future`]s:
6//!
7//! - [`Sleep`], a future which completes after a specified duration,
8//! - [`Timeout`], which wraps another [`Future`] to limit the duration it can
9//! run for.
10//!
11//! # Timers
12//!
13//! The [`Sleep`] and [`Timeout`] futures do not complete on their own. Instead,
14//! they must be driven by a [`Timer`], which tracks the current time and
15//! notifies time-based futures when their deadlines are reached.
16//!
17//! The [`Timer`] struct implements a [hierarchical timer wheel][wheel], a data
18//! structure for tracking large numbers of timers efficiently. It is used to
19//! create [`Sleep`]s and [`Timeout`]s, and notify them when their deadlines
20//! complete. In order to be used, a [`Timer`] must be driven by a hardware time
21//! source. See [the `Timer` documentation][driving-timers] for more information
22//! on using this type to implement a system timer.
23//!
24//! ### Global Timers
25//!
26//! In most cases, it is desirable to have a single global timer instance that
27//! drives all time-based futures in the system. In particular, creating new
28//! [`Sleep`] and [`Timeout`] futures typically requires a reference to a
29//! [`Timer`] instance, which can be inconvenient to pass around to the points
30//! in a system where [`Sleep`] and [`Timeout`] futures are created.
31//!
32//! Therefore, the `maitake` timer also includes support for setting a global
33//! timer, using the [`set_global_timer`] function. Once a global timer
34//! has been initialized, the [`sleep()`] and [`timeout()`] free functions in
35//! this module can be used to create time-based futures without a reference to a
36//! [`Timer`] instance. These functions will always create futures bound to the
37//! global default timer.
38//!
39//! Note that a global default timer can only be set once. Subsequent calls to
40//! [`set_global_timer`] after a global timer has been initialized will
41//! return an error.
42//!
43//! [wheel]: http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf
44//! [driving-timers]: Timer#driving-timers
45#![warn(missing_docs, missing_debug_implementations)]
46pub mod clock;
47pub mod timeout;
48mod timer;
49
50use crate::util;
51
52#[doc(inline)]
53pub use self::{
54 clock::{Clock, Instant},
55 timeout::Timeout,
56 timer::{set_global_timer, sleep::Sleep, AlreadyInitialized, Timer, TimerError, Turn},
57};
58pub use core::time::Duration;
59
60use core::future::Future;
61
62/// Returns a [`Future`] that completes after the specified [`Duration`].
63///
64/// This function uses the [global default timer][global], and the returned
65/// [`Sleep`] future will live for the `'static` lifetime. See [the
66/// module-level documentation][global] for details on using the global default
67/// timer.
68///
69/// # Panics
70///
71/// - If a [global timer][global] was not set by calling [`set_global_timer`]
72/// first.
73/// - If the provided duration exceeds the [maximum sleep duration][max] allowed
74/// by the global default timer.
75///
76/// For a version of this function that does not panic, see [`try_sleep()`]
77/// instead.
78///
79/// # Examples
80///
81/// ```
82/// use maitake::time;
83///
84/// async fn example() {
85/// time::sleep(time::Duration::from_secs(1)).await;
86/// println!("one second has passed!");
87/// }
88/// ```
89///
90/// [global]: #global-timers
91/// [max]: Timer::max_duration
92#[track_caller]
93pub fn sleep(duration: Duration) -> Sleep<'static> {
94 util::expect_display(try_sleep(duration), "cannot create `Sleep` future")
95}
96
97/// Returns a [`Future`] that completes after the specified [`Duration`].
98///
99/// This function uses the [global default timer][global], and the returned
100/// [`Timeout`] future will live for the `'static` lifetime. See [the
101/// module-level documentation][global] for details on using the global default
102/// timer.
103///
104/// # Returns
105///
106/// - [`Ok`]`(`[`Sleep`]`)` if a new [`Sleep`] future was created
107/// successfully.
108/// - [`Err`]`(`[`TimerError::NoGlobalTimer`]`)` if a [global timer][global] was
109/// not set by calling [`set_global_timer`] first.
110/// - [`Err`]`(`[`TimerError::DurationTooLong`]`)` if the requested sleep
111/// duration exceeds the [global timer][global]'s [maximum sleep
112/// duration](Timer::max_duration`).
113///
114/// # Panics
115///
116/// This function does not panic. For a version of this function that panics
117/// rather than returning a [`TimerError`], use [`sleep()`] instead.
118///
119/// # Examples
120///
121/// ```
122/// use maitake::time;
123///
124/// async fn example() {
125/// // try to sleep for one second
126/// match time::try_sleep(time::Duration::from_secs(1)) {
127/// // the sleep future was created successfully, so we can now await it.
128/// Ok(sleep) => {
129/// sleep.await;
130/// println!("one second has passed!");
131/// },
132/// Err(time::TimerError::NoGlobalTimer) =>
133/// println!("timer is not initialized"),
134/// Err(time::TimerError::DurationTooLong { .. }) =>
135/// unreachable!("1 second should not exceed the max duration"),
136/// Err(error) => panic!("unexpected timer error: {error}"),
137/// }
138/// }
139/// ```
140///
141/// [global]: #global-timers
142pub fn try_sleep(duration: Duration) -> Result<Sleep<'static>, TimerError> {
143 timer::global::default()?.try_sleep(duration)
144}
145
146/// Requires the provided [`Future`] to complete before the specified [`Duration`]
147/// has elapsed.
148///
149/// This function uses the [global default timer][global], and the returned
150/// [`Timeout`] future will live for the `'static` lifetime. See [the
151/// module-level documentation][global] for details on using the global default
152/// timer.
153///
154/// # Output
155///
156/// - [`Ok`]`(F::Output)` if the inner future completed before the specified
157/// timeout.
158/// - [`Err`]`(`[`Elapsed`]`)` if the timeout elapsed before the inner [`Future`]
159/// completed.
160///
161/// # Cancellation
162///
163/// Dropping a `Timeout` future cancels the timeout. The wrapped [`Future`] can
164/// be extracted from the `Timeout` future by calling [`Timeout::into_inner`],
165/// allowing the future to be polled without failing if the timeout elapses.
166///
167/// # Panics
168///
169/// - If a [global timer][global] was not set by calling [`set_global_timer`]
170/// first.
171/// - If the provided duration exceeds the [maximum sleep duration][max] allowed
172/// by the global default timer.
173///
174/// For a version of this function that does not panic, use the [`try_timeout()`]
175/// function instead.
176///
177/// # Examples
178///
179/// ```
180/// use maitake::time::{timeout, Duration};
181///
182/// /// A function that might wait for a long time before it completes.
183/// async fn do_slow_stuff() {
184/// // do some slow stuff ...
185/// }
186///
187/// async fn example() {
188/// // try to do some slow stuff, but if it takes longer than 10 seconds,
189/// // give up.
190/// match timeout(Duration::from_secs(10), do_slow_stuff()).await {
191/// Ok(_) => println!("slow stuff completed successfully"),
192/// Err(elapsed) =>
193/// eprintln!("slow stuff did not complete in {:?}!", elapsed.duration()),
194/// }
195/// }
196/// ```
197///
198/// [global]: #global-timers
199/// [max]: Timer::max_duration
200/// [`Elapsed`]: timeout::Elapsed
201#[track_caller]
202pub fn timeout<F: Future>(duration: Duration, future: F) -> Timeout<'static, F> {
203 util::expect_display(
204 try_timeout(duration, future),
205 "cannot create `Timeout` future",
206 )
207}
208
209/// Requires the provided [`Future`] to complete before the specified [`Duration`]
210/// has elapsed.
211///
212/// This function uses the [global default timer][global], and the returned
213/// [`Timeout`] future will live for the `'static` lifetime. See [the
214/// module-level documentation][global] for details on using the global default
215/// timer.
216///
217/// # Returns
218///
219/// - [`Ok`]`(`[`Timeout`]`)` if a new [`Timeout`] future was created
220/// successfully.
221/// - [`Err`]`(`[`TimerError::NoGlobalTimer`]`)` if a [global timer][global] was
222/// not set by calling [`set_global_timer`] first.
223/// - [`Err`]`(`[`TimerError::DurationTooLong`]`)` if the requested timeout
224/// duration exceeds the [global timer][global]'s [maximum sleep
225/// duration](Timer::max_duration`).
226///
227/// # Output
228///
229/// - [`Ok`]`(F::Output)` if the inner future completed before the specified
230/// timeout.
231/// - [`Err`]`(`[`Elapsed`]`)` if the timeout elapsed before the inner [`Future`]
232/// completed.
233///
234/// # Cancellation
235///
236/// Dropping a `Timeout` future cancels the timeout. The wrapped [`Future`] can
237/// be extracted from the `Timeout` future by calling [`Timeout::into_inner`],
238/// allowing the future to be polled without failing if the timeout elapses.
239///
240/// # Panics
241///
242/// This function does not panic. For a version of this function that panics
243/// rather than returning a [`TimerError`], use [`timeout()`] instead.
244///
245/// # Examples
246///
247/// ```
248/// use maitake::time::{self, Duration};
249///
250/// /// A function that might wait for a long time before it completes.
251/// async fn do_slow_stuff() {
252/// // do some slow stuff ...
253/// }
254///
255/// async fn example() {
256/// // if we can't create the timeout, just wait for the future to complete
257/// // with no timeout.
258/// match time::try_timeout(Duration::from_secs(10), do_slow_stuff()) {
259/// // we successfully created a timeout, so await the timeout future.
260/// Ok(timeout) => match timeout.await {
261/// Ok(_) => {},
262/// Err(elapsed) => {
263/// eprintln!("slow stuff did not complete in {:?}", elapsed.duration());
264/// return;
265/// },
266/// },
267/// // a timeout could not be created, so just try the slow stuff
268/// // without setting the timeout.
269/// Err(_) => do_slow_stuff().await,
270/// };
271///
272/// println!("slow stuff completed successfully");
273/// }
274/// ```
275///
276/// [global]: #global-timers
277/// [`Elapsed`]: timeout::Elapsed
278pub fn try_timeout<F: Future>(
279 duration: Duration,
280 future: F,
281) -> Result<Timeout<'static, F>, timer::TimerError> {
282 timer::global::default()?.try_timeout(duration, future)
283}