smoltimeout/
lib.rs

1/* ┌────────────────────────────────────────────────────────────────────────────────────────────┐ *\
2 * │                                                                                            │ *
3 * │ This Source Code Form is subject to the terms of the Mozilla Public                        │ *
4 * │ License, v. 2.0. If a copy of the MPL was not distributed with this                        │ *
5 * │ file, You can obtain one at http://mozilla.org/MPL/2.0/.                                   │ *
6 * │                                                                                            │ *
7\* └────────────────────────────────────────────────────────────────────────────────────────────┘ */
8
9/* ┌────────────────────────────────────────────────────────────────────────────────────────────┐ *\
10 * │                                       Configuration                                        │ *
11\* └────────────────────────────────────────────────────────────────────────────────────────────┘ */
12
13// This is completely useless: due to this library depends async-io (it is not optional), and async-io uses std. so the final generated native code is still linked to std.
14//#![no_std]
15
16/* ┌────────────────────────────────────────────────────────────────────────────────────────────┐ *\
17 * │                                       Documentation                                        │ *
18\* └────────────────────────────────────────────────────────────────────────────────────────────┘ */
19
20//! A way to poll a future until it or a timer completes.
21//!
22//! ## Example
23//! see [`TimeoutExt::timeout`] and [`TimeoutExt::deadline`].
24
25/* ┌────────────────────────────────────────────────────────────────────────────────────────────┐ *\
26 * │                                          Imports                                           │ *
27\* └────────────────────────────────────────────────────────────────────────────────────────────┘ */
28
29use async_io::Timer;
30use core::future::Future;
31use core::pin::Pin;
32use core::task::{Context, Poll};
33use std::time::{Instant, Duration};
34use pin_project_lite::pin_project;
35
36/* ┌────────────────────────────────────────────────────────────────────────────────────────────┐ *\
37 * │                                    struct Timed<Fut>                                     │ *
38\* └────────────────────────────────────────────────────────────────────────────────────────────┘ */
39
40pin_project! {
41    /// A helper that polling both inner Future, and a [`Timer`] that will complete after a specified
42    /// timeout or deadline.
43    ///
44    /// ## Example
45    /// see [`TimeoutExt::timeout`] and [`TimeoutExt::deadline`].
46    #[derive(Debug)]
47    pub struct Timed<Fut: Future> {
48        #[pin]
49        future: Fut,
50        #[pin]
51        timer: Timer,
52    }
53}
54
55/* ┌────────────────────────────────────────────────────────────────────────────────────────────┐ *\
56 * │                                  trait TimeoutExt: Future                                  │ *
57\* └────────────────────────────────────────────────────────────────────────────────────────────┘ */
58
59/// An extension trait for [`Future`]s that provides a way to create [`Timeout`]s.
60pub trait TimedExt: Future {
61    /// Given a [`Duration`], creates and returns a new [`Timed`] that will poll both the Future, and a [`Timer`] that will complete after the provided duration.
62    /// * first polling the Future, returns it's output if any.
63    /// * then polling the Timer, and fallback to [`None`] if the Timer completes.
64    ///
65    /// ## Example
66    ///
67    /// ```rust
68    /// use async_io::Timer;
69    /// # use futures_lite::future;
70    /// use smoltimeout::TimeoutExt;
71    /// use std::time::Duration;
72    ///
73    /// # future::block_on(async {
74    /// #
75    /// let mut timer = Timer::after(Duration::from_millis(250));
76    /// let foo = async {
77    ///     timer.await;
78    ///     24
79    /// }.timeout(Duration::from_millis(100));
80    /// assert_eq!(foo.await, None);
81    ///
82    /// timer = Timer::after(Duration::from_millis(100));
83    /// let bar = async move {
84    ///     timer.await;
85    ///     42
86    /// }.timeout(Duration::from_millis(250));
87    /// assert_eq!(bar.await, Some(42));
88    ///
89    /// timer = Timer::after(Duration::from_millis(50));
90    /// let mod_of_delta4 = async move {
91    ///     timer.await;
92    ///     (21*2) as f64
93    /// }.timeout(Duration::from_millis(0));
94    /// Timer::after(Duration::from_millis(100)).await;
95    /// assert_eq!(mod_of_delta4.await, Some(42.0));
96    /// #
97    /// # })
98    /// ```
99    fn timeout(self, after: Duration) -> Timed<Self>
100    where
101        Self: Sized,
102    {
103        Timed {
104            future: self,
105            timer: Timer::after(after),
106        }
107    }
108
109    /// Given a [`Instant`], creates and returns a new [`Timed`] that will poll both the Future
110    /// and a [`Timer`] that will complete after the provided deadline.
111    /// * first polling the Future, returns it's output if any.
112    /// * then polling the Timer, and fallback to [`None`] if the Timer completes.
113    ///
114    /// ## Example
115    ///
116    /// ```rust
117    /// use async_io::Timer;
118    /// # use futures_lite::future;
119    /// use smoltimeout::TimeoutExt;
120    /// use std::time::{Instant, Duration};
121    ///
122    /// # future::block_on(async {
123    /// #
124    /// let mut timer = Timer::after(Duration::from_millis(250));
125    /// let foo = async move {
126    ///     timer.await;
127    ///     24
128    /// }.deadline(Instant::now() + Duration::from_millis(100));
129    /// assert_eq!(foo.await, None);
130    ///
131    /// timer = Timer::after(Duration::from_millis(100));
132    /// let bar = async move {
133    ///     timer.await;
134    ///     42
135    /// }.deadline(Instant::now() + Duration::from_millis(250));
136    /// assert_eq!(bar.await, Some(42));
137    ///
138    /// timer = Timer::after(Duration::from_millis(50));
139    /// let mod_of_delta4 = async move {
140    ///     timer.await;
141    ///     (21*2) as f64
142    /// }.deadline(Instant::now());
143    /// Timer::after(Duration::from_millis(100)).await;
144    /// assert_eq!(mod_of_delta4.await, Some(42.0));
145    /// #
146    /// # })
147    /// ```
148    fn deadline(self, deadline: Instant) -> Timed<Self>
149    where
150        Self: Sized,
151    {
152        Timed {
153            future: self,
154            timer: Timer::at(deadline),
155        }
156    }
157}
158
159impl<Fut: Future> TimedExt for Fut {}
160
161/// for provide compatibly to older versions.
162pub use {
163    TimedExt as TimeoutExt,
164    Timed as Timeout,
165};
166
167/* ┌────────────────────────────────────────────────────────────────────────────────────────────┐ *\
168 * │                                impl Future for Timeout<Fut>                                │ *
169\* └────────────────────────────────────────────────────────────────────────────────────────────┘ */
170
171impl<Fut: Future> Future for Timed<Fut> {
172    type Output = Option<Fut::Output>;
173
174    fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output> {
175        let this = self.project();
176
177        if let Poll::Ready(output) = this.future.poll(ctx) {
178            return Poll::Ready(Some(output));
179        }
180
181        if this.timer.poll(ctx).is_ready() {
182            return Poll::Ready(None);
183        }
184
185        Poll::Pending
186    }
187}