1use std::io;
19use std::pin::Pin;
20use std::task::{Context, Poll};
21use std::time::Duration;
22
23use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
24use tokio::time::{Instant, Sleep};
25
26#[derive(Debug)]
27struct IoTimeout {
28 duration: Duration,
29 deadline: Option<Instant>,
31 sleep: Option<Pin<Box<Sleep>>>,
34}
35
36impl IoTimeout {
37 fn new(duration: Duration) -> Self {
38 Self {
39 duration,
40 deadline: None,
41 sleep: None,
42 }
43 }
44
45 fn poll<T>(
46 &mut self,
47 cx: &mut Context<'_>,
48 poll_io: impl FnOnce(&mut Context<'_>) -> Poll<io::Result<T>>,
49 ) -> Poll<io::Result<T>> {
50 let deadline = self
53 .deadline
54 .or_else(|| Instant::now().checked_add(self.duration));
55
56 let poll = poll_io(cx);
62 if poll.is_ready() {
63 self.deadline = None;
66 return poll;
67 }
68
69 if let Some(deadline) = deadline {
70 if self.poll_expired(cx, deadline) {
71 self.deadline = None;
72 return Poll::Ready(Err(io::ErrorKind::TimedOut.into()));
73 }
74 self.deadline = Some(deadline);
75 }
76 Poll::Pending
77 }
78
79 fn poll_expired(&mut self, cx: &mut Context<'_>, deadline: Instant) -> bool {
82 let sleep = self
83 .sleep
84 .get_or_insert_with(|| Box::pin(tokio::time::sleep_until(deadline)));
85 loop {
90 if sleep.as_mut().poll(cx).is_pending() {
91 return false;
92 }
93 if sleep.deadline() >= deadline {
94 return true;
95 }
96 sleep.as_mut().reset(deadline);
97 }
98 }
99}
100
101#[derive(Debug)]
103pub struct TimedReader<R> {
104 reader: R,
105 timeout: IoTimeout,
106}
107
108impl<R> TimedReader<R> {
109 pub fn new(reader: R, timeout: Duration) -> Self {
111 Self {
112 reader,
113 timeout: IoTimeout::new(timeout),
114 }
115 }
116}
117
118impl<R: AsyncRead + Unpin> AsyncRead for TimedReader<R> {
119 fn poll_read(
120 self: Pin<&mut Self>,
121 cx: &mut Context<'_>,
122 buf: &mut ReadBuf<'_>,
123 ) -> Poll<io::Result<()>> {
124 let this = self.get_mut();
125 this.timeout
126 .poll(cx, |cx| Pin::new(&mut this.reader).poll_read(cx, buf))
127 }
128}
129
130#[derive(Debug)]
132pub struct TimedWriter<W> {
133 writer: W,
134 timeout: IoTimeout,
135}
136
137impl<W> TimedWriter<W> {
138 pub fn new(writer: W, timeout: Duration) -> Self {
140 Self {
141 writer,
142 timeout: IoTimeout::new(timeout),
143 }
144 }
145}
146
147impl<W: AsyncWrite + Unpin> AsyncWrite for TimedWriter<W> {
148 fn poll_write(
149 self: Pin<&mut Self>,
150 cx: &mut Context<'_>,
151 buf: &[u8],
152 ) -> Poll<Result<usize, io::Error>> {
153 let this = self.get_mut();
154 this.timeout
155 .poll(cx, |cx| Pin::new(&mut this.writer).poll_write(cx, buf))
156 }
157
158 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
159 let this = self.get_mut();
160 this.timeout
161 .poll(cx, |cx| Pin::new(&mut this.writer).poll_flush(cx))
162 }
163
164 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
165 let this = self.get_mut();
166 this.timeout
167 .poll(cx, |cx| Pin::new(&mut this.writer).poll_shutdown(cx))
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use futures::task::noop_waker;
175
176 #[crate::test]
177 fn ready_io_does_not_create_a_timer() {
178 let waker = noop_waker();
179 let mut cx = Context::from_waker(&waker);
180 let mut reader = TimedReader::new(tokio::io::empty(), Duration::from_secs(1));
181 let mut bytes = [0; 8];
182 let mut buf = ReadBuf::new(&mut bytes);
183 assert!(
184 Pin::new(&mut reader)
185 .poll_read(&mut cx, &mut buf)
186 .is_ready()
187 );
188 assert_eq!(buf.filled().len(), 0);
189 assert!(reader.timeout.sleep.is_none());
190 let mut writer = TimedWriter::new(tokio::io::sink(), Duration::from_secs(1));
191 assert!(matches!(
192 Pin::new(&mut writer).poll_write(&mut cx, b"hello"),
193 Poll::Ready(Ok(5))
194 ));
195 assert!(writer.timeout.sleep.is_none());
196 assert!(matches!(
197 Pin::new(&mut writer).poll_flush(&mut cx),
198 Poll::Ready(Ok(()))
199 ));
200 assert!(writer.timeout.sleep.is_none());
201 assert!(matches!(
202 Pin::new(&mut writer).poll_shutdown(&mut cx),
203 Poll::Ready(Ok(()))
204 ));
205 assert!(writer.timeout.sleep.is_none());
206 }
207
208 #[crate::test(tokio::test(start_paused = true))]
209 async fn pending_repoll_keeps_deadline_and_ready_io_wins_over_expired_timer() {
210 let waker = noop_waker();
211 let mut cx = Context::from_waker(&waker);
212 let mut timeout = IoTimeout::new(Duration::from_millis(10));
213 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
214 let deadline = timeout.deadline.unwrap();
215 tokio::time::advance(Duration::from_millis(5)).await;
216 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
217 assert_eq!(timeout.deadline, Some(deadline));
218 tokio::time::advance(Duration::from_millis(6)).await;
219 assert!(matches!(
220 timeout.poll(&mut cx, |_| Poll::Ready(Ok(42))),
221 Poll::Ready(Ok(42))
222 ));
223 assert!(timeout.deadline.is_none());
224 }
225
226 #[crate::test(tokio::test(start_paused = true))]
227 async fn read_returns_data_that_arrived_before_a_late_poll() {
228 let waker = noop_waker();
229 let mut cx = Context::from_waker(&waker);
230 let mut reader = TimedReader::new(ScriptedIo { ready: false }, Duration::from_millis(10));
231 let mut bytes = [0; 8];
232 let mut buf = ReadBuf::new(&mut bytes);
233 assert!(
234 Pin::new(&mut reader)
235 .poll_read(&mut cx, &mut buf)
236 .is_pending()
237 );
238 reader.reader.ready = true;
241 tokio::time::advance(Duration::from_millis(20)).await;
242 assert!(matches!(
243 Pin::new(&mut reader).poll_read(&mut cx, &mut buf),
244 Poll::Ready(Ok(()))
245 ));
246 assert_eq!(buf.filled(), b"x");
247 assert!(reader.timeout.deadline.is_none());
248 }
249
250 #[crate::test(tokio::test(start_paused = true))]
251 async fn completion_and_error_clear_deadline_and_next_operation_reuses_timer() {
252 let waker = noop_waker();
253 let mut cx = Context::from_waker(&waker);
254 let mut timeout = IoTimeout::new(Duration::from_secs(1));
255 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
256 let first_deadline = timeout.deadline.unwrap();
257 let sleep: *const Sleep = &**timeout.sleep.as_ref().unwrap();
258 tokio::time::advance(Duration::from_millis(20)).await;
259 assert!(matches!(
260 timeout.poll(&mut cx, |_| Poll::Ready(Ok(42))),
261 Poll::Ready(Ok(42))
262 ));
263 assert!(timeout.deadline.is_none());
264 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
265 assert!(timeout.deadline.unwrap() > first_deadline);
266 let reused = timeout.sleep.as_ref().unwrap();
267 assert!(std::ptr::eq(&**reused, sleep), "timer must be reused");
268 assert_eq!(
269 reused.deadline(),
270 first_deadline,
271 "unfired timer must not be reset"
272 );
273 let error = timeout.poll::<()>(&mut cx, |_| {
274 Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()))
275 });
276 assert!(matches!(error, Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::BrokenPipe));
277 assert!(timeout.deadline.is_none());
278 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
279 drop(timeout);
280 tokio::time::advance(Duration::from_secs(2)).await;
281 }
282
283 #[crate::test(tokio::test(start_paused = true))]
284 async fn deadline_starts_before_first_io_poll() {
285 let waker = noop_waker();
286 let mut cx = Context::from_waker(&waker);
287 let mut timeout = IoTimeout::new(Duration::from_secs(1));
288 let start = Instant::now();
289 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
290 assert_eq!(timeout.deadline, Some(start + Duration::from_secs(1)));
291 assert_eq!(
292 timeout.sleep.as_ref().unwrap().deadline(),
293 start + Duration::from_secs(1)
294 );
295 }
296
297 #[crate::test(tokio::test(start_paused = true))]
298 async fn zero_timeout_completes_ready_io_and_times_out_pending_io() {
299 let waker = noop_waker();
300 let mut cx = Context::from_waker(&waker);
301 let mut timeout = IoTimeout::new(Duration::ZERO);
302 assert!(matches!(
303 timeout.poll(&mut cx, |_| Poll::Ready(Ok(()))),
304 Poll::Ready(Ok(()))
305 ));
306 assert!(timeout.deadline.is_none());
307 let result = timeout.poll::<()>(&mut cx, |_| Poll::Pending);
308 assert!(matches!(result, Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::TimedOut));
309 assert!(timeout.deadline.is_none());
310 }
311
312 #[crate::test(tokio::test(start_paused = true))]
313 async fn overflowing_duration_never_times_out() {
314 let waker = noop_waker();
315 let mut cx = Context::from_waker(&waker);
316 let mut timeout = IoTimeout::new(Duration::MAX);
317 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
318 assert!(matches!(
319 timeout.poll(&mut cx, |_| Poll::Ready(Ok(()))),
320 Poll::Ready(Ok(()))
321 ));
322 assert!(timeout.sleep.is_none());
323 }
324
325 struct ScriptedIo {
326 ready: bool,
327 }
328
329 impl AsyncRead for ScriptedIo {
330 fn poll_read(
331 self: Pin<&mut Self>,
332 _: &mut Context<'_>,
333 buf: &mut ReadBuf<'_>,
334 ) -> Poll<io::Result<()>> {
335 if self.ready {
336 buf.put_slice(b"x");
337 Poll::Ready(Ok(()))
338 } else {
339 Poll::Pending
340 }
341 }
342 }
343
344 impl AsyncWrite for ScriptedIo {
345 fn poll_write(
346 self: Pin<&mut Self>,
347 _: &mut Context<'_>,
348 buf: &[u8],
349 ) -> Poll<io::Result<usize>> {
350 if self.ready {
351 Poll::Ready(Ok(buf.len()))
352 } else {
353 Poll::Pending
354 }
355 }
356
357 fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
358 if self.ready {
359 Poll::Ready(Ok(()))
360 } else {
361 Poll::Pending
362 }
363 }
364
365 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
366 self.poll_flush(cx)
367 }
368 }
369
370 #[crate::test(tokio::test(start_paused = true))]
371 async fn pending_read_completes_before_deadline_and_clears_deadline() {
372 let waker = noop_waker();
373 let mut cx = Context::from_waker(&waker);
374 let mut reader = TimedReader::new(ScriptedIo { ready: false }, Duration::from_millis(10));
375 let mut bytes = [0; 8];
376 let mut buf = ReadBuf::new(&mut bytes);
377 assert!(
378 Pin::new(&mut reader)
379 .poll_read(&mut cx, &mut buf)
380 .is_pending()
381 );
382 tokio::time::advance(Duration::from_millis(5)).await;
383 reader.reader.ready = true;
384 assert!(matches!(
385 Pin::new(&mut reader).poll_read(&mut cx, &mut buf),
386 Poll::Ready(Ok(()))
387 ));
388 assert_eq!(buf.filled(), b"x");
389 assert!(reader.timeout.deadline.is_none());
390 }
391
392 #[crate::test(tokio::test(start_paused = true))]
393 async fn timeout_wakes_pending_io_without_an_io_wakeup() {
394 use tokio::io::AsyncReadExt;
395
396 let mut reader = TimedReader::new(ScriptedIo { ready: false }, Duration::from_millis(10));
397 let start = Instant::now();
398 let mut bytes = [0; 8];
399 let error = reader.read(&mut bytes).await.unwrap_err();
400 assert_eq!(error.kind(), io::ErrorKind::TimedOut);
401 assert!(Instant::now() >= start + Duration::from_millis(10));
402 assert!(reader.timeout.deadline.is_none());
403 }
404
405 #[crate::test]
406 fn immediate_error_does_not_create_a_timer() {
407 let waker = noop_waker();
408 let mut cx = Context::from_waker(&waker);
409 let mut timeout = IoTimeout::new(Duration::from_secs(1));
410 let result = timeout.poll::<()>(&mut cx, |_| {
411 Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()))
412 });
413 assert!(matches!(result, Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::BrokenPipe));
414 assert!(timeout.sleep.is_none());
415 }
416
417 #[crate::test(tokio::test(start_paused = true))]
418 async fn writer_transitions_preserve_pending_deadline_and_reset_on_completion() {
419 let waker = noop_waker();
420 let mut cx = Context::from_waker(&waker);
421 let mut writer = TimedWriter::new(ScriptedIo { ready: false }, Duration::from_millis(10));
422 assert!(
423 Pin::new(&mut writer)
424 .poll_write(&mut cx, b"hello")
425 .is_pending()
426 );
427 let deadline = writer.timeout.deadline.unwrap();
428 tokio::time::advance(Duration::from_millis(5)).await;
429 assert!(Pin::new(&mut writer).poll_flush(&mut cx).is_pending());
430 assert_eq!(writer.timeout.deadline, Some(deadline));
431 writer.writer.ready = true;
432 assert!(matches!(
433 Pin::new(&mut writer).poll_flush(&mut cx),
434 Poll::Ready(Ok(()))
435 ));
436 assert!(writer.timeout.deadline.is_none());
437 writer.writer.ready = false;
438 assert!(Pin::new(&mut writer).poll_shutdown(&mut cx).is_pending());
439 assert!(writer.timeout.deadline.unwrap() > deadline);
440 tokio::time::advance(Duration::from_millis(11)).await;
441 let result = Pin::new(&mut writer).poll_shutdown(&mut cx);
442 assert!(matches!(result, Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::TimedOut));
443 assert!(writer.timeout.deadline.is_none());
444 assert!(Pin::new(&mut writer).poll_write(&mut cx, b"x").is_pending());
445 tokio::time::advance(Duration::from_millis(11)).await;
446 writer.writer.ready = true;
447 assert!(matches!(
448 Pin::new(&mut writer).poll_write(&mut cx, b"x"),
449 Poll::Ready(Ok(1))
450 ));
451 assert!(writer.timeout.deadline.is_none());
452 }
453
454 #[crate::test(tokio::test(start_paused = true))]
455 async fn early_timer_fire_rearms_to_current_deadline() {
456 let waker = noop_waker();
457 let mut cx = Context::from_waker(&waker);
458 let mut timeout = IoTimeout::new(Duration::from_millis(10));
459 let start = Instant::now();
460 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
461 tokio::time::advance(Duration::from_millis(5)).await;
462 assert!(timeout.poll(&mut cx, |_| Poll::Ready(Ok(()))).is_ready());
463 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
464 let deadline = timeout.deadline.unwrap();
465 assert_eq!(deadline, start + Duration::from_millis(15));
466 tokio::time::advance(Duration::from_millis(6)).await;
467 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
468 assert_eq!(timeout.sleep.as_ref().unwrap().deadline(), deadline);
469 tokio::time::advance(Duration::from_millis(4)).await;
470 let result = timeout.poll::<()>(&mut cx, |_| Poll::Pending);
471 assert!(matches!(result, Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::TimedOut));
472 }
473
474 #[crate::test(tokio::test(start_paused = true))]
475 async fn early_timer_fire_wakes_task_until_current_deadline() {
476 let mut timeout = IoTimeout::new(Duration::from_millis(10));
477 let start = Instant::now();
478 let waker = noop_waker();
479 let mut cx = Context::from_waker(&waker);
480 assert!(timeout.poll::<()>(&mut cx, |_| Poll::Pending).is_pending());
481 tokio::time::advance(Duration::from_millis(5)).await;
482 assert!(timeout.poll(&mut cx, |_| Poll::Ready(Ok(()))).is_ready());
483 let error = std::future::poll_fn(|cx| timeout.poll::<()>(cx, |_| Poll::Pending))
484 .await
485 .unwrap_err();
486 assert_eq!(error.kind(), io::ErrorKind::TimedOut);
487 assert!(Instant::now() >= start + Duration::from_millis(15));
488 }
489}