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}