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