New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
io: reintroduce vectored writes #3149
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
use std::io; | ||
use std::io::{self, IoSlice}; | ||
use std::ops::DerefMut; | ||
use std::pin::Pin; | ||
use std::task::{Context, Poll}; | ||
|
@@ -127,6 +127,51 @@ pub trait AsyncWrite { | |
/// This function will panic if not called within the context of a future's | ||
/// task. | ||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>>; | ||
|
||
/// Like [`poll_write`], except that it writes from a slice of buffers. | ||
/// | ||
/// Data is copied from each buffer in order, with the final buffer | ||
/// read from possibly being only partially consumed. This method must | ||
/// behave as a call to [`write`] with the buffers concatenated would. | ||
/// | ||
/// The default implementation calls [`poll_write`] with either the first nonempty | ||
/// buffer provided, or an empty one if none exists. | ||
Comment on lines
+137
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should there be a note that the default implementation is less efficient than just using |
||
/// | ||
/// Attempt to write bytes from `buf` into the object. | ||
/// | ||
/// On success, returns `Poll::Ready(Ok(num_bytes_written))`. | ||
/// | ||
/// If the object is not ready for writing, the method returns | ||
/// `Poll::Pending` and arranges for the current task (via | ||
/// `cx.waker()`) to receive a notification when the object becomes | ||
/// writable or is closed. | ||
/// | ||
/// [`poll_write`]: AsyncWrite::poll_write | ||
fn poll_write_vectored( | ||
self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
bufs: &[IoSlice<'_>], | ||
) -> Poll<Result<usize, io::Error>> { | ||
let buf = bufs | ||
.iter() | ||
.find(|b| !b.is_empty()) | ||
.map_or(&[][..], |b| &**b); | ||
self.poll_write(cx, buf) | ||
seanmonstar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/// Determines if this writer has an efficient [`poll_write_vectored`] | ||
/// implementation. | ||
/// | ||
/// If a writer does not override the default [`poll_write_vectored`] | ||
/// implementation, code using it may want to avoid the method all together | ||
/// and coalesce writes into a single buffer for higher performance. | ||
/// | ||
/// The default implementation returns `false`. | ||
/// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it may be worth documenting a contract around whether or not the return value from a given instance of a type implementing |
||
/// [`poll_write_vectored`]: AsyncWrite::poll_write_vectored | ||
fn is_write_vectored(&self) -> bool { | ||
false | ||
} | ||
} | ||
|
||
macro_rules! deref_async_write { | ||
|
@@ -139,6 +184,18 @@ macro_rules! deref_async_write { | |
Pin::new(&mut **self).poll_write(cx, buf) | ||
} | ||
|
||
fn poll_write_vectored( | ||
mut self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
bufs: &[IoSlice<'_>], | ||
) -> Poll<io::Result<usize>> { | ||
Pin::new(&mut **self).poll_write_vectored(cx, bufs) | ||
} | ||
|
||
fn is_write_vectored(&self) -> bool { | ||
(**self).is_write_vectored() | ||
} | ||
|
||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
Pin::new(&mut **self).poll_flush(cx) | ||
} | ||
|
@@ -170,6 +227,18 @@ where | |
self.get_mut().as_mut().poll_write(cx, buf) | ||
} | ||
|
||
fn poll_write_vectored( | ||
self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
bufs: &[IoSlice<'_>], | ||
) -> Poll<io::Result<usize>> { | ||
self.get_mut().as_mut().poll_write_vectored(cx, bufs) | ||
} | ||
|
||
fn is_write_vectored(&self) -> bool { | ||
(**self).is_write_vectored() | ||
} | ||
|
||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
self.get_mut().as_mut().poll_flush(cx) | ||
} | ||
|
@@ -189,6 +258,18 @@ impl AsyncWrite for Vec<u8> { | |
Poll::Ready(Ok(buf.len())) | ||
} | ||
|
||
fn poll_write_vectored( | ||
mut self: Pin<&mut Self>, | ||
_: &mut Context<'_>, | ||
bufs: &[IoSlice<'_>], | ||
) -> Poll<io::Result<usize>> { | ||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs)) | ||
} | ||
|
||
fn is_write_vectored(&self) -> bool { | ||
true | ||
} | ||
|
||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
Poll::Ready(Ok(())) | ||
} | ||
|
@@ -207,6 +288,18 @@ impl AsyncWrite for io::Cursor<&mut [u8]> { | |
Poll::Ready(io::Write::write(&mut *self, buf)) | ||
} | ||
|
||
fn poll_write_vectored( | ||
mut self: Pin<&mut Self>, | ||
_: &mut Context<'_>, | ||
bufs: &[IoSlice<'_>], | ||
) -> Poll<io::Result<usize>> { | ||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs)) | ||
} | ||
|
||
fn is_write_vectored(&self) -> bool { | ||
true | ||
} | ||
|
||
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
Poll::Ready(io::Write::flush(&mut *self)) | ||
} | ||
|
@@ -225,6 +318,18 @@ impl AsyncWrite for io::Cursor<&mut Vec<u8>> { | |
Poll::Ready(io::Write::write(&mut *self, buf)) | ||
} | ||
|
||
fn poll_write_vectored( | ||
mut self: Pin<&mut Self>, | ||
_: &mut Context<'_>, | ||
bufs: &[IoSlice<'_>], | ||
) -> Poll<io::Result<usize>> { | ||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs)) | ||
} | ||
|
||
fn is_write_vectored(&self) -> bool { | ||
true | ||
} | ||
|
||
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
Poll::Ready(io::Write::flush(&mut *self)) | ||
} | ||
|
@@ -243,6 +348,18 @@ impl AsyncWrite for io::Cursor<Vec<u8>> { | |
Poll::Ready(io::Write::write(&mut *self, buf)) | ||
} | ||
|
||
fn poll_write_vectored( | ||
mut self: Pin<&mut Self>, | ||
_: &mut Context<'_>, | ||
bufs: &[IoSlice<'_>], | ||
) -> Poll<io::Result<usize>> { | ||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs)) | ||
} | ||
|
||
fn is_write_vectored(&self) -> bool { | ||
true | ||
} | ||
|
||
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
Poll::Ready(io::Write::flush(&mut *self)) | ||
} | ||
|
@@ -261,6 +378,18 @@ impl AsyncWrite for io::Cursor<Box<[u8]>> { | |
Poll::Ready(io::Write::write(&mut *self, buf)) | ||
} | ||
|
||
fn poll_write_vectored( | ||
mut self: Pin<&mut Self>, | ||
_: &mut Context<'_>, | ||
bufs: &[IoSlice<'_>], | ||
) -> Poll<io::Result<usize>> { | ||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs)) | ||
} | ||
|
||
fn is_write_vectored(&self) -> bool { | ||
true | ||
} | ||
|
||
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> { | ||
Poll::Ready(io::Write::flush(&mut *self)) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it might be worth saying something about how this is generally expected to use platform vectored write APIs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took the documentation for these 2 new methods directly from the standard library. I kind of like that they don't mention platform gather IO, and just leave that up to the implementor. 🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, that's fair!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is worth saying that, if any data is written, the function returns
Ok(n)
. I.e. an error / pending means no data was written.