1 //! Additional utilities for tracking time.
2 //!
3 //! This module provides additional utilities for executing code after a set period
4 //! of time. Currently there is only one:
5 //!
6 //! * `DelayQueue`: A queue where items are returned once the requested delay
7 //!   has expired.
8 //!
9 //! This type must be used from within the context of the `Runtime`.
10 
11 use std::future::Future;
12 use std::time::Duration;
13 use tokio::time::Timeout;
14 
15 mod wheel;
16 
17 pub mod delay_queue;
18 
19 #[doc(inline)]
20 pub use delay_queue::DelayQueue;
21 
22 /// A trait which contains a variety of convenient adapters and utilities for `Future`s.
23 pub trait FutureExt: Future {
24     /// A wrapper around [`tokio::time::timeout`], with the advantage that it is easier to write
25     /// fluent call chains.
26     ///
27     /// # Examples
28     ///
29     /// ```rust
30     /// use tokio::{sync::oneshot, time::Duration};
31     /// use tokio_util::time::FutureExt;
32     ///
33     /// # async fn dox() {
34     /// let (tx, rx) = oneshot::channel::<()>();
35     ///
36     /// let res = rx.timeout(Duration::from_millis(10)).await;
37     /// assert!(res.is_err());
38     /// # }
39     /// ```
timeout(self, timeout: Duration) -> Timeout<Self> where Self: Sized,40     fn timeout(self, timeout: Duration) -> Timeout<Self>
41     where
42         Self: Sized,
43     {
44         tokio::time::timeout(timeout, self)
45     }
46 }
47 
48 impl<T: Future + ?Sized> FutureExt for T {}
49 
50 // ===== Internal utils =====
51 
52 enum Round {
53     Up,
54     Down,
55 }
56 
57 /// Convert a `Duration` to milliseconds, rounding up and saturating at
58 /// `u64::MAX`.
59 ///
60 /// The saturating is fine because `u64::MAX` milliseconds are still many
61 /// million years.
62 #[inline]
ms(duration: Duration, round: Round) -> u6463 fn ms(duration: Duration, round: Round) -> u64 {
64     const NANOS_PER_MILLI: u32 = 1_000_000;
65     const MILLIS_PER_SEC: u64 = 1_000;
66 
67     // Round up.
68     let millis = match round {
69         Round::Up => (duration.subsec_nanos() + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI,
70         Round::Down => duration.subsec_millis(),
71     };
72 
73     duration
74         .as_secs()
75         .saturating_mul(MILLIS_PER_SEC)
76         .saturating_add(u64::from(millis))
77 }
78