agnostic_lite/async_io/
timeout.rs1use crate::time::{AsyncLocalTimeout, AsyncTimeout, Elapsed};
2use ::async_io::Timer;
3use core::{
4 future::Future,
5 pin::Pin,
6 task::{Context, Poll},
7 time::Duration,
8};
9use std::time::Instant;
10
11pin_project_lite::pin_project! {
12 pub struct AsyncIoTimeout<F> {
14 #[pin]
15 future: F,
16 #[pin]
17 delay: Timer,
18 }
19}
20
21impl<F: Future> Future for AsyncIoTimeout<F> {
22 type Output = Result<F::Output, Elapsed>;
23
24 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
25 let this = self.project();
26 match this.future.poll(cx) {
27 Poll::Pending => {}
28 other => return other.map(Ok),
29 }
30
31 if this.delay.poll(cx).is_ready() {
32 Poll::Ready(Err(Elapsed))
33 } else {
34 Poll::Pending
35 }
36 }
37}
38
39impl<F: Future + Send> AsyncTimeout<F> for AsyncIoTimeout<F> {
40 type Instant = Instant;
41
42 fn timeout(t: Duration, fut: F) -> Self
43 where
44 Self: Sized,
45 {
46 <Self as AsyncLocalTimeout<F>>::timeout_local(t, fut)
47 }
48
49 fn timeout_at(deadline: Instant, fut: F) -> Self
50 where
51 Self: Sized,
52 {
53 <Self as AsyncLocalTimeout<F>>::timeout_local_at(deadline, fut)
54 }
55}
56
57impl<F> AsyncLocalTimeout<F> for AsyncIoTimeout<F>
58where
59 F: Future,
60{
61 type Instant = Instant;
62
63 fn timeout_local(timeout: Duration, fut: F) -> Self
64 where
65 Self: Sized,
66 {
67 Self {
68 future: fut,
69 delay: Timer::after(timeout),
70 }
71 }
72
73 fn timeout_local_at(deadline: Instant, fut: F) -> Self
74 where
75 Self: Sized,
76 {
77 Self {
78 future: fut,
79 delay: Timer::at(deadline),
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::{AsyncIoTimeout, AsyncTimeout, Timer};
87 use std::time::{Duration, Instant};
88
89 const BAD: Duration = Duration::from_secs(1);
90 const GOOD: Duration = Duration::from_millis(10);
91 const TIMEOUT: Duration = Duration::from_millis(200);
92 const BOUND: Duration = Duration::from_secs(10);
93
94 #[test]
95 fn test_timeout() {
96 futures::executor::block_on(async {
97 let fut = async {
98 Timer::after(BAD).await;
99 1
100 };
101 let start = Instant::now();
102 let rst = AsyncIoTimeout::timeout(TIMEOUT, fut).await;
103 assert!(rst.is_err());
104 let elapsed = start.elapsed();
105 assert!(elapsed >= TIMEOUT && elapsed <= TIMEOUT + BOUND);
106
107 let fut = async {
108 Timer::after(GOOD).await;
109 1
110 };
111
112 let start = Instant::now();
113 let rst = AsyncIoTimeout::timeout(TIMEOUT, fut).await;
114 assert!(rst.is_ok());
115 let elapsed = start.elapsed();
116 assert!(elapsed >= GOOD && elapsed <= GOOD + BOUND);
117 });
118 }
119
120 #[test]
121 fn test_timeout_at() {
122 futures::executor::block_on(async {
123 let fut = async {
124 Timer::after(BAD).await;
125 1
126 };
127 let start = Instant::now();
128 let rst = AsyncIoTimeout::timeout_at(Instant::now() + TIMEOUT, fut).await;
129 assert!(rst.is_err());
130 let elapsed = start.elapsed();
131 assert!(elapsed >= TIMEOUT && elapsed <= TIMEOUT + BOUND);
132
133 let fut = async {
134 Timer::after(GOOD).await;
135 1
136 };
137
138 let start = Instant::now();
139 let rst = AsyncIoTimeout::timeout_at(Instant::now() + TIMEOUT, fut).await;
140 assert!(rst.is_ok());
141 let elapsed = start.elapsed();
142 assert!(elapsed >= GOOD && elapsed <= GOOD + BOUND);
143 });
144 }
145}