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}