Skip to main content

tokio_util/io/
write_all_vectored.rs

1use tokio::io::AsyncWrite;
2
3use pin_project_lite::pin_project;
4use std::marker::PhantomPinned;
5use std::pin::Pin;
6use std::task::{ready, Context, Poll};
7use std::{future::Future, io::IoSlice};
8use std::{io, mem};
9
10pin_project! {
11    /// A future that writes all data from multiple buffers to a writer.
12    #[derive(Debug)]
13    #[must_use = "futures do nothing unless you `.await` or poll them"]
14    pub struct WriteAllVectored<'a, 'b, W: ?Sized> {
15        writer: &'a mut W,
16        bufs: &'a mut [IoSlice<'b>],
17        // Make this future `!Unpin` for compatibility with async trait methods.
18        #[pin]
19        _pin: PhantomPinned,
20    }
21}
22/// Like [`write_all`] but writes all data from multiple buffers into this writer.
23///
24/// This function writes multiple (possibly non-contiguous) buffers into the writer,
25/// using the `writev` syscall to potentially write in a single system call.
26///
27/// Interrupted writes are retried. Other errors are returned immediately.
28///
29/// Equivalent to:
30///
31/// ```ignore
32/// async fn write_all_vectored<W: AsyncWrite + Unpin + ?Sized>(
33///     writer: &mut W,
34///     mut bufs: &mut [IoSlice<'_>]
35/// ) -> io::Result<()> {
36///     while !bufs.is_empty() {
37///         let n = match write_vectored(writer, bufs).await {
38///             Ok(n) => n,
39///             Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
40///             Err(e) => return Err(e),
41///         };
42///         if n == 0 {
43///             return Err(io::ErrorKind::WriteZero.into());
44///         }
45///         IoSlice::advance_slices(&mut bufs, n);
46///     }
47///     Ok(())
48/// }
49/// ```
50///
51/// # Cancel safety
52///
53/// This method is not cancellation safe. If it is used as the event
54/// in a `tokio::select!` statement and some other
55/// branch completes first, then the provided buffer may have been
56/// partially written, but future calls to `write_all_vectored` will
57/// have lost its place in the buffer.
58///
59/// # Examples
60///
61/// ```rust
62/// use tokio_util::io::write_all_vectored;
63/// use std::io::IoSlice;
64///
65/// #[tokio::main(flavor = "current_thread")]
66/// async fn main() -> std::io::Result<()> {
67///
68///     let mut writer = Vec::new();
69///     let bufs = &mut [
70///         IoSlice::new(&[1]),
71///         IoSlice::new(&[2, 3]),
72///         IoSlice::new(&[4, 5, 6]),
73///     ];
74///
75///     write_all_vectored(&mut writer, bufs).await?;
76///
77///     // Note: `bufs` has been modified by `IoSlice::advance_slices` and should not be reused.
78///     assert_eq!(writer, &[1, 2, 3, 4, 5, 6]);
79///     Ok(())
80/// }
81/// ```
82///
83/// # Notes
84///
85/// See the documentation for [`Write::write_all_vectored`] from std.
86/// After calling this function, the buffer slices may have
87/// been advanced and should not be reused.
88///
89/// [`Write::write_all_vectored`]: std::io::Write::write_all_vectored
90/// [`write_all`]: tokio::io::AsyncWriteExt::write_all
91/// [`writev`]: https://man7.org/linux/man-pages/man3/writev.3p.html
92pub fn write_all_vectored<'a, 'b, W>(
93    writer: &'a mut W,
94    bufs: &'a mut [IoSlice<'b>],
95) -> WriteAllVectored<'a, 'b, W>
96where
97    W: AsyncWrite + Unpin + ?Sized,
98{
99    WriteAllVectored {
100        writer,
101        bufs,
102        _pin: PhantomPinned,
103    }
104}
105
106impl<W> Future for WriteAllVectored<'_, '_, W>
107where
108    W: AsyncWrite + Unpin + ?Sized,
109{
110    type Output = io::Result<()>;
111
112    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
113        let me = self.project();
114        while !me.bufs.is_empty() {
115            // advance to first non-empty buffer
116            let non_empty = match me.bufs.iter().position(|b| !b.is_empty()) {
117                Some(pos) => pos,
118                None => return Poll::Ready(Ok(())),
119            };
120
121            // drop empty buffers at the start
122            *me.bufs = &mut mem::take(me.bufs)[non_empty..];
123
124            let n = match ready!(Pin::new(&mut *me.writer).poll_write_vectored(cx, me.bufs)) {
125                Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
126                res => res?,
127            };
128            if n == 0 {
129                return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
130            }
131            self::advance_slices(me.bufs, n);
132        }
133
134        Poll::Ready(Ok(()))
135    }
136}
137
138// copied from `std::IoSlice::advance_slices`
139// replace with method when MSRV is 1.81.0
140fn advance_slices<'a>(bufs: &mut &mut [IoSlice<'a>], n: usize) {
141    // Number of buffers to remove.
142    let mut remove = 0;
143    // Remaining length before reaching n. This prevents overflow
144    // that could happen if the length of slices in `bufs` were instead
145    // accumulated. Those slice may be aliased and, if they are large
146    // enough, their added length may overflow a `usize`.
147    let mut left = n;
148    for buf in bufs.iter() {
149        if let Some(remainder) = left.checked_sub(buf.len()) {
150            left = remainder;
151            remove += 1;
152        } else {
153            break;
154        }
155    }
156
157    *bufs = &mut std::mem::take(bufs)[remove..];
158    if let Some(first) = bufs.first_mut() {
159        let buf = &first[left..];
160        // Necessary due to a limitation in the borrow checker,
161        // when tokio MSRV reaches 1.81.0 this entire function
162        // can be replaced with `IoSlice::advance_slices`
163        //
164        // SAFETY: transmute a sub-slice of an IoSlice<'a> back to
165        // the lifetime `'a`. This is safe because the underlying memory
166        // is guaranteed to live for 'a, we have shared access, and no
167        // underlying data is reinterpreted to a different type.
168        unsafe {
169            *first = IoSlice::new(std::mem::transmute::<&[u8], &'a [u8]>(buf));
170        }
171    } else {
172        assert!(left == 0, "advancing io slices beyond their length");
173    }
174}