-
Notifications
You must be signed in to change notification settings - Fork 626
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
Add futures-io #780
Add futures-io #780
Changes from all 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 |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "futures-io" | ||
version = "0.2.0" | ||
authors = ["Alex Crichton <alex@alexcrichton.com>"] | ||
license = "MIT/Apache-2.0" | ||
repository = "https://github.com/alexcrichton/futures-rs" | ||
homepage = "https://github.com/alexcrichton/futures-rs" | ||
documentation = "https://docs.rs/futures-io" | ||
description = """ | ||
The `AsyncRead` and `AsyncWrite` traits for the futures-rs library. | ||
""" | ||
|
||
[features] | ||
std = ["futures-core/std", "iovec"] | ||
default = ["std"] | ||
|
||
[dependencies] | ||
futures-core = { path = "../futures-core", version = "0.2.0", default-features = false } | ||
iovec = { version = "0.1", optional = true } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
//! Asynchronous IO | ||
//! | ||
//! This crate contains the `AsyncRead` and `AsyncWrite` traits which allow | ||
//! data to be read and written asynchronously. | ||
|
||
#![no_std] | ||
#![deny(missing_docs, missing_debug_implementations)] | ||
#![doc(html_root_url = "https://docs.rs/futures-io/0.2")] | ||
|
||
macro_rules! if_std { | ||
($($i:item)*) => ($( | ||
#[cfg(feature = "std")] | ||
$i | ||
)*) | ||
} | ||
|
||
if_std! { | ||
extern crate futures_core; | ||
extern crate iovec; | ||
extern crate std; | ||
|
||
use futures_core::{Async, Poll, task}; | ||
use std::boxed::Box; | ||
use std::io as StdIo; | ||
use std::ptr; | ||
use std::vec::Vec; | ||
|
||
// Re-export IoVec for convenience | ||
pub use iovec::IoVec; | ||
|
||
// Re-export io::Error so that users don't have to deal | ||
// with conflicts when `use`ing `futures::io` and `std::io`. | ||
pub use StdIo::Error as Error; | ||
|
||
/// A type used to conditionally initialize buffers passed to `AsyncRead` | ||
/// methods. | ||
#[derive(Debug)] | ||
pub struct Initializer(bool); | ||
|
||
impl Initializer { | ||
/// Returns a new `Initializer` which will zero out buffers. | ||
#[inline] | ||
pub fn zeroing() -> Initializer { | ||
Initializer(true) | ||
} | ||
|
||
/// Returns a new `Initializer` which will not zero out buffers. | ||
/// | ||
/// # Safety | ||
/// | ||
/// This method may only be called by `AsyncRead`ers which guarantee | ||
/// that they will not read from the buffers passed to `AsyncRead` | ||
/// methods, and that the return value of the method accurately reflects | ||
/// the number of bytes that have been written to the head of the buffer. | ||
#[inline] | ||
pub unsafe fn nop() -> Initializer { | ||
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. What is 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. This is done to match the current (unstable) std API. When that API is released, this 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. The name |
||
Initializer(false) | ||
} | ||
|
||
/// Indicates if a buffer should be initialized. | ||
#[inline] | ||
pub fn should_initialize(&self) -> bool { | ||
self.0 | ||
} | ||
|
||
/// Initializes a buffer if necessary. | ||
#[inline] | ||
pub fn initialize(&self, buf: &mut [u8]) { | ||
if self.should_initialize() { | ||
unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } | ||
} | ||
} | ||
} | ||
|
||
/// Objects which can be read asynchronously. | ||
pub trait AsyncRead { | ||
/// Determines if this `AsyncRead`er can work with buffers of | ||
/// uninitialized memory. | ||
/// | ||
/// The default implementation returns an initializer which will zero | ||
/// buffers. | ||
/// | ||
/// # Safety | ||
/// | ||
/// This method is `unsafe` because and `AsyncRead`er could otherwise | ||
/// return a non-zeroing `Initializer` from another `AsyncRead` type | ||
/// without an `unsafe` block. | ||
#[inline] | ||
unsafe fn initializer(&self) -> Initializer { | ||
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. I thought the point of the 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.
This function is made All of this is copied from the std::io API. If you have comments, questions, or ideas about how to improve this design, please leave them in the tracking issue. If that API changes, we will update this one as necessary. 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. Quoting from the 0.2 RFC:
The implementation doesn't seem to match, since even if I'm leaving comments here because this API is being proposed in futures, a library that I rely on, and a place where stability markers cannot protect the it. The tracking issue in std hasn't seen much traction otherwise. 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. This PR does indeed address the issue I raised in the text you quoted. However, I think the wording I used may have been confusing. Currently, This PR fixes this problem by moving the unsafety to the call to 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.
I assume you mean that is the intent, since it definitely is unsafe to call the function with the current compilers. I find the reasoning that you could get an 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. Consider this implementation: struct MyUnsafePerformantRead;
impl AsyncRead for MyUnsafePerformantRead {
fn initializer(&self) -> Initializer {
unsafe { Initializer::nop() }
}
fn poll_read.... // Doesn't read from the buffer
}
struct MySafeButStillUninitRead;
impl AsyncRead for MySafeButStillUninitRead {
fn initializer&self) -> Initializer {
MyUnsafePerformantRead.initializer()
}
fn poll_read.... // Does read from the buffer (uninitialized memory), causes UB
} 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. Yes, I imagined that is the concern, but I don't see anything that adding an 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.
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. I'm even more unconvinced now, since you can easily grab an As a not-arbitrary example, consider I think the std API gives more an illusion of protection against UB, while still quite easily allowing it to happen, even by mistake. 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.
Not without using |
||
Initializer::zeroing() | ||
} | ||
|
||
/// Attempt to read from the `AsyncRead` into `buf`. | ||
/// | ||
/// On success, returns `Ok(Async::Ready(num_bytes_read))`. | ||
/// | ||
/// If reading would block, this function returns `Ok(Async::Pending)` | ||
/// and arranges for `cx.waker()` to receive a notification when the | ||
/// object becomes readable or is closed. | ||
fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) | ||
-> Poll<usize, Error>; | ||
|
||
/// Attempt to read from the `AsyncRead` into `vec` using vectored | ||
/// IO operations. This allows data to be read into multiple buffers | ||
/// using a single operation. | ||
/// | ||
/// On success, returns `Ok(Async::Ready(num_bytes_read))`. | ||
/// | ||
/// By default, this method delegates to using `poll_read` on the first | ||
/// buffer in `vec`. Objects which support vectored IO should override | ||
/// this method. | ||
/// | ||
/// If reading would block, this function returns `Ok(Async::Pending)` | ||
/// and arranges for `cx.waker()` to receive a notification when the | ||
/// object becomes readable or is closed. | ||
fn poll_vectored_read(&mut self, cx: &mut task::Context, vec: &mut [&mut IoVec]) | ||
-> Poll<usize, Error> | ||
{ | ||
if let Some(ref mut first_iovec) = vec.get_mut(0) { | ||
self.poll_read(cx, first_iovec) | ||
} else { | ||
// `vec` is empty. | ||
return Ok(Async::Ready(0)); | ||
} | ||
} | ||
} | ||
|
||
/// Objects which can be written to asynchronously. | ||
pub trait AsyncWrite { | ||
/// Attempt to write bytes from `buf` into the object. | ||
/// | ||
/// On success, returns `Ok(Async::Ready(num_bytes_written))`. | ||
/// | ||
/// If writing would block, this function returns `Ok(Async::Pending)` | ||
/// and arranges for `cx.waker()` to receive a notification when the | ||
/// the object becomes writable or is closed. | ||
fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) | ||
-> Poll<usize, Error>; | ||
|
||
/// Attempt to write bytes from `vec` into the object using vectored | ||
/// IO operations. This allows data from multiple buffers to be written | ||
/// using a single operation. | ||
/// | ||
/// On success, returns `Ok(Async::Ready(num_bytes_written))`. | ||
/// | ||
/// By default, this method delegates to using `poll_write` on the first | ||
/// buffer in `vec`. Objects which support vectored IO should override | ||
/// this method. | ||
/// | ||
/// If writing would block, this function returns `Ok(Async::Pending)` | ||
/// and arranges for `cx.waker()` to receive a notification when the | ||
/// object becomes writable or is closed. | ||
fn poll_vectored_write(&mut self, cx: &mut task::Context, vec: &[&IoVec]) | ||
-> Poll<usize, Error> | ||
{ | ||
if let Some(ref first_iovec) = vec.get(0) { | ||
self.poll_write(cx, &*first_iovec) | ||
} else { | ||
// `vec` is empty. | ||
return Ok(Async::Ready(0)); | ||
} | ||
} | ||
|
||
/// Attempt to flush the object, ensuring that all intermediately | ||
/// buffered contents reach their destination. | ||
/// | ||
/// On success, returns `Ok(Async::Ready(()))`. | ||
/// | ||
/// If flushing is incomplete, this function returns `Ok(Async::Pending)` | ||
/// and arranges for `cx.waker()` to receive a notification when the | ||
/// object can make progress towards flushing. | ||
fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), Error>; | ||
|
||
/// Attempt to close the object. | ||
/// | ||
/// On success, returns `Ok(Async::Ready(()))`. | ||
/// | ||
/// If closing is incomplete, this function returns `Ok(Async::Pending)` | ||
/// and arranges for `cx.waker()` to receive a notification when the | ||
/// object can make progress towards closing. | ||
fn poll_close(&mut self, cx: &mut task::Context) -> Poll<(), Error>; | ||
} | ||
|
||
macro_rules! deref_async_read { | ||
() => { | ||
unsafe fn initializer(&self) -> Initializer { | ||
(**self).initializer() | ||
} | ||
|
||
fn poll_read(&mut self, cx: &mut task::Context, buf: &mut [u8]) | ||
-> Poll<usize, Error> | ||
{ | ||
(**self).poll_read(cx, buf) | ||
} | ||
|
||
fn poll_vectored_read(&mut self, cx: &mut task::Context, vec: &mut [&mut IoVec]) | ||
-> Poll<usize, Error> | ||
{ | ||
(**self).poll_vectored_read(cx, vec) | ||
} | ||
} | ||
} | ||
|
||
impl<T: ?Sized + AsyncRead> AsyncRead for Box<T> { | ||
deref_async_read!(); | ||
} | ||
|
||
impl<'a, T: ?Sized + AsyncRead> AsyncRead for &'a mut T { | ||
deref_async_read!(); | ||
} | ||
|
||
/// `unsafe` because the `StdIo::Read` type must not access the buffer | ||
/// before reading data into it. | ||
macro_rules! unsafe_delegate_async_read_to_stdio { | ||
() => { | ||
unsafe fn initializer(&self) -> Initializer { | ||
Initializer::nop() | ||
} | ||
|
||
fn poll_read(&mut self, _: &mut task::Context, buf: &mut [u8]) | ||
-> Poll<usize, Error> | ||
{ | ||
Ok(Async::Ready(StdIo::Read::read(self, buf)?)) | ||
} | ||
} | ||
} | ||
|
||
impl<'a> AsyncRead for &'a [u8] { | ||
unsafe_delegate_async_read_to_stdio!(); | ||
} | ||
|
||
impl AsyncRead for StdIo::Repeat { | ||
unsafe_delegate_async_read_to_stdio!(); | ||
} | ||
|
||
impl<T: AsRef<[u8]>> AsyncRead for StdIo::Cursor<T> { | ||
unsafe_delegate_async_read_to_stdio!(); | ||
} | ||
|
||
macro_rules! deref_async_write { | ||
() => { | ||
fn poll_write(&mut self, cx: &mut task::Context, buf: &[u8]) | ||
-> Poll<usize, Error> | ||
{ | ||
(**self).poll_write(cx, buf) | ||
} | ||
|
||
fn poll_vectored_write(&mut self, cx: &mut task::Context, vec: &[&IoVec]) | ||
-> Poll<usize, Error> | ||
{ | ||
(**self).poll_vectored_write(cx, vec) | ||
} | ||
|
||
fn poll_flush(&mut self, cx: &mut task::Context) -> Poll<(), Error> { | ||
(**self).poll_flush(cx) | ||
} | ||
|
||
fn poll_close(&mut self, cx: &mut task::Context) -> Poll<(), Error> { | ||
(**self).poll_close(cx) | ||
} | ||
} | ||
} | ||
|
||
impl<T: ?Sized + AsyncWrite> AsyncWrite for Box<T> { | ||
deref_async_write!(); | ||
} | ||
|
||
impl<'a, T: ?Sized + AsyncWrite> AsyncWrite for &'a mut T { | ||
deref_async_write!(); | ||
} | ||
|
||
macro_rules! delegate_async_write_to_stdio { | ||
() => { | ||
fn poll_write(&mut self, _: &mut task::Context, buf: &[u8]) | ||
-> Poll<usize, Error> | ||
{ | ||
Ok(Async::Ready(StdIo::Write::write(self, buf)?)) | ||
} | ||
|
||
fn poll_flush(&mut self, _: &mut task::Context) -> Poll<(), Error> { | ||
Ok(Async::Ready(StdIo::Write::flush(self)?)) | ||
} | ||
|
||
fn poll_close(&mut self, cx: &mut task::Context) -> Poll<(), Error> { | ||
self.poll_flush(cx) | ||
} | ||
} | ||
} | ||
|
||
impl<'a> AsyncWrite for StdIo::Cursor<&'a mut [u8]> { | ||
delegate_async_write_to_stdio!(); | ||
} | ||
|
||
impl AsyncWrite for StdIo::Cursor<Vec<u8>> { | ||
delegate_async_write_to_stdio!(); | ||
} | ||
|
||
impl AsyncWrite for StdIo::Cursor<Box<[u8]>> { | ||
delegate_async_write_to_stdio!(); | ||
} | ||
|
||
impl AsyncWrite for StdIo::Sink { | ||
delegate_async_write_to_stdio!(); | ||
} | ||
} |
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.
Looking at the std Read::initializer tracking issue, it seems there is doubt that this is the correct API. In std, the
unstable
attribute protects them.It seems odd to be using that API here if it's thought to be inferior, especially since we can't rely on
unstable
to protect against breaking changes.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's true that some people have doubts about the API. I personally think that it's the best-designed interface for this that I've seen, and it's the only one I've seen that is sound in the presence of rust-lang/rfcs#2316.