timeout_readwrite/writer.rs
1// Copyright 2017 Jonathan Creekmore
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use nix::libc::c_int;
10use nix::poll::PollFlags;
11use std::io::Result;
12use std::io::Seek;
13use std::io::SeekFrom;
14use std::io::Write;
15use std::os::fd::AsFd;
16use std::os::fd::BorrowedFd;
17use std::time::Duration;
18
19use super::utils;
20
21/// The `TimeoutWriter` struct adds write timeouts to any writer.
22///
23/// The `write` call on a `Write` instance can block forever until data cannot be written.
24/// A `TimeoutWriter` will wait until data can be written, up until an optional timeout,
25/// before actually performing the `write` operation on the underlying writer.
26///
27/// If any `Write` operation times out, the method called will return
28/// an `io::ErrorKind::TimedOut` variant as the value of `io::Error`. All other
29/// error values that would normally be produced by the underlying implementation
30/// of the `Write` trait could also be produced by the `TimeoutWriter`.
31pub struct TimeoutWriter<H>
32where
33 H: Write + AsFd,
34{
35 timeout: Option<c_int>,
36 handle: H,
37}
38
39impl<H> Write for TimeoutWriter<H>
40where
41 H: Write + AsFd,
42{
43 fn write(&mut self, buf: &[u8]) -> Result<usize> {
44 utils::wait_until_ready(self.timeout, &self.handle, PollFlags::POLLOUT)?;
45 self.handle.write(buf)
46 }
47
48 fn flush(&mut self) -> Result<()> {
49 utils::wait_until_ready(self.timeout, &self.handle, PollFlags::POLLOUT)?;
50 self.handle.flush()
51 }
52}
53
54impl<H> Seek for TimeoutWriter<H>
55where
56 H: Write + AsFd + Seek,
57{
58 fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
59 self.handle.seek(pos)
60 }
61}
62
63impl<H> AsFd for TimeoutWriter<H>
64where
65 H: Write + AsFd,
66{
67 fn as_fd(&self) -> BorrowedFd<'_> {
68 self.handle.as_fd()
69 }
70}
71
72impl<H> Clone for TimeoutWriter<H>
73where
74 H: Write + AsFd + Clone,
75{
76 fn clone(&self) -> TimeoutWriter<H> {
77 TimeoutWriter {
78 handle: self.handle.clone(),
79 ..*self
80 }
81 }
82}
83
84impl<H> TimeoutWriter<H>
85where
86 H: Write + AsFd,
87{
88 /// Create a new `TimeoutWriter` with an optional timeout.
89 ///
90 /// # Examples
91 ///
92 /// This first example creates the `TimeoutWriter` with a 5-second timeout.
93 ///
94 /// ```
95 /// use timeout_readwrite::TimeoutWriter;
96 /// use std::fs::File;
97 /// use std::time::Duration;
98 ///
99 /// # fn foo() -> std::io::Result<()> {
100 /// let mut f = File::open("file.txt")?;
101 /// let mut wtr = TimeoutWriter::new(f, Duration::new(5, 0));
102 /// # Ok(())
103 /// # }
104 /// ```
105 ///
106 /// This example creates the `TimeoutWriter` without a timeout at all.
107 ///
108 /// ```
109 /// use timeout_readwrite::TimeoutWriter;
110 /// use std::fs::File;
111 /// use std::time::Duration;
112 ///
113 /// # fn foo() -> std::io::Result<()> {
114 /// let mut f = File::open("file.txt")?;
115 /// let mut wtr = TimeoutWriter::new(f, None);
116 /// # Ok(())
117 /// # }
118 /// ```
119 pub fn new<T: Into<Option<Duration>>>(handle: H, timeout: T) -> TimeoutWriter<H> {
120 TimeoutWriter {
121 timeout: timeout.into().map(utils::duration_to_ms),
122 handle,
123 }
124 }
125}
126
127pub trait TimeoutWriteExt<H>
128where
129 H: Write + AsFd,
130{
131 fn with_timeout<T: Into<Option<Duration>>>(self, timeout: T) -> TimeoutWriter<H>;
132}
133
134impl<H> TimeoutWriteExt<H> for H
135where
136 H: Write + AsFd,
137{
138 fn with_timeout<T: Into<Option<Duration>>>(self, timeout: T) -> TimeoutWriter<H> {
139 TimeoutWriter::new(self, timeout)
140 }
141}