maitake/time/
timeout.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
//! [`Timeout`]s limit the amount of time a [`Future`] is allowed to run before
//! it completes.
//!
//! See the documentation for the [`Timeout`] type for details.
use super::{timer::TimerError, Sleep, Timer};
use crate::util;
use core::{
    fmt,
    future::Future,
    pin::Pin,
    task::{Context, Poll},
    time::Duration,
};
use pin_project::pin_project;

/// A [`Future`] that requires an inner [`Future`] to complete within a
/// specified [`Duration`].
///
/// This `Future` is returned by the [`timeout`] and [`try_timeout`] functions,
/// and by the [`Timer::timeout`] and [`Timer::try_timeout`] methods.
///
/// [`timeout`]: super::timeout()
/// [`try_timeout`]: super::try_timeout
///
/// # 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.
#[derive(Debug)]
#[pin_project]
#[must_use = "futures do nothing unless `.await`ed or `poll`ed"]
pub struct Timeout<'timer, F> {
    #[pin]
    sleep: Sleep<'timer>,
    #[pin]
    future: F,
    duration: Duration,
}

/// An error indicating that a [`Timeout`] elapsed before the inner [`Future`]
/// completed.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Elapsed(Duration);

// === impl Timeout ===

impl<'timer, F: Future> Timeout<'timer, F> {
    /// Returns a new [`Timeout`] future that fails if `future` does not
    /// complete within the specified `duration`.
    ///
    /// The timeout will be driven by the specified `timer`.
    ///
    /// See the documentation for the [`Timeout`] future for details.
    fn new(sleep: Sleep<'timer>, future: F) -> Self {
        let duration = sleep.duration();
        Self {
            sleep,
            future,
            duration,
        }
    }

    /// Consumes this `Timeout`, returning the inner [`Future`].
    ///
    /// This can be used to continue polling the inner [`Future`] without
    /// requiring it to complete prior to the specified timeout.
    pub fn into_inner(self) -> F {
        self.future
    }

    /// Borrows the inner [`Future`] immutably.
    pub fn get_ref(&self) -> &F {
        &self.future
    }

    /// Mutably the inner [`Future`].
    pub fn get_mut(&mut self) -> &mut F {
        &mut self.future
    }

    /// Borrows the inner [`Future`] as a [`Pin`]ned reference, if this
    /// `Timeout` is pinned.
    pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut F> {
        self.project().future
    }

    /// Returns the [`Duration`] the inner [`Future`] is allowed to run for.
    pub fn duration(&self) -> Duration {
        self.duration
    }
}

impl<F: Future> Future for Timeout<'_, F> {
    type Output = Result<F::Output, Elapsed>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.project();
        // first, poll the sleep.
        if this.sleep.poll(cx).is_ready() {
            return Poll::Ready(Err(Elapsed(*this.duration)));
        }

        // then, try polling the future.
        if let Poll::Ready(output) = this.future.poll(cx) {
            return Poll::Ready(Ok(output));
        }

        Poll::Pending
    }
}

// === impl Elapsed ===

impl From<Elapsed> for Duration {
    #[inline]
    fn from(Elapsed(duration): Elapsed) -> Self {
        duration
    }
}

impl fmt::Display for Elapsed {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "timed out after {:?}", self.0)
    }
}

impl Elapsed {
    /// Returns the [`Duration`] the inner [`Future`] was allowed to run for.
    pub fn duration(self) -> Duration {
        self.0
    }
}

feature! {
    #![feature = "core-error"]
    impl core::error::Error for Elapsed {}
}

// === impl Timer ===

impl Timer {
    /// Returns a new [`Timeout`] future that fails if `future` does not
    /// complete within the specified `duration`.
    ///
    /// The timeout will be driven by this 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
    ///
    /// This method panics if the provided duration exceeds the [maximum sleep
    /// duration][max] allowed this timer.
    ///
    /// For a version of this method that does not panic, use the
    /// [`Timer::try_timeout`] method instead.
    ///
    /// [max]: Timer::max_duration
    #[track_caller]
    pub fn timeout<F: Future>(&self, duration: Duration, future: F) -> Timeout<'_, F> {
        util::expect_display(
            self.try_timeout(duration, future),
            "cannot create `Timeout` future",
        )
    }

    /// Returns a new [`Timeout`] future that fails if `future` does not
    /// complete within the specified `duration`.
    ///
    /// The timeout will be driven by this timer.
    ///
    /// # Returns
    ///
    /// - [`Ok`]`(`[`Timeout`]`)` if a new [`Timeout`] future was created
    ///   successfully.
    /// - [`Err`]`(`[`TimerError::DurationTooLong`]`)` if the requested timeout
    ///   duration exceeds this timer'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 method does not panic. For a version of this methodthat panics
    /// rather than returning a [`TimerError`], use [`Timer::timeout`].
    ///
    pub fn try_timeout<F: Future>(
        &self,
        duration: Duration,
        future: F,
    ) -> Result<Timeout<'_, F>, TimerError> {
        let sleep = self.try_sleep(duration)?;
        Ok(Timeout::new(sleep, future))
    }
}