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)
}