From 90f8ebc7f0d013d2a3761b047b6ecd4f66235a06 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Wed, 6 Nov 2019 17:14:17 -0800 Subject: [PATCH 01/21] Need to resolve Poll::poll taking &mut self Signed-off-by: Kevin Leimkuhler --- tokio/Cargo.toml | 3 +- tokio/src/net/driver/mod.rs | 3 + tokio/src/net/driver/platform.rs | 44 +- .../reactor/dispatch/page/scheduled_io.rs | 4 +- tokio/src/net/driver/reactor/mod.rs | 124 ++- tokio/src/net/driver/readiness.rs | 116 +++ tokio/src/net/driver/registration.rs | 36 +- tokio/src/net/tcp/listener.rs | 37 +- tokio/src/net/tcp/stream.rs | 297 +------ tokio/src/net/udp/socket.rs | 26 +- tokio/src/net/unix/datagram.rs | 42 +- tokio/src/net/unix/listener.rs | 52 +- tokio/src/net/unix/stream.rs | 119 ++- tokio/src/net/util/io_source.rs | 198 +++++ tokio/src/net/util/mod.rs | 4 +- tokio/src/net/util/poll_evented.rs | 826 +++++++++--------- tokio/src/process/unix/mod.rs | 46 +- tokio/src/signal/unix.rs | 12 +- 18 files changed, 1035 insertions(+), 954 deletions(-) create mode 100644 tokio/src/net/driver/readiness.rs create mode 100644 tokio/src/net/util/io_source.rs diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index 9364ea827b3..58bd6e266b2 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -100,7 +100,8 @@ fnv = { version = "1.0.6", optional = true } futures-core = { version = "0.3.0", optional = true } lazy_static = { version = "1.0.2", optional = true } memchr = { version = "2.2", optional = true } -mio = { version = "0.6.14", optional = true } +# mio = { git = "https://github.com/tokio-rs/mio", branch = "master", optional = true } +mio = { path = "../../mio", optional = true } num_cpus = { version = "1.8.0", optional = true } # Backs `DelayQueue` slab = { version = "0.4.1", optional = true } diff --git a/tokio/src/net/driver/mod.rs b/tokio/src/net/driver/mod.rs index 2b3afc7d8e0..2035d07baad 100644 --- a/tokio/src/net/driver/mod.rs +++ b/tokio/src/net/driver/mod.rs @@ -139,3 +139,6 @@ mod registration; pub use self::reactor::{set_default, DefaultGuard, Handle, Reactor}; pub use self::registration::Registration; + +mod readiness; +pub use self::readiness::Readiness; diff --git a/tokio/src/net/driver/platform.rs b/tokio/src/net/driver/platform.rs index 4cfe7345b78..58f3b4c0f7f 100644 --- a/tokio/src/net/driver/platform.rs +++ b/tokio/src/net/driver/platform.rs @@ -1,28 +1,28 @@ -pub(crate) use self::sys::*; +// pub(crate) use self::sys::*; -#[cfg(unix)] -mod sys { - use mio::unix::UnixReady; - use mio::Ready; +// #[cfg(unix)] +// mod sys { +// use mio::unix::UnixReady; +// use mio::Ready; - pub(crate) fn hup() -> Ready { - UnixReady::hup().into() - } +// pub(crate) fn hup() -> Ready { +// UnixReady::hup().into() +// } - pub(crate) fn is_hup(ready: Ready) -> bool { - UnixReady::from(ready).is_hup() - } -} +// pub(crate) fn is_hup(ready: Ready) -> bool { +// UnixReady::from(ready).is_hup() +// } +// } -#[cfg(windows)] -mod sys { - use mio::Ready; +// #[cfg(windows)] +// mod sys { +// use mio::Ready; - pub(crate) fn hup() -> Ready { - Ready::empty() - } +// pub(crate) fn hup() -> Ready { +// Ready::empty() +// } - pub(crate) fn is_hup(_: Ready) -> bool { - false - } -} +// pub(crate) fn is_hup(_: Ready) -> bool { +// false +// } +// } diff --git a/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs b/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs index aa0441220a0..0349bc5c346 100644 --- a/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs +++ b/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs @@ -1,5 +1,6 @@ use super::super::{Pack, Tid, RESERVED_BITS, WIDTH}; use crate::loom::{cell::CausalCell, sync::atomic::AtomicUsize}; +use crate::net::driver::Readiness; use crate::sync::AtomicWaker; use std::sync::atomic::Ordering; @@ -147,7 +148,8 @@ impl ScheduledIo { } // Mask out the generation bits so that the modifying function // doesn't see them. - let current_readiness = current & mio::Ready::all().as_usize(); + let mask = Readiness::all(); + let current_readiness = current & mask.as_usize(); let new = f(current_readiness); debug_assert!( new < Generation::ONE, diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index 8bcded41119..16439eb18f2 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -1,5 +1,4 @@ use crate::loom::sync::atomic::AtomicUsize; -use crate::net::driver::platform; use crate::runtime::{Park, Unpark}; use std::sync::atomic::Ordering::SeqCst; @@ -8,7 +7,8 @@ mod dispatch; use dispatch::SingleShard; pub(crate) use dispatch::MAX_SOURCES; -use mio::event::Evented; +use super::Readiness; +use mio; use std::cell::RefCell; use std::io; use std::marker::PhantomData; @@ -31,8 +31,8 @@ pub struct Reactor { /// State shared between the reactor and the handles. inner: Arc, - - _wakeup_registration: mio::Registration, + // Is this still needed? + // _wakeup_registration: mio::Registry, } /// A reference to a reactor. @@ -56,7 +56,7 @@ pub struct Turn { pub(super) struct Inner { /// The underlying system event queue. - io: mio::Poll, + poll: mio::Poll, /// Dispatch slabs for I/O and futures events // TODO(eliza): once worker threads are available, replace this with a @@ -67,7 +67,9 @@ pub(super) struct Inner { n_sources: AtomicUsize, /// Used to wake up the reactor from a call to `turn` - wakeup: mio::SetReadiness, + /// + /// TODO: Rename to waker? + wakeup: mio::Waker, } #[derive(Debug, Eq, PartialEq, Clone, Copy)] @@ -129,24 +131,24 @@ impl Reactor { /// Creates a new event loop, returning any error that happened during the /// creation. pub fn new() -> io::Result { - let io = mio::Poll::new()?; - let wakeup_pair = mio::Registration::new2(); - - io.register( - &wakeup_pair.0, - TOKEN_WAKEUP, - mio::Ready::readable(), - mio::PollOpt::level(), - )?; + let poll = mio::Poll::new()?; + // let wakeup_pair = mio::Registration::new2(); + let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?; + + // io.register( + // &wakeup_pair.0, + // TOKEN_WAKEUP, + // mio::Ready::readable(), + // mio::PollOpt::level(), + // )?; Ok(Reactor { events: mio::Events::with_capacity(1024), - _wakeup_registration: wakeup_pair.0, inner: Arc::new(Inner { - io, + poll, io_dispatch: SingleShard::new(), n_sources: AtomicUsize::new(0), - wakeup: wakeup_pair.1, + wakeup: waker, }), }) } @@ -205,30 +207,33 @@ impl Reactor { fn poll(&mut self, max_wait: Option) -> io::Result<()> { // Block waiting for an event to happen, peeling out how many events // happened. - match self.inner.io.poll(&mut self.events, max_wait) { + match self.inner.poll.poll(&mut self.events, max_wait) { Ok(_) => {} Err(e) => return Err(e), } // Process all the events that came in, dispatching appropriately - for event in self.events.iter() { let token = event.token(); - if token == TOKEN_WAKEUP { - self.inner - .wakeup - .set_readiness(mio::Ready::empty()) - .unwrap(); - } else { - self.dispatch(token, event.readiness()); + // if token == TOKEN_WAKEUP { + // self.inner + // .wakeup + // .set_readiness(mio::Ready::empty()) + // .unwrap(); + // } else { + // self.dispatch(token, event.readiness()); + // } + if token != TOKEN_WAKEUP { + self.dispatch(token, event); } } Ok(()) } - fn dispatch(&self, token: mio::Token, ready: mio::Ready) { + // fn dispatch(&self, token: mio::Token, ready: mio::Ready) { + fn dispatch(&self, token: mio::Token, event: &mio::event::Event) { let mut rd = None; let mut wr = None; @@ -237,19 +242,35 @@ impl Reactor { None => return, }; + let mut readiness = Readiness::empty(); + if event.is_readable() { + readiness |= Readiness::readable() + } + if event.is_writable() { + readiness |= Readiness::writable() + } + if event.is_read_closed() { + readiness |= Readiness::read_closed() + } + if event.is_write_closed() { + readiness |= Readiness::write_closed() + } + if io - .set_readiness(token.0, |curr| curr | ready.as_usize()) + .set_readiness(token.0, |curr| curr | readiness.as_usize()) .is_err() { // token no longer valid! return; } - if ready.is_writable() || platform::is_hup(ready) { + // if ready.is_writable() || platform::is_hup(ready) { + if readiness.is_writable() || readiness.is_write_closed() { wr = io.writer.take_waker(); } - if !(ready & (!mio::Ready::writable())).is_empty() { + // if !(ready & (!mio::Ready::writable())).is_empty() { + if readiness.is_readable() || readiness.is_read_closed() { rd = io.reader.take_waker(); } @@ -266,7 +287,7 @@ impl Reactor { #[cfg(all(unix, not(target_os = "fuchsia")))] impl AsRawFd for Reactor { fn as_raw_fd(&self) -> RawFd { - self.inner.io.as_raw_fd() + self.inner.poll.as_raw_fd() } } @@ -321,7 +342,8 @@ impl Handle { /// return immediately. fn wakeup(&self) { if let Some(inner) = self.inner() { - inner.wakeup.set_readiness(mio::Ready::readable()).unwrap(); + // inner.wakeup.set_readiness(mio::Ready::readable()).unwrap(); + inner.wakeup.wake().expect("failed to wake reactor") } } @@ -348,7 +370,7 @@ impl Inner { /// Register an I/O resource with the reactor. /// /// The registration token is returned. - pub(super) fn add_source(&self, source: &dyn Evented) -> io::Result { + pub(super) fn add_source(&self, source: &dyn mio::event::Source) -> io::Result { let token = self.io_dispatch.alloc().ok_or_else(|| { io::Error::new( io::ErrorKind::Other, @@ -356,19 +378,24 @@ impl Inner { ) })?; self.n_sources.fetch_add(1, SeqCst); - self.io.register( + // self.io.register( + // source, + // mio::Token(token), + // mio::Ready::all(), + // mio::PollOpt::edge(), + // )?; + self.poll.registry().register( source, mio::Token(token), - mio::Ready::all(), - mio::PollOpt::edge(), + mio::Interests::READABLE | mio::Interests::WRITABLE, )?; Ok(token) } /// Deregisters an I/O resource from the reactor. - pub(super) fn deregister_source(&self, source: &dyn Evented) -> io::Result<()> { - self.io.deregister(source) + pub(super) fn deregister_source(&self, source: &dyn mio::event::Source) -> io::Result<()> { + self.poll.registry().deregister(source) } pub(super) fn drop_source(&self, token: usize) { @@ -382,17 +409,19 @@ impl Inner { .io_dispatch .get(token) .unwrap_or_else(|| panic!("IO resource for token {} does not exist!", token)); - let readiness = sched + let current = sched .get_readiness(token) .unwrap_or_else(|| panic!("token {} no longer valid!", token)); - let (waker, ready) = match dir { - Direction::Read => (&sched.reader, !mio::Ready::writable()), - Direction::Write => (&sched.writer, mio::Ready::writable()), + let (waker, readiness) = match dir { + // Direction::Read => (&sched.reader, !mio::Ready::writable()), + Direction::Read => (&sched.reader, Readiness::readable()), + // Direction::Write => (&sched.writer, mio::Ready::writable()), + Direction::Write => (&sched.writer, Readiness::writable()), }; waker.register(w); - if readiness & ready.as_usize() != 0 { + if current & readiness.as_usize() != 0 { waker.wake(); } } @@ -411,13 +440,14 @@ impl Drop for Inner { } impl Direction { - pub(super) fn mask(self) -> mio::Ready { + pub(super) fn mask(self) -> Readiness { match self { Direction::Read => { // Everything except writable is signaled through read. - mio::Ready::all() - mio::Ready::writable() + // mio::Ready::all() - mio::Ready::writable() + Readiness::readable() } - Direction::Write => mio::Ready::writable() | platform::hup(), + Direction::Write => Readiness::writable(), } } } diff --git a/tokio/src/net/driver/readiness.rs b/tokio/src/net/driver/readiness.rs new file mode 100644 index 00000000000..139cc6abc9f --- /dev/null +++ b/tokio/src/net/driver/readiness.rs @@ -0,0 +1,116 @@ +use std::ops; + +const READABLE: usize = 0b01; +const WRITABLE: usize = 0b10; +const READ_CLOSED: usize = 0b100; +const WRITE_CLOSED: usize = 0b1000; + +#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)] +pub struct Readiness(usize); + +impl Readiness { + pub fn empty() -> Readiness { + Readiness(0) + } + + pub fn readable() -> Readiness { + Readiness(READABLE) + } + + pub fn writable() -> Readiness { + Readiness(WRITABLE) + } + + pub fn read_closed() -> Readiness { + Readiness(READ_CLOSED) + } + + pub fn write_closed() -> Readiness { + Readiness(WRITE_CLOSED) + } + + pub fn all() -> Readiness { + Readiness(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED) + } + + pub fn is_empty(&self) -> bool { + *self == Readiness::empty() + } + + pub fn is_readable(&self) -> bool { + self.contains(Readiness::readable()) + } + + pub fn is_writable(&self) -> bool { + self.contains(Readiness::writable()) + } + + pub fn is_read_closed(&self) -> bool { + self.contains(Readiness::read_closed()) + } + + pub fn is_write_closed(&self) -> bool { + self.contains(Readiness::write_closed()) + } + + pub fn contains>(&self, other: T) -> bool { + let other = other.into(); + (*self & other) == other + } + + pub fn from_usize(val: usize) -> Readiness { + Readiness(val) + } + + pub fn as_usize(&self) -> usize { + self.0 + } +} + +impl> ops::BitOr for Readiness { + type Output = Readiness; + + #[inline] + fn bitor(self, other: T) -> Readiness { + Readiness(self.0 | other.into().0) + } +} + +impl> ops::BitOrAssign for Readiness { + #[inline] + fn bitor_assign(&mut self, other: T) { + self.0 |= other.into().0; + } +} + +impl> ops::BitAnd for Readiness { + type Output = Readiness; + + #[inline] + fn bitand(self, other: T) -> Readiness { + Readiness(self.0 & other.into().0) + } +} + +impl> ops::BitAndAssign for Readiness { + #[inline] + fn bitand_assign(&mut self, other: T) { + self.0 &= other.into().0 + } +} + +impl> ops::Sub for Readiness { + type Output = Readiness; + + #[inline] + fn sub(self, other: T) -> Readiness { + Readiness(self.0 & !other.into().0) + } +} + +impl> ops::SubAssign for Readiness { + #[inline] + fn sub_assign(&mut self, other: T) { + self.0 &= !other.into().0; + } +} diff --git a/tokio/src/net/driver/registration.rs b/tokio/src/net/driver/registration.rs index 8e946d5a94f..53ce488ec91 100644 --- a/tokio/src/net/driver/registration.rs +++ b/tokio/src/net/driver/registration.rs @@ -1,7 +1,7 @@ -use super::platform; use super::reactor::{Direction, Handle}; +use super::Readiness; -use mio::{self, Evented}; +use mio; use std::task::{Context, Poll}; use std::{io, usize}; @@ -52,7 +52,7 @@ impl Registration { /// - `Err` if an error was encountered during registration pub fn new(io: &T) -> io::Result where - T: Evented, + T: mio::event::Source, { let handle = Handle::current(); let token = if let Some(inner) = handle.inner() { @@ -84,7 +84,7 @@ impl Registration { /// `Err` is returned if an error is encountered. pub fn deregister(&mut self, io: &T) -> io::Result<()> where - T: Evented, + T: mio::event::Source, { let inner = match self.handle.inner() { Some(inner) => inner, @@ -125,7 +125,7 @@ impl Registration { /// # Panics /// /// This function will panic if called from outside of a task context. - pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { let v = self.poll_ready(Direction::Read, Some(cx))?; match v { Some(v) => Poll::Ready(Ok(v)), @@ -140,7 +140,7 @@ impl Registration { /// it is safe to call this function from outside of a task context. /// /// [`poll_read_ready`]: #method.poll_read_ready - pub fn take_read_ready(&self) -> io::Result> { + pub fn take_read_ready(&self) -> io::Result> { self.poll_ready(Direction::Read, None) } @@ -176,7 +176,7 @@ impl Registration { /// # Panics /// /// This function will panic if called from outside of a task context. - pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { let v = self.poll_ready(Direction::Write, Some(cx))?; match v { Some(v) => Poll::Ready(Ok(v)), @@ -191,7 +191,7 @@ impl Registration { /// it is safe to call this function from outside of a task context. /// /// [`poll_write_ready`]: #method.poll_write_ready - pub fn take_write_ready(&self) -> io::Result> { + pub fn take_write_ready(&self) -> io::Result> { self.poll_ready(Direction::Write, None) } @@ -203,7 +203,7 @@ impl Registration { &self, direction: Direction, cx: Option<&mut Context<'_>>, - ) -> io::Result> { + ) -> io::Result> { let inner = match self.handle.inner() { Some(inner) => inner, None => return Err(io::Error::new(io::ErrorKind::Other, "reactor gone")), @@ -216,7 +216,9 @@ impl Registration { } let mask = direction.mask(); - let mask_no_hup = (mask - platform::hup()).as_usize(); + let mask_no_closed = + (mask - (Readiness::read_closed() - Readiness::write_closed())).as_usize(); + // let mask_no_hup = (mask - platform::hup()).as_usize(); let sched = inner.io_dispatch.get(self.token).unwrap(); @@ -226,12 +228,14 @@ impl Registration { // observe this state. // // If HUP were to be cleared when `direction` is `Read`, then when - // `poll_ready` is called again with a _`direction` of `Write`, the HUP + // `poll_ready` is called again with a `direction` of `Write`, the HUP // state would not be visible. let curr_ready = sched - .set_readiness(self.token, |curr| curr & (!mask_no_hup)) + // .set_readiness(self.token, |curr| curr & (!mask_no_hup)) + .set_readiness(self.token, |curr| curr & (!mask_no_closed)) .unwrap_or_else(|_| panic!("token {} no longer valid!", self.token)); - let mut ready = mask & mio::Ready::from_usize(curr_ready); + // let mut ready = mask & mio::Ready::from_usize(curr_ready); + let mut ready = mask & Readiness::from_usize(curr_ready); if ready.is_empty() { if let Some(cx) = cx { @@ -243,9 +247,11 @@ impl Registration { // Try again let curr_ready = sched - .set_readiness(self.token, |curr| curr & (!mask_no_hup)) + // .set_readiness(self.token, |curr| curr & (!mask_no_hup)) + .set_readiness(self.token, |curr| curr & (!mask_no_closed)) .unwrap_or_else(|_| panic!("token {} no longer valid!", self.token)); - ready = mask & mio::Ready::from_usize(curr_ready); + // ready = mask & mio::Ready::from_usize(curr_ready); + ready = mask & Readiness::from_usize(curr_ready); } } diff --git a/tokio/src/net/tcp/listener.rs b/tokio/src/net/tcp/listener.rs index c0e7f1b3478..e4dc24aa190 100644 --- a/tokio/src/net/tcp/listener.rs +++ b/tokio/src/net/tcp/listener.rs @@ -1,6 +1,6 @@ use crate::future::poll_fn; use crate::net::tcp::{Incoming, TcpStream}; -use crate::net::util::PollEvented; +use crate::net::util::IoSource; use crate::net::ToSocketAddrs; use std::convert::TryFrom; @@ -30,7 +30,7 @@ use std::task::{Context, Poll}; /// } /// ``` pub struct TcpListener { - io: PollEvented, + io: IoSource, } impl TcpListener { @@ -87,7 +87,7 @@ impl TcpListener { } fn bind_addr(addr: SocketAddr) -> io::Result { - let listener = mio::net::TcpListener::bind(&addr)?; + let listener = mio::net::TcpListener::bind(addr)?; TcpListener::new(listener) } @@ -127,27 +127,18 @@ impl TcpListener { &mut self, cx: &mut Context<'_>, ) -> Poll> { - let (io, addr) = ready!(self.poll_accept_std(cx))?; + ready!(self.io.poll_read_ready(cx))?; - let io = mio::net::TcpStream::from_stream(io)?; - let io = TcpStream::new(io)?; - - Poll::Ready(Ok((io, addr))) - } - - fn poll_accept_std( - &mut self, - cx: &mut Context<'_>, - ) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; - - match self.io.get_ref().accept_std() { - Ok(pair) => Poll::Ready(Ok(pair)), + match self.io.get_ref().accept() { + Ok((stream, sockaddr)) => { + let stream = TcpStream::new(stream)?; + Ok((stream, sockaddr)).into() + } Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } - Err(e) => Poll::Ready(Err(e)), + Err(e) => Err(e).into(), } } @@ -190,13 +181,13 @@ impl TcpListener { /// } /// ``` pub fn from_std(listener: net::TcpListener) -> io::Result { - let io = mio::net::TcpListener::from_std(listener)?; - let io = PollEvented::new(io)?; + let io = mio::net::TcpListener::from_std(listener); + let io = IoSource::new(io)?; Ok(TcpListener { io }) } fn new(listener: mio::net::TcpListener) -> io::Result { - let io = PollEvented::new(listener)?; + let io = IoSource::new(listener)?; Ok(TcpListener { io }) } diff --git a/tokio/src/net/tcp/stream.rs b/tokio/src/net/tcp/stream.rs index 1a844e86c83..5c060572695 100644 --- a/tokio/src/net/tcp/stream.rs +++ b/tokio/src/net/tcp/stream.rs @@ -1,18 +1,16 @@ use crate::future::poll_fn; use crate::io::{AsyncRead, AsyncWrite}; use crate::net::tcp::split::{split, ReadHalf, WriteHalf}; -use crate::net::util::PollEvented; +use crate::net::util::IoSource; use crate::net::ToSocketAddrs; use bytes::{Buf, BufMut}; -use iovec::IoVec; use std::convert::TryFrom; use std::fmt; use std::io::{self, Read, Write}; use std::net::{self, Shutdown, SocketAddr}; use std::pin::Pin; use std::task::{Context, Poll}; -use std::time::Duration; /// An I/O object representing a TCP stream connected to a remote endpoint. /// @@ -42,7 +40,7 @@ use std::time::Duration; /// } /// ``` pub struct TcpStream { - io: PollEvented, + io: IoSource, } impl TcpStream { @@ -96,7 +94,7 @@ impl TcpStream { /// Establish a connection to the specified `addr`. async fn connect_addr(addr: SocketAddr) -> io::Result { - let sys = mio::net::TcpStream::connect(&addr)?; + let sys = mio::net::TcpStream::connect(addr)?; let stream = TcpStream::new(sys)?; // Once we've connected, wait for the stream to be writable as @@ -115,7 +113,7 @@ impl TcpStream { } pub(crate) fn new(connected: mio::net::TcpStream) -> io::Result { - let io = PollEvented::new(connected)?; + let io = IoSource::new(connected)?; Ok(TcpStream { io }) } @@ -138,8 +136,8 @@ impl TcpStream { /// } /// ``` pub fn from_std(stream: net::TcpStream) -> io::Result { - let io = mio::net::TcpStream::from_stream(stream)?; - let io = PollEvented::new(io)?; + let io = mio::net::TcpStream::from_std(stream); + let io = IoSource::new(io)?; Ok(TcpStream { io }) } @@ -147,10 +145,10 @@ impl TcpStream { // // This should be removed in favor of some in-crate TcpSocket builder API. #[doc(hidden)] - pub async fn connect_std(stream: net::TcpStream, addr: &SocketAddr) -> io::Result { - let io = mio::net::TcpStream::connect_stream(stream, addr)?; - let io = PollEvented::new(io)?; - let stream = TcpStream { io }; + pub async fn connect_std(_stream: net::TcpStream, _addr: &SocketAddr) -> io::Result { + // let io = mio::net::TcpStream::connect_stream(stream, addr)?; + // let io = IoSource::new(io)?; + // let stream = TcpStream { io }; // Once we've connected, wait for the stream to be writable as // that's when the actual connection has been initiated. Once we're @@ -158,13 +156,14 @@ impl TcpStream { // actually hit an error or not. // // If all that succeeded then we ship everything on up. - poll_fn(|cx| stream.io.poll_write_ready(cx)).await?; + // poll_fn(|cx| stream.io.poll_write_ready(cx)).await?; - if let Some(e) = stream.io.get_ref().take_error()? { - return Err(e); - } + // if let Some(e) = stream.io.get_ref().take_error()? { + // return Err(e); + // } - Ok(stream) + // Ok(stream) + unimplemented!() } /// Returns the local address that this stream is bound to. @@ -203,12 +202,12 @@ impl TcpStream { } fn poll_peek(&mut self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; + ready!(self.io.poll_read_ready(cx))?; match self.io.get_ref().peek(buf) { Ok(ret) => Poll::Ready(Ok(ret)), Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } Err(e) => Poll::Ready(Err(e)), @@ -325,153 +324,6 @@ impl TcpStream { self.io.get_ref().set_nodelay(nodelay) } - /// Gets the value of the `SO_RCVBUF` option on this socket. - /// - /// For more information about this option, see [`set_recv_buffer_size`]. - /// - /// [`set_recv_buffer_size`]: #tymethod.set_recv_buffer_size - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// println!("{:?}", stream.recv_buffer_size()?); - /// # Ok(()) - /// # } - /// ``` - pub fn recv_buffer_size(&self) -> io::Result { - self.io.get_ref().recv_buffer_size() - } - - /// Sets the value of the `SO_RCVBUF` option on this socket. - /// - /// Changes the size of the operating system's receive buffer associated - /// with the socket. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// stream.set_recv_buffer_size(100)?; - /// # Ok(()) - /// # } - /// ``` - pub fn set_recv_buffer_size(&self, size: usize) -> io::Result<()> { - self.io.get_ref().set_recv_buffer_size(size) - } - - /// Gets the value of the `SO_SNDBUF` option on this socket. - /// - /// For more information about this option, see [`set_send_buffer`]. - /// - /// [`set_send_buffer`]: #tymethod.set_send_buffer - /// - /// # Examples - /// - /// Returns whether keepalive messages are enabled on this socket, and if so - /// the duration of time between them. - /// - /// For more information about this option, see [`set_keepalive`]. - /// - /// [`set_keepalive`]: #tymethod.set_keepalive - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// println!("{:?}", stream.send_buffer_size()?); - /// # Ok(()) - /// # } - /// ``` - pub fn send_buffer_size(&self) -> io::Result { - self.io.get_ref().send_buffer_size() - } - - /// Sets the value of the `SO_SNDBUF` option on this socket. - /// - /// Changes the size of the operating system's send buffer associated with - /// the socket. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// stream.set_send_buffer_size(100)?; - /// # Ok(()) - /// # } - /// ``` - pub fn set_send_buffer_size(&self, size: usize) -> io::Result<()> { - self.io.get_ref().set_send_buffer_size(size) - } - - /// Returns whether keepalive messages are enabled on this socket, and if so - /// the duration of time between them. - /// - /// For more information about this option, see [`set_keepalive`]. - /// - /// [`set_keepalive`]: #tymethod.set_keepalive - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// println!("{:?}", stream.keepalive()?); - /// # Ok(()) - /// # } - /// ``` - pub fn keepalive(&self) -> io::Result> { - self.io.get_ref().keepalive() - } - - /// Sets whether keepalive messages are enabled to be sent on this socket. - /// - /// On Unix, this option will set the `SO_KEEPALIVE` as well as the - /// `TCP_KEEPALIVE` or `TCP_KEEPIDLE` option (depending on your platform). - /// On Windows, this will set the `SIO_KEEPALIVE_VALS` option. - /// - /// If `None` is specified then keepalive messages are disabled, otherwise - /// the duration specified will be the time to remain idle before sending a - /// TCP keepalive probe. - /// - /// Some platforms specify this value in seconds, so sub-second - /// specifications may be omitted. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// stream.set_keepalive(None)?; - /// # Ok(()) - /// # } - /// ``` - pub fn set_keepalive(&self, keepalive: Option) -> io::Result<()> { - self.io.get_ref().set_keepalive(keepalive) - } - /// Gets the value of the `IP_TTL` option for this socket. /// /// For more information about this option, see [`set_ttl`]. @@ -515,57 +367,6 @@ impl TcpStream { self.io.get_ref().set_ttl(ttl) } - /// Reads the linger duration for this socket by getting the `SO_LINGER` - /// option. - /// - /// For more information about this option, see [`set_linger`]. - /// - /// [`set_linger`]: #tymethod.set_linger - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// println!("{:?}", stream.linger()?); - /// # Ok(()) - /// # } - /// ``` - pub fn linger(&self) -> io::Result> { - self.io.get_ref().linger() - } - - /// Sets the linger duration of this socket by setting the `SO_LINGER` - /// option. - /// - /// This option controls the action taken when a stream has unsent messages - /// and the stream is closed. If `SO_LINGER` is set, the system - /// shall block the process until it can transmit the data or until the - /// time expires. - /// - /// If `SO_LINGER` is not specified, and the stream is closed, the system - /// handles the call in a way that allows the process to continue as quickly - /// as possible. - /// - /// # Examples - /// - /// ```no_run - /// use tokio::net::TcpStream; - /// - /// # async fn dox() -> Result<(), Box> { - /// let stream = TcpStream::connect("127.0.0.1:8080").await?; - /// - /// stream.set_linger(None)?; - /// # Ok(()) - /// # } - /// ``` - pub fn set_linger(&self, dur: Option) -> io::Result<()> { - self.io.get_ref().set_linger(dur) - } - /// Split a `TcpStream` into a read half and a write half, which can be used /// to read and write the stream concurrently. /// @@ -591,11 +392,11 @@ impl TcpStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; + ready!(self.io.poll_read_ready(cx))?; match self.io.get_ref().read(buf) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } x => Poll::Ready(x), @@ -607,50 +408,9 @@ impl TcpStream { cx: &mut Context<'_>, buf: &mut B, ) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; - - let r = unsafe { - // The `IoVec` type can't have a 0-length size, so we create a bunch - // of dummy versions on the stack with 1 length which we'll quickly - // overwrite. - let b1: &mut [u8] = &mut [0]; - let b2: &mut [u8] = &mut [0]; - let b3: &mut [u8] = &mut [0]; - let b4: &mut [u8] = &mut [0]; - let b5: &mut [u8] = &mut [0]; - let b6: &mut [u8] = &mut [0]; - let b7: &mut [u8] = &mut [0]; - let b8: &mut [u8] = &mut [0]; - let b9: &mut [u8] = &mut [0]; - let b10: &mut [u8] = &mut [0]; - let b11: &mut [u8] = &mut [0]; - let b12: &mut [u8] = &mut [0]; - let b13: &mut [u8] = &mut [0]; - let b14: &mut [u8] = &mut [0]; - let b15: &mut [u8] = &mut [0]; - let b16: &mut [u8] = &mut [0]; - let mut bufs: [&mut IoVec; 16] = [ - b1.into(), - b2.into(), - b3.into(), - b4.into(), - b5.into(), - b6.into(), - b7.into(), - b8.into(), - b9.into(), - b10.into(), - b11.into(), - b12.into(), - b13.into(), - b14.into(), - b15.into(), - b16.into(), - ]; - let n = buf.bytes_vec_mut(&mut bufs); - self.io.get_ref().read_bufs(&mut bufs[..n]) - }; + ready!(self.io.poll_read_ready(cx))?; + let r = unsafe { self.io.get_ref().read(buf.bytes_mut()) }; match r { Ok(n) => { unsafe { @@ -659,7 +419,7 @@ impl TcpStream { Poll::Ready(Ok(n)) } Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } Err(e) => Poll::Ready(Err(e)), @@ -689,16 +449,7 @@ impl TcpStream { ) -> Poll> { ready!(self.io.poll_write_ready(cx))?; - let r = { - // The `IoVec` type can't have a zero-length size, so create a dummy - // version from a 1-length slice which we'll overwrite with the - // `bytes_vec` method. - static DUMMY: &[u8] = &[0]; - let iovec = <&IoVec>::from(DUMMY); - let mut bufs = [iovec; 64]; - let n = buf.bytes_vec(&mut bufs); - self.io.get_ref().write_bufs(&bufs[..n]) - }; + let r = self.io.get_ref().write(buf.bytes()); match r { Ok(n) => { buf.advance(n); diff --git a/tokio/src/net/udp/socket.rs b/tokio/src/net/udp/socket.rs index b1d1e5cc499..2010513db2e 100644 --- a/tokio/src/net/udp/socket.rs +++ b/tokio/src/net/udp/socket.rs @@ -1,6 +1,6 @@ use crate::future::poll_fn; use crate::net::udp::split::{split, UdpSocketRecvHalf, UdpSocketSendHalf}; -use crate::net::util::PollEvented; +use crate::net::util::IoSource; use crate::net::ToSocketAddrs; use std::convert::TryFrom; @@ -11,7 +11,7 @@ use std::task::{Context, Poll}; /// An I/O object representing a UDP socket. pub struct UdpSocket { - io: PollEvented, + io: IoSource, } impl UdpSocket { @@ -37,12 +37,12 @@ impl UdpSocket { } fn bind_addr(addr: SocketAddr) -> io::Result { - let sys = mio::net::UdpSocket::bind(&addr)?; + let sys = mio::net::UdpSocket::bind(addr)?; UdpSocket::new(sys) } fn new(socket: mio::net::UdpSocket) -> io::Result { - let io = PollEvented::new(socket)?; + let io = IoSource::new(socket)?; Ok(UdpSocket { io }) } @@ -56,8 +56,8 @@ impl UdpSocket { /// configure a socket before it's handed off, such as setting options like /// `reuse_address` or binding to multiple addresses. pub fn from_std(socket: net::UdpSocket) -> io::Result { - let io = mio::net::UdpSocket::from_socket(socket)?; - let io = PollEvented::new(io)?; + let io = mio::net::UdpSocket::from_std(socket); + let io = IoSource::new(io)?; Ok(UdpSocket { io }) } @@ -150,11 +150,11 @@ impl UdpSocket { #[doc(hidden)] pub fn poll_recv(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; + ready!(self.io.poll_read_ready(cx))?; match self.io.get_ref().recv(buf) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } x => Poll::Ready(x), @@ -188,7 +188,7 @@ impl UdpSocket { ) -> Poll> { ready!(self.io.poll_write_ready(cx))?; - match self.io.get_ref().send_to(buf, target) { + match self.io.get_ref().send_to(buf, *target) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { self.io.clear_write_ready(cx)?; Poll::Pending @@ -213,11 +213,11 @@ impl UdpSocket { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; + ready!(self.io.poll_read_ready(cx))?; match self.io.get_ref().recv_from(buf) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } x => Poll::Ready(x), @@ -328,7 +328,7 @@ impl UdpSocket { /// multicast group. If it's equal to `INADDR_ANY` then an appropriate /// interface is chosen by the system. pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> { - self.io.get_ref().join_multicast_v4(&multiaddr, &interface) + self.io.get_ref().join_multicast_v4(multiaddr, interface) } /// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type. @@ -346,7 +346,7 @@ impl UdpSocket { /// /// [`join_multicast_v4`]: #method.join_multicast_v4 pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> { - self.io.get_ref().leave_multicast_v4(&multiaddr, &interface) + self.io.get_ref().leave_multicast_v4(multiaddr, interface) } /// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type. diff --git a/tokio/src/net/unix/datagram.rs b/tokio/src/net/unix/datagram.rs index 55eebd6c1e4..1a77c346aed 100644 --- a/tokio/src/net/unix/datagram.rs +++ b/tokio/src/net/unix/datagram.rs @@ -1,18 +1,19 @@ use crate::future::poll_fn; -use crate::net::util::PollEvented; +use crate::net::util::IoSource; +use mio; use std::convert::TryFrom; use std::fmt; use std::io; use std::net::Shutdown; use std::os::unix::io::{AsRawFd, RawFd}; -use std::os::unix::net::{self, SocketAddr}; +use std::os::unix::net; use std::path::Path; use std::task::{Context, Poll}; /// An I/O object representing a Unix datagram socket. pub struct UnixDatagram { - io: PollEvented, + io: IoSource, } impl UnixDatagram { @@ -21,7 +22,7 @@ impl UnixDatagram { where P: AsRef, { - let socket = mio_uds::UnixDatagram::bind(path)?; + let socket = mio::net::UnixDatagram::bind(path)?; UnixDatagram::new(socket) } @@ -31,7 +32,7 @@ impl UnixDatagram { /// communicating back and forth between one another. Each socket will /// be associated with the default event loop's handle. pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { - let (a, b) = mio_uds::UnixDatagram::pair()?; + let (a, b) = mio::net::UnixDatagram::pair()?; let a = UnixDatagram::new(a)?; let b = UnixDatagram::new(b)?; @@ -44,19 +45,19 @@ impl UnixDatagram { /// The returned datagram will be associated with the given event loop /// specified by `handle` and is ready to perform I/O. pub fn from_std(datagram: net::UnixDatagram) -> io::Result { - let socket = mio_uds::UnixDatagram::from_datagram(datagram)?; - let io = PollEvented::new(socket)?; + let socket = mio::net::UnixDatagram::from_std(datagram); + let io = IoSource::new(socket)?; Ok(UnixDatagram { io }) } - fn new(socket: mio_uds::UnixDatagram) -> io::Result { - let io = PollEvented::new(socket)?; + fn new(socket: mio::net::UnixDatagram) -> io::Result { + let io = IoSource::new(socket)?; Ok(UnixDatagram { io }) } /// Creates a new `UnixDatagram` which is not bound to any address. pub fn unbound() -> io::Result { - let socket = mio_uds::UnixDatagram::unbound()?; + let socket = mio::net::UnixDatagram::unbound()?; UnixDatagram::new(socket) } @@ -109,11 +110,11 @@ impl UnixDatagram { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; + ready!(self.io.poll_read_ready(cx))?; match self.io.get_ref().recv(buf) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } x => Poll::Ready(x), @@ -146,7 +147,10 @@ impl UnixDatagram { } /// Receives data from the socket. - pub async fn recv_from(&mut self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + pub async fn recv_from( + &mut self, + buf: &mut [u8], + ) -> io::Result<(usize, mio::unix::SocketAddr)> { poll_fn(|cx| self.poll_recv_from_priv(cx, buf)).await } @@ -154,12 +158,12 @@ impl UnixDatagram { &self, cx: &mut Context<'_>, buf: &mut [u8], - ) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; + ) -> Poll> { + ready!(self.io.poll_read_ready(cx))?; match self.io.get_ref().recv_from(buf) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } x => Poll::Ready(x), @@ -167,14 +171,14 @@ impl UnixDatagram { } /// Returns the local address that this socket is bound to. - pub fn local_addr(&self) -> io::Result { + pub fn local_addr(&self) -> io::Result { self.io.get_ref().local_addr() } /// Returns the address of this socket's peer. /// /// The `connect` method will connect the socket to a peer. - pub fn peer_addr(&self) -> io::Result { + pub fn peer_addr(&self) -> io::Result { self.io.get_ref().peer_addr() } @@ -193,7 +197,7 @@ impl UnixDatagram { } } -impl TryFrom for mio_uds::UnixDatagram { +impl TryFrom for mio::net::UnixDatagram { type Error = io::Error; /// Consumes value, returning the mio I/O object. diff --git a/tokio/src/net/unix/listener.rs b/tokio/src/net/unix/listener.rs index c7de68b1338..52b24b9d1ee 100644 --- a/tokio/src/net/unix/listener.rs +++ b/tokio/src/net/unix/listener.rs @@ -1,20 +1,19 @@ use crate::future::poll_fn; use crate::net::unix::{Incoming, UnixStream}; -use crate::net::util::PollEvented; +use crate::net::util::IoSource; -use mio::Ready; -use mio_uds; +use mio; use std::convert::TryFrom; use std::fmt; use std::io; use std::os::unix::io::{AsRawFd, RawFd}; -use std::os::unix::net::{self, SocketAddr}; +use std::os::unix::net; use std::path::Path; use std::task::{Context, Poll}; /// A Unix socket which can accept connections from other Unix sockets. pub struct UnixListener { - io: PollEvented, + io: IoSource, } impl UnixListener { @@ -23,8 +22,8 @@ impl UnixListener { where P: AsRef, { - let listener = mio_uds::UnixListener::bind(path)?; - let io = PollEvented::new(listener)?; + let listener = mio::net::UnixListener::bind(path)?; + let io = IoSource::new(listener)?; Ok(UnixListener { io }) } @@ -34,13 +33,13 @@ impl UnixListener { /// The returned listener will be associated with the given event loop /// specified by `handle` and is ready to perform I/O. pub fn from_std(listener: net::UnixListener) -> io::Result { - let listener = mio_uds::UnixListener::from_listener(listener)?; - let io = PollEvented::new(listener)?; + let listener = mio::net::UnixListener::from_std(listener); + let io = IoSource::new(listener)?; Ok(UnixListener { io }) } /// Returns the local socket address of this listener. - pub fn local_addr(&self) -> io::Result { + pub fn local_addr(&self) -> io::Result { self.io.get_ref().local_addr() } @@ -50,34 +49,23 @@ impl UnixListener { } /// Accepts a new incoming connection to this listener. - pub async fn accept(&mut self) -> io::Result<(UnixStream, SocketAddr)> { + pub async fn accept(&mut self) -> io::Result<(UnixStream, mio::unix::SocketAddr)> { poll_fn(|cx| self.poll_accept(cx)).await } pub(crate) fn poll_accept( &mut self, cx: &mut Context<'_>, - ) -> Poll> { - let (io, addr) = ready!(self.poll_accept_std(cx))?; + ) -> Poll> { + ready!(self.io.poll_read_ready(cx))?; - let io = mio_uds::UnixStream::from_stream(io)?; - Ok((UnixStream::new(io)?, addr)).into() - } - - fn poll_accept_std( - &mut self, - cx: &mut Context<'_>, - ) -> Poll> { - ready!(self.io.poll_read_ready(cx, Ready::readable()))?; - - match self.io.get_ref().accept_std() { - Ok(None) => { - self.io.clear_read_ready(cx, Ready::readable())?; - Poll::Pending + match self.io.get_ref().accept() { + Ok((stream, sockaddr)) => { + let stream = UnixStream::new(stream)?; + Ok((stream, sockaddr)).into() } - Ok(Some((sock, addr))) => Ok((sock, addr)).into(), Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } Err(err) => Err(err).into(), @@ -94,15 +82,15 @@ impl UnixListener { } } -impl TryFrom for mio_uds::UnixListener { +impl TryFrom for mio::net::UnixListener { type Error = io::Error; /// Consumes value, returning the mio I/O object. /// - /// See [`PollEvented::into_inner`] for more details about + /// See [`IoSource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`PollEvented::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoSource::into_inner`]: crate::util::PollEvented::into_inner fn try_from(value: UnixListener) -> Result { value.io.into_inner() } diff --git a/tokio/src/net/unix/stream.rs b/tokio/src/net/unix/stream.rs index 774d20f995f..3c7eb34519f 100644 --- a/tokio/src/net/unix/stream.rs +++ b/tokio/src/net/unix/stream.rs @@ -2,16 +2,15 @@ use crate::future::poll_fn; use crate::io::{AsyncRead, AsyncWrite}; use crate::net::unix::split::{split, ReadHalf, WriteHalf}; use crate::net::unix::ucred::{self, UCred}; -use crate::net::util::PollEvented; +use crate::net::util::IoSource; use bytes::{Buf, BufMut}; -use iovec::IoVec; use std::convert::TryFrom; use std::fmt; use std::io::{self, Read, Write}; use std::net::Shutdown; use std::os::unix::io::{AsRawFd, RawFd}; -use std::os::unix::net::{self, SocketAddr}; +use std::os::unix::net; use std::path::Path; use std::pin::Pin; use std::task::{Context, Poll}; @@ -22,7 +21,7 @@ use std::task::{Context, Poll}; /// from a listener with `UnixListener::incoming`. Additionally, a pair of /// anonymous Unix sockets can be created with `UnixStream::pair`. pub struct UnixStream { - io: PollEvented, + io: IoSource, } impl UnixStream { @@ -35,7 +34,7 @@ impl UnixStream { where P: AsRef, { - let stream = mio_uds::UnixStream::connect(path)?; + let stream = mio::net::UnixStream::connect(path)?; let stream = UnixStream::new(stream)?; poll_fn(|cx| stream.io.poll_write_ready(cx)).await?; @@ -48,8 +47,8 @@ impl UnixStream { /// The returned stream will be associated with the given event loop /// specified by `handle` and is ready to perform I/O. pub fn from_std(stream: net::UnixStream) -> io::Result { - let stream = mio_uds::UnixStream::from_stream(stream)?; - let io = PollEvented::new(stream)?; + let stream = mio::net::UnixStream::from_std(stream); + let io = IoSource::new(stream)?; Ok(UnixStream { io }) } @@ -60,25 +59,25 @@ impl UnixStream { /// communicating back and forth between one another. Each socket will /// be associated with the default event loop's handle. pub fn pair() -> io::Result<(UnixStream, UnixStream)> { - let (a, b) = mio_uds::UnixStream::pair()?; + let (a, b) = mio::net::UnixStream::pair()?; let a = UnixStream::new(a)?; let b = UnixStream::new(b)?; Ok((a, b)) } - pub(crate) fn new(stream: mio_uds::UnixStream) -> io::Result { - let io = PollEvented::new(stream)?; + pub(crate) fn new(stream: mio::net::UnixStream) -> io::Result { + let io = IoSource::new(stream)?; Ok(UnixStream { io }) } /// Returns the socket address of the local half of this connection. - pub fn local_addr(&self) -> io::Result { + pub fn local_addr(&self) -> io::Result { self.io.get_ref().local_addr() } /// Returns the socket address of the remote half of this connection. - pub fn peer_addr(&self) -> io::Result { + pub fn peer_addr(&self) -> io::Result { self.io.get_ref().peer_addr() } @@ -111,15 +110,15 @@ impl UnixStream { } } -impl TryFrom for mio_uds::UnixStream { +impl TryFrom for mio::net::UnixStream { type Error = io::Error; /// Consumes value, returning the mio I/O object. /// - /// See [`PollEvented::into_inner`] for more details about + /// See [`IoSource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`PollEvented::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoSource::into_inner`]: crate::util::PollEvented::into_inner fn try_from(value: UnixStream) -> Result { value.io.into_inner() } @@ -202,11 +201,11 @@ impl UnixStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; + ready!(self.io.poll_read_ready(cx))?; match self.io.get_ref().read(buf) { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } x => Poll::Ready(x), @@ -218,48 +217,48 @@ impl UnixStream { cx: &mut Context<'_>, buf: &mut B, ) -> Poll> { - ready!(self.io.poll_read_ready(cx, mio::Ready::readable()))?; + ready!(self.io.poll_read_ready(cx))?; let r = unsafe { // The `IoVec` type can't have a 0-length size, so we create a bunch // of dummy versions on the stack with 1 length which we'll quickly // overwrite. - let b1: &mut [u8] = &mut [0]; - let b2: &mut [u8] = &mut [0]; - let b3: &mut [u8] = &mut [0]; - let b4: &mut [u8] = &mut [0]; - let b5: &mut [u8] = &mut [0]; - let b6: &mut [u8] = &mut [0]; - let b7: &mut [u8] = &mut [0]; - let b8: &mut [u8] = &mut [0]; - let b9: &mut [u8] = &mut [0]; - let b10: &mut [u8] = &mut [0]; - let b11: &mut [u8] = &mut [0]; - let b12: &mut [u8] = &mut [0]; - let b13: &mut [u8] = &mut [0]; - let b14: &mut [u8] = &mut [0]; - let b15: &mut [u8] = &mut [0]; - let b16: &mut [u8] = &mut [0]; - let mut bufs: [&mut IoVec; 16] = [ - b1.into(), - b2.into(), - b3.into(), - b4.into(), - b5.into(), - b6.into(), - b7.into(), - b8.into(), - b9.into(), - b10.into(), - b11.into(), - b12.into(), - b13.into(), - b14.into(), - b15.into(), - b16.into(), - ]; - let n = buf.bytes_vec_mut(&mut bufs); - self.io.get_ref().read_bufs(&mut bufs[..n]) + // let b1: &mut [u8] = &mut [0]; + // let b2: &mut [u8] = &mut [0]; + // let b3: &mut [u8] = &mut [0]; + // let b4: &mut [u8] = &mut [0]; + // let b5: &mut [u8] = &mut [0]; + // let b6: &mut [u8] = &mut [0]; + // let b7: &mut [u8] = &mut [0]; + // let b8: &mut [u8] = &mut [0]; + // let b9: &mut [u8] = &mut [0]; + // let b10: &mut [u8] = &mut [0]; + // let b11: &mut [u8] = &mut [0]; + // let b12: &mut [u8] = &mut [0]; + // let b13: &mut [u8] = &mut [0]; + // let b14: &mut [u8] = &mut [0]; + // let b15: &mut [u8] = &mut [0]; + // let b16: &mut [u8] = &mut [0]; + // let mut bufs: [&mut IoVec; 16] = [ + // b1.into(), + // b2.into(), + // b3.into(), + // b4.into(), + // b5.into(), + // b6.into(), + // b7.into(), + // b8.into(), + // b9.into(), + // b10.into(), + // b11.into(), + // b12.into(), + // b13.into(), + // b14.into(), + // b15.into(), + // b16.into(), + // ]; + // let n = buf.bytes_vec_mut(&mut bufs); + self.io.get_ref().read(buf.bytes_mut()) }; match r { @@ -270,7 +269,7 @@ impl UnixStream { Poll::Ready(Ok(n)) } Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - self.io.clear_read_ready(cx, mio::Ready::readable())?; + self.io.clear_read_ready(cx)?; Poll::Pending } Err(e) => Poll::Ready(Err(e)), @@ -304,11 +303,11 @@ impl UnixStream { // The `IoVec` type can't have a zero-length size, so create a dummy // version from a 1-length slice which we'll overwrite with the // `bytes_vec` method. - static DUMMY: &[u8] = &[0]; - let iovec = <&IoVec>::from(DUMMY); - let mut bufs = [iovec; 64]; - let n = buf.bytes_vec(&mut bufs); - self.io.get_ref().write_bufs(&bufs[..n]) + // static DUMMY: &[u8] = &[0]; + // let iovec = <&IoVec>::from(DUMMY); + // let mut bufs = [iovec; 64]; + // let n = buf.bytes_vec(&mut bufs); + self.io.get_ref().write(buf.bytes()) }; match r { Ok(n) => { diff --git a/tokio/src/net/util/io_source.rs b/tokio/src/net/util/io_source.rs new file mode 100644 index 00000000000..c583d5ebad6 --- /dev/null +++ b/tokio/src/net/util/io_source.rs @@ -0,0 +1,198 @@ +use crate::io::{AsyncRead, AsyncWrite}; +use crate::net::driver::{Readiness, Registration}; + +use futures_core::ready; +use mio; +use std::fmt; +use std::io::{self, Read, Write}; +use std::marker::Unpin; +use std::pin::Pin; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::Relaxed; +use std::task::{Context, Poll}; + +pub struct IoSource { + io: Option, + inner: Inner, +} + +struct Inner { + registration: Registration, + + /// Currently visible read readiness + read_readiness: AtomicUsize, + + /// Currently visible write readiness + write_readiness: AtomicUsize, +} + +impl IoSource +where + S: mio::event::Source, +{ + pub fn new(io: S) -> io::Result { + unimplemented!() + } + + pub fn get_ref(&self) -> &S { + self.io.as_ref().unwrap() + } + + pub fn get_mut(&mut self) -> &mut S { + self.io.as_mut().unwrap() + } + + pub fn into_inner(mut self) -> io::Result { + let io = self.io.take().unwrap(); + self.inner.registration.deregister(&io)?; + Ok(io) + } + + pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + self.poll_ready( + cx, + Inner::get_read_readiness, + Registration::poll_read_ready, + Readiness::readable(), + ) + } + pub fn clear_read_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { + unimplemented!() + } + + pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + self.poll_ready( + cx, + Inner::get_write_readiness, + Registration::poll_write_ready, + Readiness::writable(), + ) + } + + pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { + unimplemented!() + } + + fn poll_ready( + &self, + cx: &mut Context<'_>, + cached_readiness: C, + poll_readiness: P, + readiness: Readiness, + ) -> Poll> + where + C: for<'r> FnOnce(&'r Inner) -> Readiness, + P: for<'r> Fn(&'r Registration, &mut Context<'_>) -> Poll>, + { + let cached = cached_readiness(&self.inner); + let readiness = cached & readiness; + + if readiness.is_empty() { + loop { + let ready = match poll_readiness(&self.inner.registration, cx) { + Poll::Ready(v) => v, + Poll::Pending => return Poll::Pending, + }; + } + } + + unimplemented!() + } +} + +impl Inner { + fn get_read_readiness(&self) -> Readiness { + let cached = self.read_readiness.load(Relaxed); + Readiness::from_usize(cached) + } + + fn get_write_readiness(&self) -> Readiness { + let cached = self.write_readiness.load(Relaxed); + Readiness::from_usize(cached) + } +} + +// ===== Read / Write impls ===== + +impl AsyncRead for IoSource +where + S: mio::event::Source + Read + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + ready!(self.poll_read_ready(cx))?; + + let r = (*self).get_mut().read(buf); + + if is_wouldblock(&r) { + self.clear_read_ready(cx)?; + return Poll::Pending; + } + + Poll::Ready(r) + } +} + +impl AsyncWrite for IoSource +where + S: mio::event::Source + Write + Unpin, +{ + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + ready!(self.poll_write_ready(cx))?; + + let r = (*self).get_mut().write(buf); + + if is_wouldblock(&r) { + self.clear_write_ready(cx)?; + return Poll::Pending; + } + + Poll::Ready(r) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + ready!(self.poll_write_ready(cx))?; + + let r = (*self).get_mut().flush(); + + if is_wouldblock(&r) { + self.clear_write_ready(cx)?; + return Poll::Pending; + } + + Poll::Ready(r) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +fn is_wouldblock(r: &io::Result) -> bool { + match *r { + Ok(_) => false, + Err(ref e) => e.kind() == io::ErrorKind::WouldBlock, + } +} + +impl fmt::Debug for IoSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Source").field("io", &self.io).finish() + } +} + +impl Drop for IoSource { + fn drop(&mut self) { + if let Some(io) = self.io.take() { + // Ignore errors + let _ = self.inner.registration.deregister(&io); + } + } +} diff --git a/tokio/src/net/util/mod.rs b/tokio/src/net/util/mod.rs index 1f46a53660f..bc9ef39c3ec 100644 --- a/tokio/src/net/util/mod.rs +++ b/tokio/src/net/util/mod.rs @@ -1,4 +1,4 @@ //! Utilities for implementing networking types. -mod poll_evented; -pub use self::poll_evented::PollEvented; +mod io_source; +pub use self::io_source::IoSource; diff --git a/tokio/src/net/util/poll_evented.rs b/tokio/src/net/util/poll_evented.rs index 08dea3f3856..8d55eae4152 100644 --- a/tokio/src/net/util/poll_evented.rs +++ b/tokio/src/net/util/poll_evented.rs @@ -1,412 +1,414 @@ -use crate::io::{AsyncRead, AsyncWrite}; -use crate::net::driver::{platform, Registration}; - -use mio::event::Evented; -use std::fmt; -use std::io::{self, Read, Write}; -use std::marker::Unpin; -use std::pin::Pin; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::Relaxed; -use std::task::{Context, Poll}; - -/// Associates an I/O resource that implements the [`std::io::Read`] and/or -/// [`std::io::Write`] traits with the reactor that drives it. -/// -/// `PollEvented` uses [`Registration`] internally to take a type that -/// implements [`mio::Evented`] as well as [`std::io::Read`] and or -/// [`std::io::Write`] and associate it with a reactor that will drive it. -/// -/// Once the [`mio::Evented`] type is wrapped by `PollEvented`, it can be -/// used from within the future's execution model. As such, the `PollEvented` -/// type provides [`AsyncRead`] and [`AsyncWrite`] implementations using the -/// underlying I/O resource as well as readiness events provided by the reactor. -/// -/// **Note**: While `PollEvented` is `Sync` (if the underlying I/O type is -/// `Sync`), the caller must ensure that there are at most two tasks that use a -/// `PollEvented` instance concurrently. One for reading and one for writing. -/// While violating this requirement is "safe" from a Rust memory model point of -/// view, it will result in unexpected behavior in the form of lost -/// notifications and tasks hanging. -/// -/// ## Readiness events -/// -/// Besides just providing [`AsyncRead`] and [`AsyncWrite`] implementations, -/// this type also supports access to the underlying readiness event stream. -/// While similar in function to what [`Registration`] provides, the semantics -/// are a bit different. -/// -/// Two functions are provided to access the readiness events: -/// [`poll_read_ready`] and [`poll_write_ready`]. These functions return the -/// current readiness state of the `PollEvented` instance. If -/// [`poll_read_ready`] indicates read readiness, immediately calling -/// [`poll_read_ready`] again will also indicate read readiness. -/// -/// When the operation is attempted and is unable to succeed due to the I/O -/// resource not being ready, the caller must call [`clear_read_ready`] or -/// [`clear_write_ready`]. This clears the readiness state until a new readiness -/// event is received. -/// -/// This allows the caller to implement additional functions. For example, -/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and -/// [`clear_read_ready`]. -/// -/// ```rust -/// use tokio::net::util::PollEvented; -/// -/// use futures::ready; -/// use mio::Ready; -/// use mio::net::{TcpStream, TcpListener}; -/// use std::io; -/// use std::task::{Context, Poll}; -/// -/// struct MyListener { -/// poll_evented: PollEvented, -/// } -/// -/// impl MyListener { -/// pub fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { -/// let ready = Ready::readable(); -/// -/// ready!(self.poll_evented.poll_read_ready(cx, ready))?; -/// -/// match self.poll_evented.get_ref().accept() { -/// Ok((socket, _)) => Poll::Ready(Ok(socket)), -/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { -/// self.poll_evented.clear_read_ready(cx, ready)?; -/// Poll::Pending -/// } -/// Err(e) => Poll::Ready(Err(e)), -/// } -/// } -/// } -/// ``` -/// -/// ## Platform-specific events -/// -/// `PollEvented` also allows receiving platform-specific `mio::Ready` events. -/// These events are included as part of the read readiness event stream. The -/// write readiness event stream is only for `Ready::writable()` events. -/// -/// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html -/// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html -/// [`AsyncRead`]: ../io/trait.AsyncRead.html -/// [`AsyncWrite`]: ../io/trait.AsyncWrite.html -/// [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html -/// [`Registration`]: struct.Registration.html -/// [`TcpListener`]: ../net/struct.TcpListener.html -/// [`clear_read_ready`]: #method.clear_read_ready -/// [`clear_write_ready`]: #method.clear_write_ready -/// [`poll_read_ready`]: #method.poll_read_ready -/// [`poll_write_ready`]: #method.poll_write_ready -pub struct PollEvented { - io: Option, - inner: Inner, -} - -struct Inner { - registration: Registration, - - /// Currently visible read readiness - read_readiness: AtomicUsize, - - /// Currently visible write readiness - write_readiness: AtomicUsize, -} - -// ===== impl PollEvented ===== - -macro_rules! poll_ready { - ($me:expr, $mask:expr, $cache:ident, $take:ident, $poll:expr) => {{ - // Load cached & encoded readiness. - let mut cached = $me.inner.$cache.load(Relaxed); - let mask = $mask | platform::hup(); - - // See if the current readiness matches any bits. - let mut ret = mio::Ready::from_usize(cached) & $mask; - - if ret.is_empty() { - // Readiness does not match, consume the registration's readiness - // stream. This happens in a loop to ensure that the stream gets - // drained. - loop { - let ready = match $poll? { - Poll::Ready(v) => v, - Poll::Pending => return Poll::Pending, - }; - cached |= ready.as_usize(); - - // Update the cache store - $me.inner.$cache.store(cached, Relaxed); - - ret |= ready & mask; - - if !ret.is_empty() { - return Poll::Ready(Ok(ret)); - } - } - } else { - // Check what's new with the registration stream. This will not - // request to be notified - if let Some(ready) = $me.inner.registration.$take()? { - cached |= ready.as_usize(); - $me.inner.$cache.store(cached, Relaxed); - } - - Poll::Ready(Ok(mio::Ready::from_usize(cached))) - } - }}; -} - -impl PollEvented -where - E: Evented, -{ - /// Creates a new `PollEvented` associated with the default reactor. - pub fn new(io: E) -> io::Result { - let registration = Registration::new(&io)?; - Ok(Self { - io: Some(io), - inner: Inner { - registration, - read_readiness: AtomicUsize::new(0), - write_readiness: AtomicUsize::new(0), - }, - }) - } - - /// Returns a shared reference to the underlying I/O object this readiness - /// stream is wrapping. - pub fn get_ref(&self) -> &E { - self.io.as_ref().unwrap() - } - - /// Returns a mutable reference to the underlying I/O object this readiness - /// stream is wrapping. - pub fn get_mut(&mut self) -> &mut E { - self.io.as_mut().unwrap() - } - - /// Consumes self, returning the inner I/O object - /// - /// This function will deregister the I/O resource from the reactor before - /// returning. If the deregistration operation fails, an error is returned. - /// - /// Note that deregistering does not guarantee that the I/O resource can be - /// registered with a different reactor. Some I/O resource types can only be - /// associated with a single reactor instance for their lifetime. - pub fn into_inner(mut self) -> io::Result { - let io = self.io.take().unwrap(); - self.inner.registration.deregister(&io)?; - Ok(io) - } - - /// Check the I/O resource's read readiness state. - /// - /// The mask argument allows specifying what readiness to notify on. This - /// can be any value, including platform specific readiness, **except** - /// `writable`. HUP is always implicitly included on platforms that support - /// it. - /// - /// If the resource is not ready for a read then `Poll::Pending` is returned - /// and the current task is notified once a new event is received. - /// - /// The I/O resource will remain in a read-ready state until readiness is - /// cleared by calling [`clear_read_ready`]. - /// - /// [`clear_read_ready`]: #method.clear_read_ready - /// - /// # Panics - /// - /// This function panics if: - /// - /// * `ready` includes writable. - /// * called from outside of a task context. - pub fn poll_read_ready( - &self, - cx: &mut Context<'_>, - mask: mio::Ready, - ) -> Poll> { - assert!(!mask.is_writable(), "cannot poll for write readiness"); - poll_ready!( - self, - mask, - read_readiness, - take_read_ready, - self.inner.registration.poll_read_ready(cx) - ) - } - - /// Clears the I/O resource's read readiness state and registers the current - /// task to be notified once a read readiness event is received. - /// - /// After calling this function, `poll_read_ready` will return - /// `Poll::Pending` until a new read readiness event has been received. - /// - /// The `mask` argument specifies the readiness bits to clear. This may not - /// include `writable` or `hup`. - /// - /// # Panics - /// - /// This function panics if: - /// - /// * `ready` includes writable or HUP - /// * called from outside of a task context. - pub fn clear_read_ready(&self, cx: &mut Context<'_>, ready: mio::Ready) -> io::Result<()> { - // Cannot clear write readiness - assert!(!ready.is_writable(), "cannot clear write readiness"); - assert!(!platform::is_hup(ready), "cannot clear HUP readiness"); - - self.inner - .read_readiness - .fetch_and(!ready.as_usize(), Relaxed); - - if self.poll_read_ready(cx, ready)?.is_ready() { - // Notify the current task - cx.waker().wake_by_ref(); - } - - Ok(()) - } - - /// Check the I/O resource's write readiness state. - /// - /// This always checks for writable readiness and also checks for HUP - /// readiness on platforms that support it. - /// - /// If the resource is not ready for a write then `Async::NotReady` is - /// returned and the current task is notified once a new event is received. - /// - /// The I/O resource will remain in a write-ready state until readiness is - /// cleared by calling [`clear_write_ready`]. - /// - /// [`clear_write_ready`]: #method.clear_write_ready - /// - /// # Panics - /// - /// This function panics if: - /// - /// * `ready` contains bits besides `writable` and `hup`. - /// * called from outside of a task context. - pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - poll_ready!( - self, - mio::Ready::writable(), - write_readiness, - take_write_ready, - self.inner.registration.poll_write_ready(cx) - ) - } - - /// Resets the I/O resource's write readiness state and registers the current - /// task to be notified once a write readiness event is received. - /// - /// This only clears writable readiness. HUP (on platforms that support HUP) - /// cannot be cleared as it is a final state. - /// - /// After calling this function, `poll_write_ready(Ready::writable())` will - /// return `NotReady` until a new write readiness event has been received. - /// - /// # Panics - /// - /// This function will panic if called from outside of a task context. - pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { - let ready = mio::Ready::writable(); - - self.inner - .write_readiness - .fetch_and(!ready.as_usize(), Relaxed); - - if self.poll_write_ready(cx)?.is_ready() { - // Notify the current task - cx.waker().wake_by_ref(); - } - - Ok(()) - } -} - -// ===== Read / Write impls ===== - -impl AsyncRead for PollEvented -where - E: Evented + Read + Unpin, -{ - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - ready!(self.poll_read_ready(cx, mio::Ready::readable()))?; - - let r = (*self).get_mut().read(buf); - - if is_wouldblock(&r) { - self.clear_read_ready(cx, mio::Ready::readable())?; - return Poll::Pending; - } - - Poll::Ready(r) - } -} - -impl AsyncWrite for PollEvented -where - E: Evented + Write + Unpin, -{ - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - ready!(self.poll_write_ready(cx))?; - - let r = (*self).get_mut().write(buf); - - if is_wouldblock(&r) { - self.clear_write_ready(cx)?; - return Poll::Pending; - } - - Poll::Ready(r) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - ready!(self.poll_write_ready(cx))?; - - let r = (*self).get_mut().flush(); - - if is_wouldblock(&r) { - self.clear_write_ready(cx)?; - return Poll::Pending; - } - - Poll::Ready(r) - } - - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -fn is_wouldblock(r: &io::Result) -> bool { - match *r { - Ok(_) => false, - Err(ref e) => e.kind() == io::ErrorKind::WouldBlock, - } -} - -impl fmt::Debug for PollEvented { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PollEvented").field("io", &self.io).finish() - } -} - -impl Drop for PollEvented { - fn drop(&mut self) { - if let Some(io) = self.io.take() { - // Ignore errors - let _ = self.inner.registration.deregister(&io); - } - } -} +// use crate::io::{AsyncRead, AsyncWrite}; +// use crate::net::driver::{platform, Registration}; + +// use futures_core::ready; +// use mio::event::Evented; +// use std::fmt; +// use std::io::{self, Read, Write}; +// use std::marker::Unpin; +// use std::pin::Pin; +// use std::sync::atomic::AtomicUsize; +// use std::sync::atomic::Ordering::Relaxed; +// use std::task::{Context, Poll}; + +// /// Associates an I/O resource that implements the [`std::io::Read`] and/or +// /// [`std::io::Write`] traits with the reactor that drives it. +// /// +// /// `PollEvented` uses [`Registration`] internally to take a type that +// /// implements [`mio::Evented`] as well as [`std::io::Read`] and or +// /// [`std::io::Write`] and associate it with a reactor that will drive it. +// /// +// /// Once the [`mio::Evented`] type is wrapped by `PollEvented`, it can be +// /// used from within the future's execution model. As such, the `PollEvented` +// /// type provides [`AsyncRead`] and [`AsyncWrite`] implementations using the +// /// underlying I/O resource as well as readiness events provided by the reactor. +// /// +// /// **Note**: While `PollEvented` is `Sync` (if the underlying I/O type is +// /// `Sync`), the caller must ensure that there are at most two tasks that use a +// /// `PollEvented` instance concurrently. One for reading and one for writing. +// /// While violating this requirement is "safe" from a Rust memory model point of +// /// view, it will result in unexpected behavior in the form of lost +// /// notifications and tasks hanging. +// /// +// /// ## Readiness events +// /// +// /// Besides just providing [`AsyncRead`] and [`AsyncWrite`] implementations, +// /// this type also supports access to the underlying readiness event stream. +// /// While similar in function to what [`Registration`] provides, the semantics +// /// are a bit different. +// /// +// /// Two functions are provided to access the readiness events: +// /// [`poll_read_ready`] and [`poll_write_ready`]. These functions return the +// /// current readiness state of the `PollEvented` instance. If +// /// [`poll_read_ready`] indicates read readiness, immediately calling +// /// [`poll_read_ready`] again will also indicate read readiness. +// /// +// /// When the operation is attempted and is unable to succeed due to the I/O +// /// resource not being ready, the caller must call [`clear_read_ready`] or +// /// [`clear_write_ready`]. This clears the readiness state until a new readiness +// /// event is received. +// /// +// /// This allows the caller to implement additional functions. For example, +// /// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and +// /// [`clear_read_ready`]. +// /// +// /// ```rust +// /// use tokio::net::util::PollEvented; +// /// +// /// use futures_core::ready; +// /// use mio::Ready; +// /// use mio::net::{TcpStream, TcpListener}; +// /// use std::io; +// /// use std::task::{Context, Poll}; +// /// +// /// struct MyListener { +// /// poll_evented: PollEvented, +// /// } +// /// +// /// impl MyListener { +// /// pub fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { +// /// let ready = Ready::readable(); +// /// +// /// ready!(self.poll_evented.poll_read_ready(cx, ready))?; +// /// +// /// match self.poll_evented.get_ref().accept() { +// /// Ok((socket, _)) => Poll::Ready(Ok(socket)), +// /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { +// /// self.poll_evented.clear_read_ready(cx, ready)?; +// /// Poll::Pending +// /// } +// /// Err(e) => Poll::Ready(Err(e)), +// /// } +// /// } +// /// } +// /// ``` +// /// +// /// ## Platform-specific events +// /// +// /// `PollEvented` also allows receiving platform-specific `mio::Ready` events. +// /// These events are included as part of the read readiness event stream. The +// /// write readiness event stream is only for `Ready::writable()` events. +// /// +// /// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html +// /// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +// /// [`AsyncRead`]: ../io/trait.AsyncRead.html +// /// [`AsyncWrite`]: ../io/trait.AsyncWrite.html +// /// [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html +// /// [`Registration`]: struct.Registration.html +// /// [`TcpListener`]: ../net/struct.TcpListener.html +// /// [`clear_read_ready`]: #method.clear_read_ready +// /// [`clear_write_ready`]: #method.clear_write_ready +// /// [`poll_read_ready`]: #method.poll_read_ready +// /// [`poll_write_ready`]: #method.poll_write_ready +// pub struct PollEvented { +// io: Option, +// inner: Inner, +// } + +// struct Inner { +// registration: Registration, + +// /// Currently visible read readiness +// read_readiness: AtomicUsize, + +// /// Currently visible write readiness +// write_readiness: AtomicUsize, +// } + +// // ===== impl PollEvented ===== + +// macro_rules! poll_ready { +// ($me:expr, $mask:expr, $cache:ident, $take:ident, $poll:expr) => {{ +// // Load cached & encoded readiness. +// let mut cached = $me.inner.$cache.load(Relaxed); +// // let mask = $mask | platform::hup(); +// let mask = $mask; + +// // See if the current readiness matches any bits. +// let mut ret = mio::Ready::from_usize(cached) & $mask; + +// if ret.is_empty() { +// // Readiness does not match, consume the registration's readiness +// // stream. This happens in a loop to ensure that the stream gets +// // drained. +// loop { +// let ready = match $poll? { +// Poll::Ready(v) => v, +// Poll::Pending => return Poll::Pending, +// }; +// cached |= ready.as_usize(); + +// // Update the cache store +// $me.inner.$cache.store(cached, Relaxed); + +// ret |= ready & mask; + +// if !ret.is_empty() { +// return Poll::Ready(Ok(ret)); +// } +// } +// } else { +// // Check what's new with the registration stream. This will not +// // request to be notified +// if let Some(ready) = $me.inner.registration.$take()? { +// cached |= ready.as_usize(); +// $me.inner.$cache.store(cached, Relaxed); +// } + +// Poll::Ready(Ok(mio::Ready::from_usize(cached))) +// } +// }}; +// } + +// impl PollEvented +// where +// E: Evented, +// { +// /// Creates a new `PollEvented` associated with the default reactor. +// pub fn new(io: E) -> io::Result { +// let registration = Registration::new(&io)?; +// Ok(Self { +// io: Some(io), +// inner: Inner { +// registration, +// read_readiness: AtomicUsize::new(0), +// write_readiness: AtomicUsize::new(0), +// }, +// }) +// } + +// /// Returns a shared reference to the underlying I/O object this readiness +// /// stream is wrapping. +// pub fn get_ref(&self) -> &E { +// self.io.as_ref().unwrap() +// } + +// /// Returns a mutable reference to the underlying I/O object this readiness +// /// stream is wrapping. +// pub fn get_mut(&mut self) -> &mut E { +// self.io.as_mut().unwrap() +// } + +// /// Consumes self, returning the inner I/O object +// /// +// /// This function will deregister the I/O resource from the reactor before +// /// returning. If the deregistration operation fails, an error is returned. +// /// +// /// Note that deregistering does not guarantee that the I/O resource can be +// /// registered with a different reactor. Some I/O resource types can only be +// /// associated with a single reactor instance for their lifetime. +// pub fn into_inner(mut self) -> io::Result { +// let io = self.io.take().unwrap(); +// self.inner.registration.deregister(&io)?; +// Ok(io) +// } + +// /// Check the I/O resource's read readiness state. +// /// +// /// The mask argument allows specifying what readiness to notify on. This +// /// can be any value, including platform specific readiness, **except** +// /// `writable`. HUP is always implicitly included on platforms that support +// /// it. +// /// +// /// If the resource is not ready for a read then `Poll::Pending` is returned +// /// and the current task is notified once a new event is received. +// /// +// /// The I/O resource will remain in a read-ready state until readiness is +// /// cleared by calling [`clear_read_ready`]. +// /// +// /// [`clear_read_ready`]: #method.clear_read_ready +// /// +// /// # Panics +// /// +// /// This function panics if: +// /// +// /// * `ready` includes writable. +// /// * called from outside of a task co ntext. +// pub fn poll_read_ready( +// &self, +// cx: &mut Context<'_>, +// mask: mio::Ready, +// ) -> Poll> { +// assert!(!mask.is_writable(), "cannot poll for write readiness"); +// poll_ready!( +// self, +// mask, +// read_readiness, +// take_read_ready, +// self.inner.registration.poll_read_ready(cx) +// ) +// } + +// /// Clears the I/O resource's read readiness state and registers the current +// /// task to be notified once a read readiness event is received. +// /// +// /// After calling this function, `poll_read_ready` will return +// /// `Poll::Pending` until a new read readiness event has been received. +// /// +// /// The `mask` argument specifies the readiness bits to clear. This may not +// /// include `writable` or `hup`. +// /// +// /// # Panics +// /// +// /// This function panics if: +// /// +// /// * `ready` includes writable or HUP +// /// * called from outside of a task context. +// pub fn clear_read_ready(&self, cx: &mut Context<'_>, ready: mio::Ready) -> io::Result<()> { +// // Cannot clear write readiness +// assert!(!ready.is_writable(), "cannot clear write readiness"); +// assert!(!platform::is_hup(ready), "cannot clear HUP readiness"); + +// self.inner +// .read_readiness +// .fetch_and(!ready.as_usize(), Relaxed); + +// if self.poll_read_ready(cx, ready)?.is_ready() { +// // Notify the current task +// cx.waker().wake_by_ref(); +// } + +// Ok(()) +// } + +// /// Check the I/O resource's write readiness state. +// /// +// /// This always checks for writable readiness and also checks for HUP +// /// readiness on platforms that support it. +// /// +// /// If the resource is not ready for a write then `Async::NotReady` is +// /// returned and the current task is notified once a new event is received. +// /// +// /// The I/O resource will remain in a write-ready state until readiness is +// /// cleared by calling [`clear_write_ready`]. +// /// +// /// [`clear_write_ready`]: #method.clear_write_ready +// /// +// /// # Panics +// /// +// /// This function panics if: +// /// +// /// * `ready` contains bits besides `writable` and `hup`. +// /// * called from outside of a task context. +// pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { +// poll_ready!( +// self, +// mio::Ready::writable(), +// write_readiness, +// take_write_ready, +// self.inner.registration.poll_write_ready(cx) +// ) +// } + +// /// Resets the I/O resource's write readiness state and registers the current +// /// task to be notified once a write readiness event is received. +// /// +// /// This only clears writable readiness. HUP (on platforms that support HUP) +// /// cannot be cleared as it is a final state. +// /// +// /// After calling this function, `poll_write_ready(Ready::writable())` will +// /// return `NotReady` until a new write readiness event has been received. +// /// +// /// # Panics +// /// +// /// This function will panic if called from outside of a task context. +// pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { +// let ready = mio::Ready::writable(); + +// self.inner +// .write_readiness +// .fetch_and(!ready.as_usize(), Relaxed); + +// if self.poll_write_ready(cx)?.is_ready() { +// // Notify the current task +// cx.waker().wake_by_ref(); +// } + +// Ok(()) +// } +// } + +// // ===== Read / Write impls ===== + +// impl AsyncRead for PollEvented +// where +// E: Evented + Read + Unpin, +// { +// fn poll_read( +// mut self: Pin<&mut Self>, +// cx: &mut Context<'_>, +// buf: &mut [u8], +// ) -> Poll> { +// ready!(self.poll_read_ready(cx, mio::Ready::readable()))?; + +// let r = (*self).get_mut().read(buf); + +// if is_wouldblock(&r) { +// self.clear_read_ready(cx, mio::Ready::readable())?; +// return Poll::Pending; +// } + +// Poll::Ready(r) +// } +// } + +// impl AsyncWrite for PollEvented +// where +// E: Evented + Write + Unpin, +// { +// fn poll_write( +// mut self: Pin<&mut Self>, +// cx: &mut Context<'_>, +// buf: &[u8], +// ) -> Poll> { +// ready!(self.poll_write_ready(cx))?; + +// let r = (*self).get_mut().write(buf); + +// if is_wouldblock(&r) { +// self.clear_write_ready(cx)?; +// return Poll::Pending; +// } + +// Poll::Ready(r) +// } + +// fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { +// ready!(self.poll_write_ready(cx))?; + +// let r = (*self).get_mut().flush(); + +// if is_wouldblock(&r) { +// self.clear_write_ready(cx)?; +// return Poll::Pending; +// } + +// Poll::Ready(r) +// } + +// fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { +// Poll::Ready(Ok(())) +// } +// } + +// fn is_wouldblock(r: &io::Result) -> bool { +// match *r { +// Ok(_) => false, +// Err(ref e) => e.kind() == io::ErrorKind::WouldBlock, +// } +// } + +// impl fmt::Debug for PollEvented { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// f.debug_struct("PollEvented").field("io", &self.io).finish() +// } +// } + +// impl Drop for PollEvented { +// fn drop(&mut self) { +// if let Some(io) = self.io.take() { +// // Ignore errors +// let _ = self.inner.registration.deregister(&io); +// } +// } +// } diff --git a/tokio/src/process/unix/mod.rs b/tokio/src/process/unix/mod.rs index 72f6f0bfd7a..38386069d90 100644 --- a/tokio/src/process/unix/mod.rs +++ b/tokio/src/process/unix/mod.rs @@ -27,14 +27,14 @@ use orphan::{OrphanQueue, OrphanQueueImpl, Wait}; mod reap; use reap::Reaper; -use crate::net::util::PollEvented; +use crate::net::util::IoSource; use crate::process::kill::Kill; use crate::process::SpawnedChild; use crate::signal::unix::{signal, Signal, SignalKind}; -use mio::event::Evented; -use mio::unix::{EventedFd, UnixReady}; -use mio::{Poll as MioPoll, PollOpt, Ready, Token}; +use mio::event::Source; +use mio::unix::SourceFd; +use mio::{Interests, Registry, Token}; use std::fmt; use std::future::Future; use std::io; @@ -169,40 +169,30 @@ where } } -impl Evented for Fd +impl Source for Fd where T: AsRawFd, { - fn register( - &self, - poll: &MioPoll, - token: Token, - interest: Ready, - opts: PollOpt, - ) -> io::Result<()> { - EventedFd(&self.as_raw_fd()).register(poll, token, interest | UnixReady::hup(), opts) + // fn register(&self, poll: &MioPoll, token: Token, interest: Interests) -> io::Result<()> { + fn register(&self, registry: &Registry, token: Token, interest: Interests) -> io::Result<()> { + // SourceFd(&self.as_raw_fd()).register(poll, token, interest | UnixReady::hup(), opts) + SourceFd(&self.as_raw_fd()).register(registry, token, interest) } - fn reregister( - &self, - poll: &MioPoll, - token: Token, - interest: Ready, - opts: PollOpt, - ) -> io::Result<()> { - EventedFd(&self.as_raw_fd()).reregister(poll, token, interest | UnixReady::hup(), opts) + fn reregister(&self, registry: &Registry, token: Token, interest: Interests) -> io::Result<()> { + SourceFd(&self.as_raw_fd()).reregister(registry, token, interest) } - fn deregister(&self, poll: &MioPoll) -> io::Result<()> { - EventedFd(&self.as_raw_fd()).deregister(poll) + fn deregister(&self, registry: &Registry) -> io::Result<()> { + SourceFd(&self.as_raw_fd()).deregister(registry) } } -pub(crate) type ChildStdin = PollEvented>; -pub(crate) type ChildStdout = PollEvented>; -pub(crate) type ChildStderr = PollEvented>; +pub(crate) type ChildStdin = IoSource>; +pub(crate) type ChildStdout = IoSource>; +pub(crate) type ChildStderr = IoSource>; -fn stdio(option: Option) -> io::Result>>> +fn stdio(option: Option) -> io::Result>>> where T: AsRawFd, { @@ -223,5 +213,5 @@ where return Err(io::Error::last_os_error()); } } - Ok(Some(PollEvented::new(Fd { inner: io })?)) + Ok(Some(IoSource::new(Fd { inner: io })?)) } diff --git a/tokio/src/signal/unix.rs b/tokio/src/signal/unix.rs index 8551e85cecd..f25c4314d2b 100644 --- a/tokio/src/signal/unix.rs +++ b/tokio/src/signal/unix.rs @@ -6,12 +6,12 @@ #![cfg(unix)] use crate::io::AsyncRead; -use crate::net::util::PollEvented; +use crate::net::util::IoSource; use crate::signal::registry::{globals, EventId, EventInfo, Globals, Init, Storage}; use crate::sync::mpsc::{channel, Receiver}; use libc::c_int; -use mio_uds::UnixStream; +use mio::net::UnixStream; use std::io::{self, Error, ErrorKind, Write}; use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; @@ -257,7 +257,7 @@ fn signal_enable(signal: c_int) -> io::Result<()> { #[derive(Debug)] struct Driver { - wakeup: PollEvented, + wakeup: IoSource, } impl Driver { @@ -281,13 +281,13 @@ impl Driver { // I'm not sure if the second (failed) registration simply doesn't end up // receiving wake up notifications, or there could be some race condition // when consuming readiness events, but having distinct descriptors for - // distinct PollEvented instances appears to mitigate this. + // distinct IoSource instances appears to mitigate this. // - // Unfortunately we cannot just use a single global PollEvented instance + // Unfortunately we cannot just use a single global IoSource instance // either, since we can't compare Handles or assume they will always // point to the exact same reactor. let stream = globals().receiver.try_clone()?; - let wakeup = PollEvented::new(stream)?; + let wakeup = IoSource::new(stream)?; Ok(Driver { wakeup }) } From f4cd3448ca3d29b3dd4b9a21750ea181069194a5 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Wed, 6 Nov 2019 20:24:15 -0800 Subject: [PATCH 02/21] Tests passing; lots of cleanup Signed-off-by: Kevin Leimkuhler --- tokio/src/net/driver/reactor/mod.rs | 18 ++-- tokio/src/net/util/io_source.rs | 136 +++++++++++++++++++--------- 2 files changed, 104 insertions(+), 50 deletions(-) diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index 16439eb18f2..710e17ec7b7 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -29,6 +29,9 @@ pub struct Reactor { /// Reuse the `mio::Events` value across calls to poll. events: mio::Events, + /// TODO + poll: mio::Poll, + /// State shared between the reactor and the handles. inner: Arc, // Is this still needed? @@ -56,7 +59,7 @@ pub struct Turn { pub(super) struct Inner { /// The underlying system event queue. - poll: mio::Poll, + registry: mio::Registry, /// Dispatch slabs for I/O and futures events // TODO(eliza): once worker threads are available, replace this with a @@ -142,10 +145,13 @@ impl Reactor { // mio::PollOpt::level(), // )?; + let registry = poll.registry().try_clone()?; + Ok(Reactor { events: mio::Events::with_capacity(1024), + poll, inner: Arc::new(Inner { - poll, + registry, io_dispatch: SingleShard::new(), n_sources: AtomicUsize::new(0), wakeup: waker, @@ -207,7 +213,7 @@ impl Reactor { fn poll(&mut self, max_wait: Option) -> io::Result<()> { // Block waiting for an event to happen, peeling out how many events // happened. - match self.inner.poll.poll(&mut self.events, max_wait) { + match self.poll.poll(&mut self.events, max_wait) { Ok(_) => {} Err(e) => return Err(e), } @@ -287,7 +293,7 @@ impl Reactor { #[cfg(all(unix, not(target_os = "fuchsia")))] impl AsRawFd for Reactor { fn as_raw_fd(&self) -> RawFd { - self.inner.poll.as_raw_fd() + self.poll.as_raw_fd() } } @@ -384,7 +390,7 @@ impl Inner { // mio::Ready::all(), // mio::PollOpt::edge(), // )?; - self.poll.registry().register( + self.registry.register( source, mio::Token(token), mio::Interests::READABLE | mio::Interests::WRITABLE, @@ -395,7 +401,7 @@ impl Inner { /// Deregisters an I/O resource from the reactor. pub(super) fn deregister_source(&self, source: &dyn mio::event::Source) -> io::Result<()> { - self.poll.registry().deregister(source) + self.registry.deregister(source) } pub(super) fn drop_source(&self, token: usize) { diff --git a/tokio/src/net/util/io_source.rs b/tokio/src/net/util/io_source.rs index c583d5ebad6..2a14e63c96a 100644 --- a/tokio/src/net/util/io_source.rs +++ b/tokio/src/net/util/io_source.rs @@ -11,6 +11,7 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::Relaxed; use std::task::{Context, Poll}; +/// TODO pub struct IoSource { io: Option, inner: Inner, @@ -30,85 +31,132 @@ impl IoSource where S: mio::event::Source, { + /// TODO pub fn new(io: S) -> io::Result { - unimplemented!() - } - + let registration = Registration::new(&io)?; + Ok(Self { + io: Some(io), + inner: Inner { + registration, + read_readiness: AtomicUsize::default(), + write_readiness: AtomicUsize::default(), + }, + }) + } + + /// TODO pub fn get_ref(&self) -> &S { self.io.as_ref().unwrap() } + /// TODO pub fn get_mut(&mut self) -> &mut S { self.io.as_mut().unwrap() } + /// TODO pub fn into_inner(mut self) -> io::Result { let io = self.io.take().unwrap(); self.inner.registration.deregister(&io)?; Ok(io) } + /// TODO pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - self.poll_ready( - cx, - Inner::get_read_readiness, - Registration::poll_read_ready, - Readiness::readable(), - ) + self.inner.poll_read_ready(cx) } + + /// TODO pub fn clear_read_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { - unimplemented!() + self.inner + .read_readiness + .fetch_and(!Readiness::readable().as_usize(), Relaxed); + if self.poll_read_ready(cx)?.is_ready() { + // Notify the current task + cx.waker().wake_by_ref(); + } + Ok(()) } + /// TODO pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - self.poll_ready( - cx, - Inner::get_write_readiness, - Registration::poll_write_ready, - Readiness::writable(), - ) + self.inner.poll_write_ready(cx) } + /// TODO pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { - unimplemented!() + self.inner + .read_readiness + .fetch_and(!Readiness::writable().as_usize(), Relaxed); + if self.poll_write_ready(cx)?.is_ready() { + // Notify the current task + cx.waker().wake_by_ref(); + } + Ok(()) } +} - fn poll_ready( - &self, - cx: &mut Context<'_>, - cached_readiness: C, - poll_readiness: P, - readiness: Readiness, - ) -> Poll> - where - C: for<'r> FnOnce(&'r Inner) -> Readiness, - P: for<'r> Fn(&'r Registration, &mut Context<'_>) -> Poll>, - { - let cached = cached_readiness(&self.inner); - let readiness = cached & readiness; - - if readiness.is_empty() { +impl Inner { + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + // Load cached readiness and see if it matches any bits + let mut cached = self.read_readiness.load(Relaxed); + let mut current = Readiness::from_usize(cached) & Readiness::readable(); + + if current.is_empty() { loop { - let ready = match poll_readiness(&self.inner.registration, cx) { + let readiness = match self.registration.poll_read_ready(cx)? { Poll::Ready(v) => v, Poll::Pending => return Poll::Pending, }; + cached |= readiness.as_usize(); + + self.read_readiness.store(cached, Relaxed); + + current |= readiness; + + if !current.is_empty() { + return Poll::Ready(Ok(current)); + } + } + } else { + if let Some(readiness) = self.registration.take_read_ready()? { + cached |= readiness.as_usize(); + self.read_readiness.store(cached, Relaxed); } - } - unimplemented!() + Poll::Ready(Ok(Readiness::from_usize(cached))) + } } -} -impl Inner { - fn get_read_readiness(&self) -> Readiness { - let cached = self.read_readiness.load(Relaxed); - Readiness::from_usize(cached) - } + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + // Load cached readiness and see if it matches any bits + let mut cached = self.write_readiness.load(Relaxed); + let mut current = Readiness::from_usize(cached) & Readiness::writable(); + + if current.is_empty() { + loop { + let readiness = match self.registration.poll_write_ready(cx)? { + Poll::Ready(v) => v, + Poll::Pending => return Poll::Pending, + }; + cached |= readiness.as_usize(); + + self.write_readiness.store(cached, Relaxed); - fn get_write_readiness(&self) -> Readiness { - let cached = self.write_readiness.load(Relaxed); - Readiness::from_usize(cached) + current |= readiness; + + if !current.is_empty() { + return Poll::Ready(Ok(current)); + } + } + } else { + if let Some(readiness) = self.registration.take_write_ready()? { + cached |= readiness.as_usize(); + self.write_readiness.store(cached, Relaxed); + } + + Poll::Ready(Ok(Readiness::from_usize(cached))) + } } } From 4f912f11d21ee91a755224c5069df70ee40f1f21 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Wed, 6 Nov 2019 21:42:17 -0800 Subject: [PATCH 03/21] Documentation and cleanup needed Signed-off-by: Kevin Leimkuhler --- tokio/src/net/driver/readiness.rs | 53 ++++----- tokio/src/net/util/io_source.rs | 177 ++++++++++++++---------------- 2 files changed, 103 insertions(+), 127 deletions(-) diff --git a/tokio/src/net/driver/readiness.rs b/tokio/src/net/driver/readiness.rs index 139cc6abc9f..87c1aa18e29 100644 --- a/tokio/src/net/driver/readiness.rs +++ b/tokio/src/net/driver/readiness.rs @@ -1,68 +1,69 @@ use std::ops; -const READABLE: usize = 0b01; -const WRITABLE: usize = 0b10; -const READ_CLOSED: usize = 0b100; -const WRITE_CLOSED: usize = 0b1000; +const READABLE: usize = 0b0_01; +const WRITABLE: usize = 0b0_10; +const READ_CLOSED: usize = 0b0_0100; +const WRITE_CLOSED: usize = 0b0_1000; -#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)] +/// TODO +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] pub struct Readiness(usize); impl Readiness { - pub fn empty() -> Readiness { + pub(crate) fn empty() -> Readiness { Readiness(0) } - pub fn readable() -> Readiness { + pub(crate) fn readable() -> Readiness { Readiness(READABLE) } - pub fn writable() -> Readiness { + pub(crate) fn writable() -> Readiness { Readiness(WRITABLE) } - pub fn read_closed() -> Readiness { + pub(crate) fn read_closed() -> Readiness { Readiness(READ_CLOSED) } - pub fn write_closed() -> Readiness { + pub(crate) fn write_closed() -> Readiness { Readiness(WRITE_CLOSED) } - pub fn all() -> Readiness { + pub(crate) fn all() -> Readiness { Readiness(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED) } - pub fn is_empty(&self) -> bool { + pub(crate) fn is_empty(&self) -> bool { *self == Readiness::empty() } - pub fn is_readable(&self) -> bool { + pub(crate) fn is_readable(&self) -> bool { self.contains(Readiness::readable()) } - pub fn is_writable(&self) -> bool { + pub(crate) fn is_writable(&self) -> bool { self.contains(Readiness::writable()) } - pub fn is_read_closed(&self) -> bool { + pub(crate) fn is_read_closed(&self) -> bool { self.contains(Readiness::read_closed()) } - pub fn is_write_closed(&self) -> bool { + pub(crate) fn is_write_closed(&self) -> bool { self.contains(Readiness::write_closed()) } - pub fn contains>(&self, other: T) -> bool { + pub(crate) fn contains>(&self, other: T) -> bool { let other = other.into(); (*self & other) == other } - pub fn from_usize(val: usize) -> Readiness { + pub(crate) fn from_usize(val: usize) -> Readiness { Readiness(val) } - pub fn as_usize(&self) -> usize { + pub(crate) fn as_usize(&self) -> usize { self.0 } } @@ -92,13 +93,6 @@ impl> ops::BitAnd for Readiness { } } -impl> ops::BitAndAssign for Readiness { - #[inline] - fn bitand_assign(&mut self, other: T) { - self.0 &= other.into().0 - } -} - impl> ops::Sub for Readiness { type Output = Readiness; @@ -108,9 +102,8 @@ impl> ops::Sub for Readiness { } } -impl> ops::SubAssign for Readiness { - #[inline] - fn sub_assign(&mut self, other: T) { - self.0 &= !other.into().0; +impl From for Readiness { + fn from(x: usize) -> Self { + Readiness(x) } } diff --git a/tokio/src/net/util/io_source.rs b/tokio/src/net/util/io_source.rs index 2a14e63c96a..7cddec68cc7 100644 --- a/tokio/src/net/util/io_source.rs +++ b/tokio/src/net/util/io_source.rs @@ -11,19 +11,56 @@ use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::Relaxed; use std::task::{Context, Poll}; +macro_rules! poll_readiness { + ($me:expr, $cache:ident, $mask:ident, $take:ident, $poll:expr) => {{ + let mut cached = $me.$cache.load(Relaxed); + let mut current = $mask & cached; + + // If readiness is currently empty, loop until there is readiness; + // otherwise consume pending events and return the new readiness. + if current.is_empty() { + loop { + let new = match $poll? { + Poll::Ready(v) => v, + Poll::Pending => return Poll::Pending, + }; + cached |= new.as_usize(); + + $me.$cache.store(cached, Relaxed); + + current |= $mask & new; + + if !current.is_empty() { + return Poll::Ready(Ok(current)); + } + } + } else { + if let Some(readiness) = $me.registration.$take()? { + cached |= readiness.as_usize(); + $me.$cache.store(cached, Relaxed); + } + + Poll::Ready(Ok(cached.into())) + } + }}; +} + +macro_rules! clear_readiness { + ($me:expr, $cache:ident, $mask:expr, $poll:expr, $waker:expr) => {{ + $me.$cache.fetch_and(!$mask.as_usize(), Relaxed); + if $poll?.is_ready() { + // Notify the current task + $waker.wake_by_ref() + } + Ok(()) + }}; +} + /// TODO pub struct IoSource { io: Option, - inner: Inner, -} - -struct Inner { registration: Registration, - - /// Currently visible read readiness read_readiness: AtomicUsize, - - /// Currently visible write readiness write_readiness: AtomicUsize, } @@ -36,11 +73,9 @@ where let registration = Registration::new(&io)?; Ok(Self { io: Some(io), - inner: Inner { - registration, - read_readiness: AtomicUsize::default(), - write_readiness: AtomicUsize::default(), - }, + registration, + read_readiness: AtomicUsize::default(), + write_readiness: AtomicUsize::default(), }) } @@ -57,106 +92,54 @@ where /// TODO pub fn into_inner(mut self) -> io::Result { let io = self.io.take().unwrap(); - self.inner.registration.deregister(&io)?; + self.registration.deregister(&io)?; Ok(io) } /// TODO pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_read_ready(cx) + let mask = Readiness::readable() | Readiness::readable(); + poll_readiness!( + self, + read_readiness, + mask, + take_read_ready, + self.registration.poll_read_ready(cx) + ) } /// TODO pub fn clear_read_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { - self.inner - .read_readiness - .fetch_and(!Readiness::readable().as_usize(), Relaxed); - if self.poll_read_ready(cx)?.is_ready() { - // Notify the current task - cx.waker().wake_by_ref(); - } - Ok(()) + clear_readiness!( + self, + read_readiness, + Readiness::readable(), + self.registration.poll_read_ready(cx), + cx.waker() + ) } /// TODO pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_write_ready(cx) + let mask = Readiness::writable() | Readiness::write_closed(); + poll_readiness!( + self, + write_readiness, + mask, + take_write_ready, + self.registration.poll_write_ready(cx) + ) } /// TODO pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { - self.inner - .read_readiness - .fetch_and(!Readiness::writable().as_usize(), Relaxed); - if self.poll_write_ready(cx)?.is_ready() { - // Notify the current task - cx.waker().wake_by_ref(); - } - Ok(()) - } -} - -impl Inner { - fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - // Load cached readiness and see if it matches any bits - let mut cached = self.read_readiness.load(Relaxed); - let mut current = Readiness::from_usize(cached) & Readiness::readable(); - - if current.is_empty() { - loop { - let readiness = match self.registration.poll_read_ready(cx)? { - Poll::Ready(v) => v, - Poll::Pending => return Poll::Pending, - }; - cached |= readiness.as_usize(); - - self.read_readiness.store(cached, Relaxed); - - current |= readiness; - - if !current.is_empty() { - return Poll::Ready(Ok(current)); - } - } - } else { - if let Some(readiness) = self.registration.take_read_ready()? { - cached |= readiness.as_usize(); - self.read_readiness.store(cached, Relaxed); - } - - Poll::Ready(Ok(Readiness::from_usize(cached))) - } - } - - fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - // Load cached readiness and see if it matches any bits - let mut cached = self.write_readiness.load(Relaxed); - let mut current = Readiness::from_usize(cached) & Readiness::writable(); - - if current.is_empty() { - loop { - let readiness = match self.registration.poll_write_ready(cx)? { - Poll::Ready(v) => v, - Poll::Pending => return Poll::Pending, - }; - cached |= readiness.as_usize(); - - self.write_readiness.store(cached, Relaxed); - - current |= readiness; - - if !current.is_empty() { - return Poll::Ready(Ok(current)); - } - } - } else { - if let Some(readiness) = self.registration.take_write_ready()? { - cached |= readiness.as_usize(); - self.write_readiness.store(cached, Relaxed); - } - - Poll::Ready(Ok(Readiness::from_usize(cached))) - } + clear_readiness!( + self, + write_readiness, + Readiness::writable(), + self.registration.poll_write_ready(cx), + cx.waker() + ) } } @@ -240,7 +223,7 @@ impl Drop for IoSource { fn drop(&mut self) { if let Some(io) = self.io.take() { // Ignore errors - let _ = self.inner.registration.deregister(&io); + let _ = self.registration.deregister(&io); } } } From 88dab3ef88b2464002dff6f2f17566f5f8ace3eb Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Wed, 6 Nov 2019 22:22:12 -0800 Subject: [PATCH 04/21] Naming and docs Signed-off-by: Kevin Leimkuhler --- tokio/src/net/tcp/listener.rs | 8 +- tokio/src/net/tcp/stream.rs | 10 +- tokio/src/net/udp/socket.rs | 8 +- tokio/src/net/unix/datagram.rs | 8 +- tokio/src/net/unix/listener.rs | 12 +- tokio/src/net/unix/stream.rs | 12 +- tokio/src/net/util/io_resource.rs | 384 ++++++++++++++++++++++++++ tokio/src/net/util/io_source.rs | 229 ---------------- tokio/src/net/util/mod.rs | 4 +- tokio/src/net/util/poll_evented.rs | 414 ----------------------------- tokio/src/process/unix/mod.rs | 12 +- tokio/src/signal/unix.rs | 10 +- 12 files changed, 426 insertions(+), 685 deletions(-) create mode 100644 tokio/src/net/util/io_resource.rs delete mode 100644 tokio/src/net/util/io_source.rs delete mode 100644 tokio/src/net/util/poll_evented.rs diff --git a/tokio/src/net/tcp/listener.rs b/tokio/src/net/tcp/listener.rs index e4dc24aa190..11d155654a2 100644 --- a/tokio/src/net/tcp/listener.rs +++ b/tokio/src/net/tcp/listener.rs @@ -1,6 +1,6 @@ use crate::future::poll_fn; use crate::net::tcp::{Incoming, TcpStream}; -use crate::net::util::IoSource; +use crate::net::util::IoResource; use crate::net::ToSocketAddrs; use std::convert::TryFrom; @@ -30,7 +30,7 @@ use std::task::{Context, Poll}; /// } /// ``` pub struct TcpListener { - io: IoSource, + io: IoResource, } impl TcpListener { @@ -182,12 +182,12 @@ impl TcpListener { /// ``` pub fn from_std(listener: net::TcpListener) -> io::Result { let io = mio::net::TcpListener::from_std(listener); - let io = IoSource::new(io)?; + let io = IoResource::new(io)?; Ok(TcpListener { io }) } fn new(listener: mio::net::TcpListener) -> io::Result { - let io = IoSource::new(listener)?; + let io = IoResource::new(listener)?; Ok(TcpListener { io }) } diff --git a/tokio/src/net/tcp/stream.rs b/tokio/src/net/tcp/stream.rs index 5c060572695..91a47d261ab 100644 --- a/tokio/src/net/tcp/stream.rs +++ b/tokio/src/net/tcp/stream.rs @@ -1,7 +1,7 @@ use crate::future::poll_fn; use crate::io::{AsyncRead, AsyncWrite}; use crate::net::tcp::split::{split, ReadHalf, WriteHalf}; -use crate::net::util::IoSource; +use crate::net::util::IoResource; use crate::net::ToSocketAddrs; use bytes::{Buf, BufMut}; @@ -40,7 +40,7 @@ use std::task::{Context, Poll}; /// } /// ``` pub struct TcpStream { - io: IoSource, + io: IoResource, } impl TcpStream { @@ -113,7 +113,7 @@ impl TcpStream { } pub(crate) fn new(connected: mio::net::TcpStream) -> io::Result { - let io = IoSource::new(connected)?; + let io = IoResource::new(connected)?; Ok(TcpStream { io }) } @@ -137,7 +137,7 @@ impl TcpStream { /// ``` pub fn from_std(stream: net::TcpStream) -> io::Result { let io = mio::net::TcpStream::from_std(stream); - let io = IoSource::new(io)?; + let io = IoResource::new(io)?; Ok(TcpStream { io }) } @@ -147,7 +147,7 @@ impl TcpStream { #[doc(hidden)] pub async fn connect_std(_stream: net::TcpStream, _addr: &SocketAddr) -> io::Result { // let io = mio::net::TcpStream::connect_stream(stream, addr)?; - // let io = IoSource::new(io)?; + // let io = IoResource::new(io)?; // let stream = TcpStream { io }; // Once we've connected, wait for the stream to be writable as diff --git a/tokio/src/net/udp/socket.rs b/tokio/src/net/udp/socket.rs index 2010513db2e..eda5743345b 100644 --- a/tokio/src/net/udp/socket.rs +++ b/tokio/src/net/udp/socket.rs @@ -1,6 +1,6 @@ use crate::future::poll_fn; use crate::net::udp::split::{split, UdpSocketRecvHalf, UdpSocketSendHalf}; -use crate::net::util::IoSource; +use crate::net::util::IoResource; use crate::net::ToSocketAddrs; use std::convert::TryFrom; @@ -11,7 +11,7 @@ use std::task::{Context, Poll}; /// An I/O object representing a UDP socket. pub struct UdpSocket { - io: IoSource, + io: IoResource, } impl UdpSocket { @@ -42,7 +42,7 @@ impl UdpSocket { } fn new(socket: mio::net::UdpSocket) -> io::Result { - let io = IoSource::new(socket)?; + let io = IoResource::new(socket)?; Ok(UdpSocket { io }) } @@ -57,7 +57,7 @@ impl UdpSocket { /// `reuse_address` or binding to multiple addresses. pub fn from_std(socket: net::UdpSocket) -> io::Result { let io = mio::net::UdpSocket::from_std(socket); - let io = IoSource::new(io)?; + let io = IoResource::new(io)?; Ok(UdpSocket { io }) } diff --git a/tokio/src/net/unix/datagram.rs b/tokio/src/net/unix/datagram.rs index 1a77c346aed..65a20825d40 100644 --- a/tokio/src/net/unix/datagram.rs +++ b/tokio/src/net/unix/datagram.rs @@ -1,5 +1,5 @@ use crate::future::poll_fn; -use crate::net::util::IoSource; +use crate::net::util::IoResource; use mio; use std::convert::TryFrom; @@ -13,7 +13,7 @@ use std::task::{Context, Poll}; /// An I/O object representing a Unix datagram socket. pub struct UnixDatagram { - io: IoSource, + io: IoResource, } impl UnixDatagram { @@ -46,12 +46,12 @@ impl UnixDatagram { /// specified by `handle` and is ready to perform I/O. pub fn from_std(datagram: net::UnixDatagram) -> io::Result { let socket = mio::net::UnixDatagram::from_std(datagram); - let io = IoSource::new(socket)?; + let io = IoResource::new(socket)?; Ok(UnixDatagram { io }) } fn new(socket: mio::net::UnixDatagram) -> io::Result { - let io = IoSource::new(socket)?; + let io = IoResource::new(socket)?; Ok(UnixDatagram { io }) } diff --git a/tokio/src/net/unix/listener.rs b/tokio/src/net/unix/listener.rs index 52b24b9d1ee..5df0dfa0f70 100644 --- a/tokio/src/net/unix/listener.rs +++ b/tokio/src/net/unix/listener.rs @@ -1,6 +1,6 @@ use crate::future::poll_fn; use crate::net::unix::{Incoming, UnixStream}; -use crate::net::util::IoSource; +use crate::net::util::IoResource; use mio; use std::convert::TryFrom; @@ -13,7 +13,7 @@ use std::task::{Context, Poll}; /// A Unix socket which can accept connections from other Unix sockets. pub struct UnixListener { - io: IoSource, + io: IoResource, } impl UnixListener { @@ -23,7 +23,7 @@ impl UnixListener { P: AsRef, { let listener = mio::net::UnixListener::bind(path)?; - let io = IoSource::new(listener)?; + let io = IoResource::new(listener)?; Ok(UnixListener { io }) } @@ -34,7 +34,7 @@ impl UnixListener { /// specified by `handle` and is ready to perform I/O. pub fn from_std(listener: net::UnixListener) -> io::Result { let listener = mio::net::UnixListener::from_std(listener); - let io = IoSource::new(listener)?; + let io = IoResource::new(listener)?; Ok(UnixListener { io }) } @@ -87,10 +87,10 @@ impl TryFrom for mio::net::UnixListener { /// Consumes value, returning the mio I/O object. /// - /// See [`IoSource::into_inner`] for more details about + /// See [`IoResource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`IoSource::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoResource::into_inner`]: crate::util::PollEvented::into_inner fn try_from(value: UnixListener) -> Result { value.io.into_inner() } diff --git a/tokio/src/net/unix/stream.rs b/tokio/src/net/unix/stream.rs index 3c7eb34519f..60f98a74a92 100644 --- a/tokio/src/net/unix/stream.rs +++ b/tokio/src/net/unix/stream.rs @@ -2,7 +2,7 @@ use crate::future::poll_fn; use crate::io::{AsyncRead, AsyncWrite}; use crate::net::unix::split::{split, ReadHalf, WriteHalf}; use crate::net::unix::ucred::{self, UCred}; -use crate::net::util::IoSource; +use crate::net::util::IoResource; use bytes::{Buf, BufMut}; use std::convert::TryFrom; @@ -21,7 +21,7 @@ use std::task::{Context, Poll}; /// from a listener with `UnixListener::incoming`. Additionally, a pair of /// anonymous Unix sockets can be created with `UnixStream::pair`. pub struct UnixStream { - io: IoSource, + io: IoResource, } impl UnixStream { @@ -48,7 +48,7 @@ impl UnixStream { /// specified by `handle` and is ready to perform I/O. pub fn from_std(stream: net::UnixStream) -> io::Result { let stream = mio::net::UnixStream::from_std(stream); - let io = IoSource::new(stream)?; + let io = IoResource::new(stream)?; Ok(UnixStream { io }) } @@ -67,7 +67,7 @@ impl UnixStream { } pub(crate) fn new(stream: mio::net::UnixStream) -> io::Result { - let io = IoSource::new(stream)?; + let io = IoResource::new(stream)?; Ok(UnixStream { io }) } @@ -115,10 +115,10 @@ impl TryFrom for mio::net::UnixStream { /// Consumes value, returning the mio I/O object. /// - /// See [`IoSource::into_inner`] for more details about + /// See [`IoResource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`IoSource::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoResource::into_inner`]: crate::util::PollEvented::into_inner fn try_from(value: UnixStream) -> Result { value.io.into_inner() } diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs new file mode 100644 index 00000000000..7ddd205c5f9 --- /dev/null +++ b/tokio/src/net/util/io_resource.rs @@ -0,0 +1,384 @@ +use crate::io::{AsyncRead, AsyncWrite}; +use crate::net::driver::{Readiness, Registration}; + +use futures_core::ready; +use mio; +use std::fmt; +use std::io::{self, Read, Write}; +use std::marker::Unpin; +use std::pin::Pin; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::Relaxed; +use std::task::{Context, Poll}; + +/// Associates an I/O resource that implements the [`std::io::Read`] and/or +/// [`std::io::Write`] traits with the reactor that drives it. +/// +/// `IoResource` uses [`Registration`] to take a type that implements +/// [`mio::event::Source`] as well as [`std::io::Read`] and/or +/// [`std::io::Write`] and associates it with a reactor that will drive it. +/// +/// Once the [`mio::event::Source`] type is wrapped by `IoResource`, it can +/// be used from within the future's execution model. As such, the +/// `IoResource` type provides [`AsyncRead`] and [`AsyncWrite`] +/// implementations using the underlying I/O resource as well as readiness +/// events provided by the reactor. +/// +/// **Note**: While `IoResource` is `Sync` (if the underlying I/O type is +/// `Sync`), the caller must ensure that there are at most two tasks that use +/// a `IoResource` instance concurrently. One for reading and one for writing. +/// While violating this requirement is "safe" from a Rust memory model point +/// of view, it will result in unexpected behavior in the form of lost +/// notifications and tasks hanging. +/// +/// ## Readiness events +/// +/// Besides just providing [`AsyncRead`] and [`AsyncWrite`] implementations, +/// this type also supports access to the underlying readiness event stream. +/// While similar in function to what [`Registration`] provides, the semantics +/// are a bit different. +/// +/// Two functions are provided to access the readiness events: +/// [`poll_read_ready`] and [`poll_write_ready`]. These functions return the +/// current readiness state of the `IoResource` instance. If +/// [`poll_read_ready`] indicates read readiness, immediately calling +/// [`poll_read_ready`] again will also indicate read readiness. +/// +/// When the operation is attempted and is unable to succeed due to the I/O +/// resource not being ready, the caller must call [`clear_read_ready`] or +/// [`clear_write_ready`]. This clears the readiness state until a new +/// readiness event is received. +/// +/// This allows the caller to implement additional functions. For example, +/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and +/// [`clear_read_ready`]. +/// +/// TODO +/// ```rust +/// use tokio::net::util::IoResource; +/// +/// use futures_core::ready; +/// use mio::Ready; +/// use mio::net::{TcpStream, TcpListener}; +/// use std::io; +/// use std::task::{Context, Poll}; +/// +/// struct MyListener { +/// poll_evented: PollEvented, +/// } +/// +/// impl MyListener { +/// pub fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { +/// let ready = Ready::readable(); +/// +/// ready!(self.poll_evented.poll_read_ready(cx, ready))?; +/// +/// match self.poll_evented.get_ref().accept() { +/// Ok((socket, _)) => Poll::Ready(Ok(socket)), +/// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { +/// self.poll_evented.clear_read_ready(cx, ready)?; +/// Poll::Pending +/// } +/// Err(e) => Poll::Ready(Err(e)), +/// } +/// } +/// } +/// ``` +/// +/// TODO +/// ## Platform-specific events +/// +/// `PollEvented` also allows receiving platform-specific `mio::Ready` events. +/// These events are included as part of the read readiness event stream. The +/// write readiness event stream is only for `Ready::writable()` events. +/// +/// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html +/// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +/// [`AsyncRead`]: ../io/trait.AsyncRead.html +/// [`AsyncWrite`]: ../io/trait.AsyncWrite.html +/// [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html +/// [`Registration`]: struct.Registration.html +/// [`TcpListener`]: ../net/struct.TcpListener.html +/// [`clear_read_ready`]: #method.clear_read_ready +/// [`clear_write_ready`]: #method.clear_write_ready +/// [`poll_read_ready`]: #method.poll_read_ready +/// [`poll_write_ready`]: #method.poll_write_ready +pub struct IoResource { + io: Option, + registration: Registration, + /// Cached read readiness + read_readiness: AtomicUsize, + /// Cached write readiness + write_readiness: AtomicUsize, +} + +macro_rules! poll_readiness { + ($me:expr, $cache:ident, $mask:ident, $take:ident, $poll:expr) => {{ + // Load cached readiness and check if it currently has readiness + // matching `$mask`. + let mut cached = $me.$cache.load(Relaxed); + let mut current = $mask & cached; + + // If readiness is currently empty, loop until there is readiness; + // otherwise consume pending events and return the new readiness. + if current.is_empty() { + loop { + let new = match $poll? { + Poll::Ready(v) => v, + Poll::Pending => return Poll::Pending, + }; + cached |= new.as_usize(); + + // Store the cached readiness. + $me.$cache.store(cached, Relaxed); + + current |= $mask & new; + if !current.is_empty() { + return Poll::Ready(Ok(current)); + } + } + } else { + if let Some(readiness) = $me.registration.$take()? { + cached |= readiness.as_usize(); + $me.$cache.store(cached, Relaxed); + } + Poll::Ready(Ok(cached.into())) + } + }}; +} + +macro_rules! clear_readiness { + ($me:expr, $cache:ident, $mask:expr, $poll:expr, $waker:expr) => {{ + $me.$cache.fetch_and(!$mask.as_usize(), Relaxed); + if $poll?.is_ready() { + // Notify the current task + $waker.wake_by_ref() + } + Ok(()) + }}; +} + +impl IoResource +where + S: mio::event::Source, +{ + /// Creates a new `IoResource` associated with the default reactor. + pub fn new(io: S) -> io::Result { + let registration = Registration::new(&io)?; + Ok(Self { + io: Some(io), + registration, + read_readiness: AtomicUsize::default(), + write_readiness: AtomicUsize::default(), + }) + } + + /// Returns a shared reference to the underlying I/O resource this + /// readiness stream is wrapping. + pub fn get_ref(&self) -> &S { + self.io.as_ref().unwrap() + } + + /// Returns a mutable reference to the underlying I/O resource this + /// readiness stream is wrapping. + pub fn get_mut(&mut self) -> &mut S { + self.io.as_mut().unwrap() + } + + /// Consumes self, returning the inner I/O resource. + /// + /// This function will deregister the I/O resource from the reactor before + /// returning. If the deregistration operation fails, an error is + /// returned. + /// + /// **Note**: Deregistering does not guarantee that the I/O resource can be + /// registered with a different reactor. Some I/O resource types can only + /// be associated with a single reactor instance for their lifetime. + pub fn into_inner(mut self) -> io::Result { + let io = self.io.take().unwrap(); + self.registration.deregister(&io)?; + Ok(io) + } + + /// Check the I/O resource's read readiness state. + /// + /// TODO: CLOSED + /// + /// If the resource is not readable then `Poll::Pending` is returned and + /// the current task is notified once a new event is received. + /// + /// The I/O resource will remain in a read-ready state until readiness is + /// cleared by calling [`clear_read_ready`]. + /// + /// [`clear_read_ready`]: #method.clear_read_ready + /// + /// # Panics + /// + /// This function panics if: + /// + /// * called from outside of a task context. + pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { + let mask = Readiness::readable() | Readiness::readable(); + poll_readiness!( + self, + read_readiness, + mask, + take_read_ready, + self.registration.poll_read_ready(cx) + ) + } + + /// Clears the I/O resource's read readiness state and registers the + /// current task to be notified once a read readiness event is received. + /// + /// After calling this function, `poll_read_ready` will return + /// `Poll::Pending` until a new read readiness event has been received. + /// + /// # Panics + /// + /// This function panics if: + /// + /// * called from outside of a task context. + pub fn clear_read_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { + clear_readiness!( + self, + read_readiness, + Readiness::readable(), + self.registration.poll_read_ready(cx), + cx.waker() + ) + } + + /// Check the I/O resource's write readiness state. + /// + /// TODO: CLOSED + /// + /// If the resource is not writable then `Poll::Pending` is returned and + /// the current task is notified once a new event is received. + /// + /// The I/O resource will remain in a write-ready state until readiness is + /// cleared by calling [`clear_write_ready`]. + /// + /// [`clear_write_ready`]: #method.clear_write_ready + /// + /// # Panics + /// + /// This function panics if: + /// + /// * called from outside of a task context. + pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { + let mask = Readiness::writable() | Readiness::write_closed(); + poll_readiness!( + self, + write_readiness, + mask, + take_write_ready, + self.registration.poll_write_ready(cx) + ) + } + + /// Resets the I/O resource's write readiness state and registers the current + /// task to be notified once a write readiness event is received. + /// + /// + /// After calling this function, `poll_write_ready` will return + /// `Poll::Pending` until a new write readiness event has been received. + /// + /// # Panics + /// + /// This function will panic if called from outside of a task context. + pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { + clear_readiness!( + self, + write_readiness, + Readiness::writable(), + self.registration.poll_write_ready(cx), + cx.waker() + ) + } +} + +// ===== Read / Write impls ===== + +impl AsyncRead for IoResource +where + S: mio::event::Source + Read + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + ready!(self.poll_read_ready(cx))?; + + let r = (*self).get_mut().read(buf); + + if is_wouldblock(&r) { + self.clear_read_ready(cx)?; + return Poll::Pending; + } + + Poll::Ready(r) + } +} + +impl AsyncWrite for IoResource +where + S: mio::event::Source + Write + Unpin, +{ + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + ready!(self.poll_write_ready(cx))?; + + let r = (*self).get_mut().write(buf); + + if is_wouldblock(&r) { + self.clear_write_ready(cx)?; + return Poll::Pending; + } + + Poll::Ready(r) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + ready!(self.poll_write_ready(cx))?; + + let r = (*self).get_mut().flush(); + + if is_wouldblock(&r) { + self.clear_write_ready(cx)?; + return Poll::Pending; + } + + Poll::Ready(r) + } + + fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } +} + +fn is_wouldblock(r: &io::Result) -> bool { + match *r { + Ok(_) => false, + Err(ref e) => e.kind() == io::ErrorKind::WouldBlock, + } +} + +impl fmt::Debug for IoResource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Source").field("io", &self.io).finish() + } +} + +impl Drop for IoResource { + fn drop(&mut self) { + if let Some(io) = self.io.take() { + // Ignore errors + let _ = self.registration.deregister(&io); + } + } +} diff --git a/tokio/src/net/util/io_source.rs b/tokio/src/net/util/io_source.rs deleted file mode 100644 index 7cddec68cc7..00000000000 --- a/tokio/src/net/util/io_source.rs +++ /dev/null @@ -1,229 +0,0 @@ -use crate::io::{AsyncRead, AsyncWrite}; -use crate::net::driver::{Readiness, Registration}; - -use futures_core::ready; -use mio; -use std::fmt; -use std::io::{self, Read, Write}; -use std::marker::Unpin; -use std::pin::Pin; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering::Relaxed; -use std::task::{Context, Poll}; - -macro_rules! poll_readiness { - ($me:expr, $cache:ident, $mask:ident, $take:ident, $poll:expr) => {{ - let mut cached = $me.$cache.load(Relaxed); - let mut current = $mask & cached; - - // If readiness is currently empty, loop until there is readiness; - // otherwise consume pending events and return the new readiness. - if current.is_empty() { - loop { - let new = match $poll? { - Poll::Ready(v) => v, - Poll::Pending => return Poll::Pending, - }; - cached |= new.as_usize(); - - $me.$cache.store(cached, Relaxed); - - current |= $mask & new; - - if !current.is_empty() { - return Poll::Ready(Ok(current)); - } - } - } else { - if let Some(readiness) = $me.registration.$take()? { - cached |= readiness.as_usize(); - $me.$cache.store(cached, Relaxed); - } - - Poll::Ready(Ok(cached.into())) - } - }}; -} - -macro_rules! clear_readiness { - ($me:expr, $cache:ident, $mask:expr, $poll:expr, $waker:expr) => {{ - $me.$cache.fetch_and(!$mask.as_usize(), Relaxed); - if $poll?.is_ready() { - // Notify the current task - $waker.wake_by_ref() - } - Ok(()) - }}; -} - -/// TODO -pub struct IoSource { - io: Option, - registration: Registration, - read_readiness: AtomicUsize, - write_readiness: AtomicUsize, -} - -impl IoSource -where - S: mio::event::Source, -{ - /// TODO - pub fn new(io: S) -> io::Result { - let registration = Registration::new(&io)?; - Ok(Self { - io: Some(io), - registration, - read_readiness: AtomicUsize::default(), - write_readiness: AtomicUsize::default(), - }) - } - - /// TODO - pub fn get_ref(&self) -> &S { - self.io.as_ref().unwrap() - } - - /// TODO - pub fn get_mut(&mut self) -> &mut S { - self.io.as_mut().unwrap() - } - - /// TODO - pub fn into_inner(mut self) -> io::Result { - let io = self.io.take().unwrap(); - self.registration.deregister(&io)?; - Ok(io) - } - - /// TODO - pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - let mask = Readiness::readable() | Readiness::readable(); - poll_readiness!( - self, - read_readiness, - mask, - take_read_ready, - self.registration.poll_read_ready(cx) - ) - } - - /// TODO - pub fn clear_read_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { - clear_readiness!( - self, - read_readiness, - Readiness::readable(), - self.registration.poll_read_ready(cx), - cx.waker() - ) - } - - /// TODO - pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - let mask = Readiness::writable() | Readiness::write_closed(); - poll_readiness!( - self, - write_readiness, - mask, - take_write_ready, - self.registration.poll_write_ready(cx) - ) - } - - /// TODO - pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { - clear_readiness!( - self, - write_readiness, - Readiness::writable(), - self.registration.poll_write_ready(cx), - cx.waker() - ) - } -} - -// ===== Read / Write impls ===== - -impl AsyncRead for IoSource -where - S: mio::event::Source + Read + Unpin, -{ - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - ready!(self.poll_read_ready(cx))?; - - let r = (*self).get_mut().read(buf); - - if is_wouldblock(&r) { - self.clear_read_ready(cx)?; - return Poll::Pending; - } - - Poll::Ready(r) - } -} - -impl AsyncWrite for IoSource -where - S: mio::event::Source + Write + Unpin, -{ - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - ready!(self.poll_write_ready(cx))?; - - let r = (*self).get_mut().write(buf); - - if is_wouldblock(&r) { - self.clear_write_ready(cx)?; - return Poll::Pending; - } - - Poll::Ready(r) - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - ready!(self.poll_write_ready(cx))?; - - let r = (*self).get_mut().flush(); - - if is_wouldblock(&r) { - self.clear_write_ready(cx)?; - return Poll::Pending; - } - - Poll::Ready(r) - } - - fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -fn is_wouldblock(r: &io::Result) -> bool { - match *r { - Ok(_) => false, - Err(ref e) => e.kind() == io::ErrorKind::WouldBlock, - } -} - -impl fmt::Debug for IoSource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Source").field("io", &self.io).finish() - } -} - -impl Drop for IoSource { - fn drop(&mut self) { - if let Some(io) = self.io.take() { - // Ignore errors - let _ = self.registration.deregister(&io); - } - } -} diff --git a/tokio/src/net/util/mod.rs b/tokio/src/net/util/mod.rs index bc9ef39c3ec..9dc262cfe45 100644 --- a/tokio/src/net/util/mod.rs +++ b/tokio/src/net/util/mod.rs @@ -1,4 +1,4 @@ //! Utilities for implementing networking types. -mod io_source; -pub use self::io_source::IoSource; +mod io_resource; +pub use self::io_resource::IoResource; diff --git a/tokio/src/net/util/poll_evented.rs b/tokio/src/net/util/poll_evented.rs deleted file mode 100644 index 8d55eae4152..00000000000 --- a/tokio/src/net/util/poll_evented.rs +++ /dev/null @@ -1,414 +0,0 @@ -// use crate::io::{AsyncRead, AsyncWrite}; -// use crate::net::driver::{platform, Registration}; - -// use futures_core::ready; -// use mio::event::Evented; -// use std::fmt; -// use std::io::{self, Read, Write}; -// use std::marker::Unpin; -// use std::pin::Pin; -// use std::sync::atomic::AtomicUsize; -// use std::sync::atomic::Ordering::Relaxed; -// use std::task::{Context, Poll}; - -// /// Associates an I/O resource that implements the [`std::io::Read`] and/or -// /// [`std::io::Write`] traits with the reactor that drives it. -// /// -// /// `PollEvented` uses [`Registration`] internally to take a type that -// /// implements [`mio::Evented`] as well as [`std::io::Read`] and or -// /// [`std::io::Write`] and associate it with a reactor that will drive it. -// /// -// /// Once the [`mio::Evented`] type is wrapped by `PollEvented`, it can be -// /// used from within the future's execution model. As such, the `PollEvented` -// /// type provides [`AsyncRead`] and [`AsyncWrite`] implementations using the -// /// underlying I/O resource as well as readiness events provided by the reactor. -// /// -// /// **Note**: While `PollEvented` is `Sync` (if the underlying I/O type is -// /// `Sync`), the caller must ensure that there are at most two tasks that use a -// /// `PollEvented` instance concurrently. One for reading and one for writing. -// /// While violating this requirement is "safe" from a Rust memory model point of -// /// view, it will result in unexpected behavior in the form of lost -// /// notifications and tasks hanging. -// /// -// /// ## Readiness events -// /// -// /// Besides just providing [`AsyncRead`] and [`AsyncWrite`] implementations, -// /// this type also supports access to the underlying readiness event stream. -// /// While similar in function to what [`Registration`] provides, the semantics -// /// are a bit different. -// /// -// /// Two functions are provided to access the readiness events: -// /// [`poll_read_ready`] and [`poll_write_ready`]. These functions return the -// /// current readiness state of the `PollEvented` instance. If -// /// [`poll_read_ready`] indicates read readiness, immediately calling -// /// [`poll_read_ready`] again will also indicate read readiness. -// /// -// /// When the operation is attempted and is unable to succeed due to the I/O -// /// resource not being ready, the caller must call [`clear_read_ready`] or -// /// [`clear_write_ready`]. This clears the readiness state until a new readiness -// /// event is received. -// /// -// /// This allows the caller to implement additional functions. For example, -// /// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and -// /// [`clear_read_ready`]. -// /// -// /// ```rust -// /// use tokio::net::util::PollEvented; -// /// -// /// use futures_core::ready; -// /// use mio::Ready; -// /// use mio::net::{TcpStream, TcpListener}; -// /// use std::io; -// /// use std::task::{Context, Poll}; -// /// -// /// struct MyListener { -// /// poll_evented: PollEvented, -// /// } -// /// -// /// impl MyListener { -// /// pub fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { -// /// let ready = Ready::readable(); -// /// -// /// ready!(self.poll_evented.poll_read_ready(cx, ready))?; -// /// -// /// match self.poll_evented.get_ref().accept() { -// /// Ok((socket, _)) => Poll::Ready(Ok(socket)), -// /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { -// /// self.poll_evented.clear_read_ready(cx, ready)?; -// /// Poll::Pending -// /// } -// /// Err(e) => Poll::Ready(Err(e)), -// /// } -// /// } -// /// } -// /// ``` -// /// -// /// ## Platform-specific events -// /// -// /// `PollEvented` also allows receiving platform-specific `mio::Ready` events. -// /// These events are included as part of the read readiness event stream. The -// /// write readiness event stream is only for `Ready::writable()` events. -// /// -// /// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html -// /// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html -// /// [`AsyncRead`]: ../io/trait.AsyncRead.html -// /// [`AsyncWrite`]: ../io/trait.AsyncWrite.html -// /// [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html -// /// [`Registration`]: struct.Registration.html -// /// [`TcpListener`]: ../net/struct.TcpListener.html -// /// [`clear_read_ready`]: #method.clear_read_ready -// /// [`clear_write_ready`]: #method.clear_write_ready -// /// [`poll_read_ready`]: #method.poll_read_ready -// /// [`poll_write_ready`]: #method.poll_write_ready -// pub struct PollEvented { -// io: Option, -// inner: Inner, -// } - -// struct Inner { -// registration: Registration, - -// /// Currently visible read readiness -// read_readiness: AtomicUsize, - -// /// Currently visible write readiness -// write_readiness: AtomicUsize, -// } - -// // ===== impl PollEvented ===== - -// macro_rules! poll_ready { -// ($me:expr, $mask:expr, $cache:ident, $take:ident, $poll:expr) => {{ -// // Load cached & encoded readiness. -// let mut cached = $me.inner.$cache.load(Relaxed); -// // let mask = $mask | platform::hup(); -// let mask = $mask; - -// // See if the current readiness matches any bits. -// let mut ret = mio::Ready::from_usize(cached) & $mask; - -// if ret.is_empty() { -// // Readiness does not match, consume the registration's readiness -// // stream. This happens in a loop to ensure that the stream gets -// // drained. -// loop { -// let ready = match $poll? { -// Poll::Ready(v) => v, -// Poll::Pending => return Poll::Pending, -// }; -// cached |= ready.as_usize(); - -// // Update the cache store -// $me.inner.$cache.store(cached, Relaxed); - -// ret |= ready & mask; - -// if !ret.is_empty() { -// return Poll::Ready(Ok(ret)); -// } -// } -// } else { -// // Check what's new with the registration stream. This will not -// // request to be notified -// if let Some(ready) = $me.inner.registration.$take()? { -// cached |= ready.as_usize(); -// $me.inner.$cache.store(cached, Relaxed); -// } - -// Poll::Ready(Ok(mio::Ready::from_usize(cached))) -// } -// }}; -// } - -// impl PollEvented -// where -// E: Evented, -// { -// /// Creates a new `PollEvented` associated with the default reactor. -// pub fn new(io: E) -> io::Result { -// let registration = Registration::new(&io)?; -// Ok(Self { -// io: Some(io), -// inner: Inner { -// registration, -// read_readiness: AtomicUsize::new(0), -// write_readiness: AtomicUsize::new(0), -// }, -// }) -// } - -// /// Returns a shared reference to the underlying I/O object this readiness -// /// stream is wrapping. -// pub fn get_ref(&self) -> &E { -// self.io.as_ref().unwrap() -// } - -// /// Returns a mutable reference to the underlying I/O object this readiness -// /// stream is wrapping. -// pub fn get_mut(&mut self) -> &mut E { -// self.io.as_mut().unwrap() -// } - -// /// Consumes self, returning the inner I/O object -// /// -// /// This function will deregister the I/O resource from the reactor before -// /// returning. If the deregistration operation fails, an error is returned. -// /// -// /// Note that deregistering does not guarantee that the I/O resource can be -// /// registered with a different reactor. Some I/O resource types can only be -// /// associated with a single reactor instance for their lifetime. -// pub fn into_inner(mut self) -> io::Result { -// let io = self.io.take().unwrap(); -// self.inner.registration.deregister(&io)?; -// Ok(io) -// } - -// /// Check the I/O resource's read readiness state. -// /// -// /// The mask argument allows specifying what readiness to notify on. This -// /// can be any value, including platform specific readiness, **except** -// /// `writable`. HUP is always implicitly included on platforms that support -// /// it. -// /// -// /// If the resource is not ready for a read then `Poll::Pending` is returned -// /// and the current task is notified once a new event is received. -// /// -// /// The I/O resource will remain in a read-ready state until readiness is -// /// cleared by calling [`clear_read_ready`]. -// /// -// /// [`clear_read_ready`]: #method.clear_read_ready -// /// -// /// # Panics -// /// -// /// This function panics if: -// /// -// /// * `ready` includes writable. -// /// * called from outside of a task co ntext. -// pub fn poll_read_ready( -// &self, -// cx: &mut Context<'_>, -// mask: mio::Ready, -// ) -> Poll> { -// assert!(!mask.is_writable(), "cannot poll for write readiness"); -// poll_ready!( -// self, -// mask, -// read_readiness, -// take_read_ready, -// self.inner.registration.poll_read_ready(cx) -// ) -// } - -// /// Clears the I/O resource's read readiness state and registers the current -// /// task to be notified once a read readiness event is received. -// /// -// /// After calling this function, `poll_read_ready` will return -// /// `Poll::Pending` until a new read readiness event has been received. -// /// -// /// The `mask` argument specifies the readiness bits to clear. This may not -// /// include `writable` or `hup`. -// /// -// /// # Panics -// /// -// /// This function panics if: -// /// -// /// * `ready` includes writable or HUP -// /// * called from outside of a task context. -// pub fn clear_read_ready(&self, cx: &mut Context<'_>, ready: mio::Ready) -> io::Result<()> { -// // Cannot clear write readiness -// assert!(!ready.is_writable(), "cannot clear write readiness"); -// assert!(!platform::is_hup(ready), "cannot clear HUP readiness"); - -// self.inner -// .read_readiness -// .fetch_and(!ready.as_usize(), Relaxed); - -// if self.poll_read_ready(cx, ready)?.is_ready() { -// // Notify the current task -// cx.waker().wake_by_ref(); -// } - -// Ok(()) -// } - -// /// Check the I/O resource's write readiness state. -// /// -// /// This always checks for writable readiness and also checks for HUP -// /// readiness on platforms that support it. -// /// -// /// If the resource is not ready for a write then `Async::NotReady` is -// /// returned and the current task is notified once a new event is received. -// /// -// /// The I/O resource will remain in a write-ready state until readiness is -// /// cleared by calling [`clear_write_ready`]. -// /// -// /// [`clear_write_ready`]: #method.clear_write_ready -// /// -// /// # Panics -// /// -// /// This function panics if: -// /// -// /// * `ready` contains bits besides `writable` and `hup`. -// /// * called from outside of a task context. -// pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { -// poll_ready!( -// self, -// mio::Ready::writable(), -// write_readiness, -// take_write_ready, -// self.inner.registration.poll_write_ready(cx) -// ) -// } - -// /// Resets the I/O resource's write readiness state and registers the current -// /// task to be notified once a write readiness event is received. -// /// -// /// This only clears writable readiness. HUP (on platforms that support HUP) -// /// cannot be cleared as it is a final state. -// /// -// /// After calling this function, `poll_write_ready(Ready::writable())` will -// /// return `NotReady` until a new write readiness event has been received. -// /// -// /// # Panics -// /// -// /// This function will panic if called from outside of a task context. -// pub fn clear_write_ready(&self, cx: &mut Context<'_>) -> io::Result<()> { -// let ready = mio::Ready::writable(); - -// self.inner -// .write_readiness -// .fetch_and(!ready.as_usize(), Relaxed); - -// if self.poll_write_ready(cx)?.is_ready() { -// // Notify the current task -// cx.waker().wake_by_ref(); -// } - -// Ok(()) -// } -// } - -// // ===== Read / Write impls ===== - -// impl AsyncRead for PollEvented -// where -// E: Evented + Read + Unpin, -// { -// fn poll_read( -// mut self: Pin<&mut Self>, -// cx: &mut Context<'_>, -// buf: &mut [u8], -// ) -> Poll> { -// ready!(self.poll_read_ready(cx, mio::Ready::readable()))?; - -// let r = (*self).get_mut().read(buf); - -// if is_wouldblock(&r) { -// self.clear_read_ready(cx, mio::Ready::readable())?; -// return Poll::Pending; -// } - -// Poll::Ready(r) -// } -// } - -// impl AsyncWrite for PollEvented -// where -// E: Evented + Write + Unpin, -// { -// fn poll_write( -// mut self: Pin<&mut Self>, -// cx: &mut Context<'_>, -// buf: &[u8], -// ) -> Poll> { -// ready!(self.poll_write_ready(cx))?; - -// let r = (*self).get_mut().write(buf); - -// if is_wouldblock(&r) { -// self.clear_write_ready(cx)?; -// return Poll::Pending; -// } - -// Poll::Ready(r) -// } - -// fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { -// ready!(self.poll_write_ready(cx))?; - -// let r = (*self).get_mut().flush(); - -// if is_wouldblock(&r) { -// self.clear_write_ready(cx)?; -// return Poll::Pending; -// } - -// Poll::Ready(r) -// } - -// fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { -// Poll::Ready(Ok(())) -// } -// } - -// fn is_wouldblock(r: &io::Result) -> bool { -// match *r { -// Ok(_) => false, -// Err(ref e) => e.kind() == io::ErrorKind::WouldBlock, -// } -// } - -// impl fmt::Debug for PollEvented { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// f.debug_struct("PollEvented").field("io", &self.io).finish() -// } -// } - -// impl Drop for PollEvented { -// fn drop(&mut self) { -// if let Some(io) = self.io.take() { -// // Ignore errors -// let _ = self.inner.registration.deregister(&io); -// } -// } -// } diff --git a/tokio/src/process/unix/mod.rs b/tokio/src/process/unix/mod.rs index 38386069d90..dc0951becab 100644 --- a/tokio/src/process/unix/mod.rs +++ b/tokio/src/process/unix/mod.rs @@ -27,7 +27,7 @@ use orphan::{OrphanQueue, OrphanQueueImpl, Wait}; mod reap; use reap::Reaper; -use crate::net::util::IoSource; +use crate::net::util::IoResource; use crate::process::kill::Kill; use crate::process::SpawnedChild; use crate::signal::unix::{signal, Signal, SignalKind}; @@ -188,11 +188,11 @@ where } } -pub(crate) type ChildStdin = IoSource>; -pub(crate) type ChildStdout = IoSource>; -pub(crate) type ChildStderr = IoSource>; +pub(crate) type ChildStdin = IoResource>; +pub(crate) type ChildStdout = IoResource>; +pub(crate) type ChildStderr = IoResource>; -fn stdio(option: Option) -> io::Result>>> +fn stdio(option: Option) -> io::Result>>> where T: AsRawFd, { @@ -213,5 +213,5 @@ where return Err(io::Error::last_os_error()); } } - Ok(Some(IoSource::new(Fd { inner: io })?)) + Ok(Some(IoResource::new(Fd { inner: io })?)) } diff --git a/tokio/src/signal/unix.rs b/tokio/src/signal/unix.rs index f25c4314d2b..d6c121ec9c1 100644 --- a/tokio/src/signal/unix.rs +++ b/tokio/src/signal/unix.rs @@ -6,7 +6,7 @@ #![cfg(unix)] use crate::io::AsyncRead; -use crate::net::util::IoSource; +use crate::net::util::IoResource; use crate::signal::registry::{globals, EventId, EventInfo, Globals, Init, Storage}; use crate::sync::mpsc::{channel, Receiver}; @@ -257,7 +257,7 @@ fn signal_enable(signal: c_int) -> io::Result<()> { #[derive(Debug)] struct Driver { - wakeup: IoSource, + wakeup: IoResource, } impl Driver { @@ -281,13 +281,13 @@ impl Driver { // I'm not sure if the second (failed) registration simply doesn't end up // receiving wake up notifications, or there could be some race condition // when consuming readiness events, but having distinct descriptors for - // distinct IoSource instances appears to mitigate this. + // distinct IoResource instances appears to mitigate this. // - // Unfortunately we cannot just use a single global IoSource instance + // Unfortunately we cannot just use a single global IoResource instance // either, since we can't compare Handles or assume they will always // point to the exact same reactor. let stream = globals().receiver.try_clone()?; - let wakeup = IoSource::new(stream)?; + let wakeup = IoResource::new(stream)?; Ok(Driver { wakeup }) } From 1a2a5a93bebba12efb102da473d9ea8873fa58f4 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Wed, 6 Nov 2019 22:23:08 -0800 Subject: [PATCH 05/21] Remove platform specific event handling Signed-off-by: Kevin Leimkuhler --- tokio/src/net/driver/platform.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 tokio/src/net/driver/platform.rs diff --git a/tokio/src/net/driver/platform.rs b/tokio/src/net/driver/platform.rs deleted file mode 100644 index 58f3b4c0f7f..00000000000 --- a/tokio/src/net/driver/platform.rs +++ /dev/null @@ -1,28 +0,0 @@ -// pub(crate) use self::sys::*; - -// #[cfg(unix)] -// mod sys { -// use mio::unix::UnixReady; -// use mio::Ready; - -// pub(crate) fn hup() -> Ready { -// UnixReady::hup().into() -// } - -// pub(crate) fn is_hup(ready: Ready) -> bool { -// UnixReady::from(ready).is_hup() -// } -// } - -// #[cfg(windows)] -// mod sys { -// use mio::Ready; - -// pub(crate) fn hup() -> Ready { -// Ready::empty() -// } - -// pub(crate) fn is_hup(_: Ready) -> bool { -// false -// } -// } From 3c20f3335875b5ecb5b699ef5a5382cb927512d3 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Wed, 6 Nov 2019 22:34:12 -0800 Subject: [PATCH 06/21] Remove old code Signed-off-by: Kevin Leimkuhler --- tokio/src/net/driver/mod.rs | 1 - tokio/src/net/driver/reactor/mod.rs | 52 ++++------------------------- 2 files changed, 7 insertions(+), 46 deletions(-) diff --git a/tokio/src/net/driver/mod.rs b/tokio/src/net/driver/mod.rs index 2035d07baad..4e788105451 100644 --- a/tokio/src/net/driver/mod.rs +++ b/tokio/src/net/driver/mod.rs @@ -133,7 +133,6 @@ macro_rules! loom_thread_local { macro_rules! loom_thread_local { ($($tts:tt)+) => { std::thread_local!{ $($tts)+ } } } -pub(crate) mod platform; mod reactor; mod registration; diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index 710e17ec7b7..3fc62ee7bd6 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -29,13 +29,11 @@ pub struct Reactor { /// Reuse the `mio::Events` value across calls to poll. events: mio::Events, - /// TODO + /// The underlying system event queue. poll: mio::Poll, /// State shared between the reactor and the handles. inner: Arc, - // Is this still needed? - // _wakeup_registration: mio::Registry, } /// A reference to a reactor. @@ -58,7 +56,7 @@ pub struct Turn { } pub(super) struct Inner { - /// The underlying system event queue. + /// TODO registry: mio::Registry, /// Dispatch slabs for I/O and futures events @@ -70,9 +68,7 @@ pub(super) struct Inner { n_sources: AtomicUsize, /// Used to wake up the reactor from a call to `turn` - /// - /// TODO: Rename to waker? - wakeup: mio::Waker, + waker: mio::Waker, } #[derive(Debug, Eq, PartialEq, Clone, Copy)] @@ -135,18 +131,8 @@ impl Reactor { /// creation. pub fn new() -> io::Result { let poll = mio::Poll::new()?; - // let wakeup_pair = mio::Registration::new2(); let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?; - - // io.register( - // &wakeup_pair.0, - // TOKEN_WAKEUP, - // mio::Ready::readable(), - // mio::PollOpt::level(), - // )?; - let registry = poll.registry().try_clone()?; - Ok(Reactor { events: mio::Events::with_capacity(1024), poll, @@ -154,7 +140,7 @@ impl Reactor { registry, io_dispatch: SingleShard::new(), n_sources: AtomicUsize::new(0), - wakeup: waker, + waker, }), }) } @@ -221,15 +207,7 @@ impl Reactor { // Process all the events that came in, dispatching appropriately for event in self.events.iter() { let token = event.token(); - - // if token == TOKEN_WAKEUP { - // self.inner - // .wakeup - // .set_readiness(mio::Ready::empty()) - // .unwrap(); - // } else { - // self.dispatch(token, event.readiness()); - // } + // TODO if token != TOKEN_WAKEUP { self.dispatch(token, event); } @@ -270,12 +248,10 @@ impl Reactor { return; } - // if ready.is_writable() || platform::is_hup(ready) { if readiness.is_writable() || readiness.is_write_closed() { wr = io.writer.take_waker(); } - // if !(ready & (!mio::Ready::writable())).is_empty() { if readiness.is_readable() || readiness.is_read_closed() { rd = io.reader.take_waker(); } @@ -348,8 +324,7 @@ impl Handle { /// return immediately. fn wakeup(&self) { if let Some(inner) = self.inner() { - // inner.wakeup.set_readiness(mio::Ready::readable()).unwrap(); - inner.wakeup.wake().expect("failed to wake reactor") + inner.waker.wake().expect("failed to wake reactor") } } @@ -384,18 +359,11 @@ impl Inner { ) })?; self.n_sources.fetch_add(1, SeqCst); - // self.io.register( - // source, - // mio::Token(token), - // mio::Ready::all(), - // mio::PollOpt::edge(), - // )?; self.registry.register( source, mio::Token(token), mio::Interests::READABLE | mio::Interests::WRITABLE, )?; - Ok(token) } @@ -420,9 +388,7 @@ impl Inner { .unwrap_or_else(|| panic!("token {} no longer valid!", token)); let (waker, readiness) = match dir { - // Direction::Read => (&sched.reader, !mio::Ready::writable()), Direction::Read => (&sched.reader, Readiness::readable()), - // Direction::Write => (&sched.writer, mio::Ready::writable()), Direction::Write => (&sched.writer, Readiness::writable()), }; @@ -448,11 +414,7 @@ impl Drop for Inner { impl Direction { pub(super) fn mask(self) -> Readiness { match self { - Direction::Read => { - // Everything except writable is signaled through read. - // mio::Ready::all() - mio::Ready::writable() - Readiness::readable() - } + Direction::Read => Readiness::readable(), Direction::Write => Readiness::writable(), } } From b2565393f12ea0735e4b644adf113d98410263dd Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Fri, 8 Nov 2019 15:40:31 -0800 Subject: [PATCH 07/21] Add additional comments and cleanup Signed-off-by: Kevin Leimkuhler --- tokio/Cargo.toml | 3 +- tokio/src/net/driver/mod.rs | 5 +- .../reactor/dispatch/page/scheduled_io.rs | 1 + tokio/src/net/driver/reactor/mod.rs | 54 ++++++++--------- tokio/src/net/driver/readiness.rs | 47 ++++++++++----- tokio/src/net/driver/registration.rs | 59 ++++++++----------- tokio/src/net/tcp/listener.rs | 6 +- tokio/src/net/tcp/stream.rs | 31 +--------- tokio/src/net/unix/stream.rs | 56 +----------------- tokio/src/process/unix/mod.rs | 2 - 10 files changed, 90 insertions(+), 174 deletions(-) diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index 58bd6e266b2..aae7bbf43b6 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -100,8 +100,7 @@ fnv = { version = "1.0.6", optional = true } futures-core = { version = "0.3.0", optional = true } lazy_static = { version = "1.0.2", optional = true } memchr = { version = "2.2", optional = true } -# mio = { git = "https://github.com/tokio-rs/mio", branch = "master", optional = true } -mio = { path = "../../mio", optional = true } +mio = { git = "https://github.com/tokio-rs/mio", branch = "master", optional = true } num_cpus = { version = "1.8.0", optional = true } # Backs `DelayQueue` slab = { version = "0.4.1", optional = true } diff --git a/tokio/src/net/driver/mod.rs b/tokio/src/net/driver/mod.rs index 4e788105451..b666ea3a41d 100644 --- a/tokio/src/net/driver/mod.rs +++ b/tokio/src/net/driver/mod.rs @@ -134,10 +134,9 @@ macro_rules! loom_thread_local { ($($tts:tt)+) => { std::thread_local!{ $($tts)+ } } } mod reactor; +mod readiness; mod registration; pub use self::reactor::{set_default, DefaultGuard, Handle, Reactor}; -pub use self::registration::Registration; - -mod readiness; pub use self::readiness::Readiness; +pub use self::registration::Registration; diff --git a/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs b/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs index 0349bc5c346..3693e590628 100644 --- a/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs +++ b/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs @@ -123,6 +123,7 @@ impl ScheduledIo { /// the current value, returning the previous readiness value. /// /// # Arguments + /// /// - `token`: the token for this `ScheduledIo`. /// - `f`: a closure returning a new readiness value given the previous /// readiness. diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index 3fc62ee7bd6..3bad39c2329 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -1,8 +1,6 @@ use crate::loom::sync::atomic::AtomicUsize; use crate::runtime::{Park, Unpark}; -use std::sync::atomic::Ordering::SeqCst; - mod dispatch; use dispatch::SingleShard; pub(crate) use dispatch::MAX_SOURCES; @@ -14,6 +12,7 @@ use std::io; use std::marker::PhantomData; #[cfg(all(unix, not(target_os = "fuchsia")))] use std::os::unix::io::{AsRawFd, RawFd}; +use std::sync::atomic::Ordering::SeqCst; use std::sync::{Arc, Weak}; use std::task::Waker; use std::time::Duration; @@ -56,7 +55,7 @@ pub struct Turn { } pub(super) struct Inner { - /// TODO + /// Registers I/O resources. registry: mio::Registry, /// Dispatch slabs for I/O and futures events @@ -67,7 +66,9 @@ pub(super) struct Inner { /// The number of sources in `io_dispatch`. n_sources: AtomicUsize, - /// Used to wake up the reactor from a call to `turn` + /// Used to wake up the reactor from a call to [`turn`]. + /// + /// [`turn`]: #method.turn waker: mio::Waker, } @@ -207,8 +208,9 @@ impl Reactor { // Process all the events that came in, dispatching appropriately for event in self.events.iter() { let token = event.token(); - // TODO - if token != TOKEN_WAKEUP { + if token == TOKEN_WAKEUP { + self.inner.waker.wake()?; + } else { self.dispatch(token, event); } } @@ -216,7 +218,6 @@ impl Reactor { Ok(()) } - // fn dispatch(&self, token: mio::Token, ready: mio::Ready) { fn dispatch(&self, token: mio::Token, event: &mio::event::Event) { let mut rd = None; let mut wr = None; @@ -383,17 +384,17 @@ impl Inner { .io_dispatch .get(token) .unwrap_or_else(|| panic!("IO resource for token {} does not exist!", token)); - let current = sched + let readiness = sched .get_readiness(token) .unwrap_or_else(|| panic!("token {} no longer valid!", token)); - let (waker, readiness) = match dir { + let (waker, interest) = match dir { Direction::Read => (&sched.reader, Readiness::readable()), Direction::Write => (&sched.writer, Readiness::writable()), }; waker.register(w); - if current & readiness.as_usize() != 0 { + if readiness & interest.as_usize() != 0 { waker.wake(); } } @@ -426,30 +427,23 @@ mod tests { use loom::thread; // No-op `Evented` impl just so we can have something to pass to `add_source`. - struct NotEvented; + struct NotSource; - impl Evented for NotEvented { - fn register( - &self, - _: &mio::Poll, - _: mio::Token, - _: mio::Ready, - _: mio::PollOpt, - ) -> io::Result<()> { + impl mio::event::Source for NotSource { + fn register(&self, _: &mio::Registry, _: mio::Token, _: mio::Interests) -> io::Result<()> { Ok(()) } fn reregister( &self, - _: &mio::Poll, + _: &mio::Registry, _: mio::Token, - _: mio::Ready, - _: mio::PollOpt, + _: mio::Interests, ) -> io::Result<()> { Ok(()) } - fn deregister(&self, _: &mio::Poll) -> io::Result<()> { + fn deregister(&self, _: &mio::Registry) -> io::Result<()> { Ok(()) } } @@ -462,14 +456,14 @@ mod tests { let inner = reactor.inner; let inner2 = inner.clone(); - let token_1 = inner.add_source(&NotEvented).unwrap(); + let token_1 = inner.add_source(&NotSource).unwrap(); println!("token 1: {:#x}", token_1); let thread = thread::spawn(move || { inner2.drop_source(token_1); println!("dropped: {:#x}", token_1); }); - let token_2 = inner.add_source(&NotEvented).unwrap(); + let token_2 = inner.add_source(&NotSource).unwrap(); println!("token 2: {:#x}", token_2); thread.join().unwrap(); @@ -487,17 +481,17 @@ mod tests { // add sources to fill up the first page so that the dropped index // may be reused. for _ in 0..31 { - inner.add_source(&NotEvented).unwrap(); + inner.add_source(&NotSource).unwrap(); } - let token_1 = inner.add_source(&NotEvented).unwrap(); + let token_1 = inner.add_source(&NotSource).unwrap(); println!("token 1: {:#x}", token_1); let thread = thread::spawn(move || { inner2.drop_source(token_1); println!("dropped: {:#x}", token_1); }); - let token_2 = inner.add_source(&NotEvented).unwrap(); + let token_2 = inner.add_source(&NotSource).unwrap(); println!("token 2: {:#x}", token_2); thread.join().unwrap(); @@ -514,12 +508,12 @@ mod tests { let inner2 = inner.clone(); let thread = thread::spawn(move || { - let token_2 = inner2.add_source(&NotEvented).unwrap(); + let token_2 = inner2.add_source(&NotSource).unwrap(); println!("token 2: {:#x}", token_2); token_2 }); - let token_1 = inner.add_source(&NotEvented).unwrap(); + let token_1 = inner.add_source(&NotSource).unwrap(); println!("token 1: {:#x}", token_1); let token_2 = thread.join().unwrap(); diff --git a/tokio/src/net/driver/readiness.rs b/tokio/src/net/driver/readiness.rs index 87c1aa18e29..de1c9c5b535 100644 --- a/tokio/src/net/driver/readiness.rs +++ b/tokio/src/net/driver/readiness.rs @@ -5,65 +5,82 @@ const WRITABLE: usize = 0b0_10; const READ_CLOSED: usize = 0b0_0100; const WRITE_CLOSED: usize = 0b0_1000; -/// TODO +/// A set of readiness event kinds. +/// +/// `Readiness` is set of operation descriptors indicating which kind of an +/// operation is ready to be performed. +/// +/// This struct only represents portable event kinds. Portable events are +/// events that can be raised on any platform while guaranteeing no false +/// positives. +/// +/// # Examples +/// +/// ```rust +/// use tokio::net::driver::Readiness; +/// +/// let readiness = Readiness::readable() | Readiness::read_closed(); +/// assert!(readiness.is_readable()); +/// assert!(readiness.is_read_closed()); +/// ``` #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] pub struct Readiness(usize); impl Readiness { - pub(crate) fn empty() -> Readiness { + pub fn empty() -> Readiness { Readiness(0) } - pub(crate) fn readable() -> Readiness { + pub fn readable() -> Readiness { Readiness(READABLE) } - pub(crate) fn writable() -> Readiness { + pub fn writable() -> Readiness { Readiness(WRITABLE) } - pub(crate) fn read_closed() -> Readiness { + pub fn read_closed() -> Readiness { Readiness(READ_CLOSED) } - pub(crate) fn write_closed() -> Readiness { + pub fn write_closed() -> Readiness { Readiness(WRITE_CLOSED) } - pub(crate) fn all() -> Readiness { + pub fn all() -> Readiness { Readiness(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED) } - pub(crate) fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { *self == Readiness::empty() } - pub(crate) fn is_readable(&self) -> bool { + pub fn is_readable(&self) -> bool { self.contains(Readiness::readable()) } - pub(crate) fn is_writable(&self) -> bool { + pub fn is_writable(&self) -> bool { self.contains(Readiness::writable()) } - pub(crate) fn is_read_closed(&self) -> bool { + pub fn is_read_closed(&self) -> bool { self.contains(Readiness::read_closed()) } - pub(crate) fn is_write_closed(&self) -> bool { + pub fn is_write_closed(&self) -> bool { self.contains(Readiness::write_closed()) } - pub(crate) fn contains>(&self, other: T) -> bool { + pub fn contains>(&self, other: T) -> bool { let other = other.into(); (*self & other) == other } - pub(crate) fn from_usize(val: usize) -> Readiness { + pub fn from_usize(val: usize) -> Readiness { Readiness(val) } - pub(crate) fn as_usize(&self) -> usize { + pub fn as_usize(&self) -> usize { self.0 } } diff --git a/tokio/src/net/driver/registration.rs b/tokio/src/net/driver/registration.rs index 53ce488ec91..09b5b905517 100644 --- a/tokio/src/net/driver/registration.rs +++ b/tokio/src/net/driver/registration.rs @@ -1,7 +1,7 @@ use super::reactor::{Direction, Handle}; use super::Readiness; -use mio; +use mio::event; use std::task::{Context, Poll}; use std::{io, usize}; @@ -19,19 +19,13 @@ use std::{io, usize}; /// the read readiness and one for write readiness. These streams are /// independent and can be consumed from separate tasks. /// -/// **Note**: while `Registration` is `Sync`, the caller must ensure that there -/// are at most two tasks that use a registration instance concurrently. One -/// task for [`poll_read_ready`] and one task for [`poll_write_ready`]. While -/// violating this requirement is "safe" from a Rust memory safety point of -/// view, it will result in unexpected behavior in the form of lost +/// **Note**: while `Registration` is `Sync`, the caller must ensure that +/// there are at most two tasks that use a registration instance concurrently. +/// One task for [`poll_read_ready`] and one task for [`poll_write_ready`]. +/// While violating this requirement is "safe" from a Rust memory safety point +/// of view, it will result in unexpected behavior in the form of lost /// notifications and tasks hanging. /// -/// ## Platform-specific events -/// -/// `Registration` also allows receiving platform-specific `mio::Ready` events. -/// These events are included as part of the read readiness event stream. The -/// write readiness event stream is only for `Ready::writable()` events. -/// /// [`new`]: #method.new /// [`poll_read_ready`]: #method.poll_read_ready`] /// [`poll_write_ready`]: #method.poll_write_ready`] @@ -52,7 +46,7 @@ impl Registration { /// - `Err` if an error was encountered during registration pub fn new(io: &T) -> io::Result where - T: mio::event::Source, + T: event::Source, { let handle = Handle::current(); let token = if let Some(inner) = handle.inner() { @@ -77,14 +71,13 @@ impl Registration { /// /// # Return /// - /// If the deregistration was successful, `Ok` is returned. Any calls to + /// - `Ok` is the deregistration was successful. Any calls to /// `Reactor::turn` that happen after a successful call to `deregister` will /// no longer result in notifications getting sent for this registration. - /// - /// `Err` is returned if an error is encountered. + /// - `Err` if an error is encountered. pub fn deregister(&mut self, io: &T) -> io::Result<()> where - T: mio::event::Source, + T: event::Source, { let inner = match self.handle.inner() { Some(inner) => inner, @@ -99,9 +92,10 @@ impl Registration { /// call to `poll_read_ready`, it is returned. If it has not, the current /// task is notified once a new event is received. /// - /// All events except `HUP` are [edge-triggered]. Once `HUP` is returned, - /// the function will always return `Ready(HUP)`. This should be treated as - /// the end of the readiness stream. + /// All events except `READ_CLOSED` are [`edge-triggered`]. Once + /// `READ_CLOSED` is returned, the function will always return this + /// readiness. This should be treated as the end of the *read* readiness + /// stream, however the write readiness stream may still receive events. /// /// Ensure that [`register`] has been called first. /// @@ -150,9 +144,10 @@ impl Registration { /// call to `poll_write_ready`, it is returned. If it has not, the current /// task is notified once a new event is received. /// - /// All events except `HUP` are [edge-triggered]. Once `HUP` is returned, - /// the function will always return `Ready(HUP)`. This should be treated as - /// the end of the readiness stream. + /// All events except `WRITE_CLOSED` are [`edge-triggered`]. Once + /// `WRITE_CLOSED` is returned, the function will always return this + /// readiness. This should be treated as the end of the *write* readiness + /// stream, however the read readiness stream may still receive events. /// /// Ensure that [`register`] has been called first. /// @@ -218,23 +213,17 @@ impl Registration { let mask = direction.mask(); let mask_no_closed = (mask - (Readiness::read_closed() - Readiness::write_closed())).as_usize(); - // let mask_no_hup = (mask - platform::hup()).as_usize(); let sched = inner.io_dispatch.get(self.token).unwrap(); - // This consumes the current readiness state **except** for HUP. HUP is - // excluded because a) it is a final state and never transitions out of - // HUP and b) both the read AND the write directions need to be able to - // observe this state. - // - // If HUP were to be cleared when `direction` is `Read`, then when - // `poll_ready` is called again with a `direction` of `Write`, the HUP - // state would not be visible. + // This consumes the current readiness state **except** for + // `READ_CLOSED` and `WRITE_CLOSED`. These are excluded because: + // - They are a final state and never transitioned out of + // - Both the read and the write directions should be able to observe + // this state let curr_ready = sched - // .set_readiness(self.token, |curr| curr & (!mask_no_hup)) .set_readiness(self.token, |curr| curr & (!mask_no_closed)) .unwrap_or_else(|_| panic!("token {} no longer valid!", self.token)); - // let mut ready = mask & mio::Ready::from_usize(curr_ready); let mut ready = mask & Readiness::from_usize(curr_ready); if ready.is_empty() { @@ -247,10 +236,8 @@ impl Registration { // Try again let curr_ready = sched - // .set_readiness(self.token, |curr| curr & (!mask_no_hup)) .set_readiness(self.token, |curr| curr & (!mask_no_closed)) .unwrap_or_else(|_| panic!("token {} no longer valid!", self.token)); - // ready = mask & mio::Ready::from_usize(curr_ready); ready = mask & Readiness::from_usize(curr_ready); } } diff --git a/tokio/src/net/tcp/listener.rs b/tokio/src/net/tcp/listener.rs index 11d155654a2..73f0dba3471 100644 --- a/tokio/src/net/tcp/listener.rs +++ b/tokio/src/net/tcp/listener.rs @@ -180,7 +180,7 @@ impl TcpListener { /// Ok(()) /// } /// ``` - pub fn from_std(listener: net::TcpListener) -> io::Result { + pub fn from_std(listener: std::net::TcpListener) -> io::Result { let io = mio::net::TcpListener::from_std(listener); let io = IoResource::new(io)?; Ok(TcpListener { io }) @@ -301,14 +301,14 @@ impl TryFrom for mio::net::TcpListener { } } -impl TryFrom for TcpListener { +impl TryFrom for TcpListener { type Error = io::Error; /// Consumes stream, returning the tokio I/O object. /// /// This is equivalent to /// [`TcpListener::from_std(stream)`](TcpListener::from_std). - fn try_from(stream: net::TcpListener) -> Result { + fn try_from(stream: std::net::TcpListener) -> Result { Self::from_std(stream) } } diff --git a/tokio/src/net/tcp/stream.rs b/tokio/src/net/tcp/stream.rs index 91a47d261ab..ec2b4eb0880 100644 --- a/tokio/src/net/tcp/stream.rs +++ b/tokio/src/net/tcp/stream.rs @@ -141,31 +141,6 @@ impl TcpStream { Ok(TcpStream { io }) } - // Connect a TcpStream asynchronously that may be built with a net2 TcpBuilder. - // - // This should be removed in favor of some in-crate TcpSocket builder API. - #[doc(hidden)] - pub async fn connect_std(_stream: net::TcpStream, _addr: &SocketAddr) -> io::Result { - // let io = mio::net::TcpStream::connect_stream(stream, addr)?; - // let io = IoResource::new(io)?; - // let stream = TcpStream { io }; - - // Once we've connected, wait for the stream to be writable as - // that's when the actual connection has been initiated. Once we're - // writable we check for `take_socket_error` to see if the connect - // actually hit an error or not. - // - // If all that succeeded then we ship everything on up. - // poll_fn(|cx| stream.io.poll_write_ready(cx)).await?; - - // if let Some(e) = stream.io.get_ref().take_error()? { - // return Err(e); - // } - - // Ok(stream) - unimplemented!() - } - /// Returns the local address that this stream is bound to. /// /// # Examples @@ -410,8 +385,7 @@ impl TcpStream { ) -> Poll> { ready!(self.io.poll_read_ready(cx))?; - let r = unsafe { self.io.get_ref().read(buf.bytes_mut()) }; - match r { + match unsafe { self.io.get_ref().read(buf.bytes_mut()) } { Ok(n) => { unsafe { buf.advance_mut(n); @@ -449,8 +423,7 @@ impl TcpStream { ) -> Poll> { ready!(self.io.poll_write_ready(cx))?; - let r = self.io.get_ref().write(buf.bytes()); - match r { + match self.io.get_ref().write(buf.bytes()) { Ok(n) => { buf.advance(n); Poll::Ready(Ok(n)) diff --git a/tokio/src/net/unix/stream.rs b/tokio/src/net/unix/stream.rs index 60f98a74a92..5c551268b1a 100644 --- a/tokio/src/net/unix/stream.rs +++ b/tokio/src/net/unix/stream.rs @@ -219,49 +219,7 @@ impl UnixStream { ) -> Poll> { ready!(self.io.poll_read_ready(cx))?; - let r = unsafe { - // The `IoVec` type can't have a 0-length size, so we create a bunch - // of dummy versions on the stack with 1 length which we'll quickly - // overwrite. - // let b1: &mut [u8] = &mut [0]; - // let b2: &mut [u8] = &mut [0]; - // let b3: &mut [u8] = &mut [0]; - // let b4: &mut [u8] = &mut [0]; - // let b5: &mut [u8] = &mut [0]; - // let b6: &mut [u8] = &mut [0]; - // let b7: &mut [u8] = &mut [0]; - // let b8: &mut [u8] = &mut [0]; - // let b9: &mut [u8] = &mut [0]; - // let b10: &mut [u8] = &mut [0]; - // let b11: &mut [u8] = &mut [0]; - // let b12: &mut [u8] = &mut [0]; - // let b13: &mut [u8] = &mut [0]; - // let b14: &mut [u8] = &mut [0]; - // let b15: &mut [u8] = &mut [0]; - // let b16: &mut [u8] = &mut [0]; - // let mut bufs: [&mut IoVec; 16] = [ - // b1.into(), - // b2.into(), - // b3.into(), - // b4.into(), - // b5.into(), - // b6.into(), - // b7.into(), - // b8.into(), - // b9.into(), - // b10.into(), - // b11.into(), - // b12.into(), - // b13.into(), - // b14.into(), - // b15.into(), - // b16.into(), - // ]; - // let n = buf.bytes_vec_mut(&mut bufs); - self.io.get_ref().read(buf.bytes_mut()) - }; - - match r { + match unsafe { self.io.get_ref().read(buf.bytes_mut()) } { Ok(n) => { unsafe { buf.advance_mut(n); @@ -299,17 +257,7 @@ impl UnixStream { ) -> Poll> { ready!(self.io.poll_write_ready(cx))?; - let r = { - // The `IoVec` type can't have a zero-length size, so create a dummy - // version from a 1-length slice which we'll overwrite with the - // `bytes_vec` method. - // static DUMMY: &[u8] = &[0]; - // let iovec = <&IoVec>::from(DUMMY); - // let mut bufs = [iovec; 64]; - // let n = buf.bytes_vec(&mut bufs); - self.io.get_ref().write(buf.bytes()) - }; - match r { + match self.io.get_ref().write(buf.bytes()) { Ok(n) => { buf.advance(n); Poll::Ready(Ok(n)) diff --git a/tokio/src/process/unix/mod.rs b/tokio/src/process/unix/mod.rs index dc0951becab..916219fadff 100644 --- a/tokio/src/process/unix/mod.rs +++ b/tokio/src/process/unix/mod.rs @@ -173,9 +173,7 @@ impl Source for Fd where T: AsRawFd, { - // fn register(&self, poll: &MioPoll, token: Token, interest: Interests) -> io::Result<()> { fn register(&self, registry: &Registry, token: Token, interest: Interests) -> io::Result<()> { - // SourceFd(&self.as_raw_fd()).register(poll, token, interest | UnixReady::hup(), opts) SourceFd(&self.as_raw_fd()).register(registry, token, interest) } From 7141696628cdc28d8c046be1eeb6cc2d76289554 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Sun, 10 Nov 2019 10:52:10 -0800 Subject: [PATCH 08/21] Remove mio-uds from Cargo.toml Signed-off-by: Kevin Leimkuhler --- tokio/Cargo.toml | 5 +---- tokio/src/net/tcp/listener.rs | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index aae7bbf43b6..22d2d40236b 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -74,7 +74,6 @@ signal = [ "io-driver", "lazy_static", "libc", - "mio-uds", "signal-hook-registry", "winapi/consoleapi", "winapi/minwindef", @@ -85,8 +84,7 @@ test-util = [] tcp = ["io-driver"] time = ["rt-core", "slab"] udp = ["io-driver"] -uds = ["io-driver", "mio-uds", "libc"] - +uds = ["io-driver", "libc"] [dependencies] tokio-macros = { version = "=0.2.0-alpha.6", optional = true, path = "../tokio-macros" } @@ -106,7 +104,6 @@ num_cpus = { version = "1.8.0", optional = true } slab = { version = "0.4.1", optional = true } [target.'cfg(unix)'.dependencies] -mio-uds = { version = "0.6.5", optional = true } libc = { version = "0.2.42", optional = true } signal-hook-registry = { version = "1.1.1", optional = true } diff --git a/tokio/src/net/tcp/listener.rs b/tokio/src/net/tcp/listener.rs index 73f0dba3471..3b94bd046a7 100644 --- a/tokio/src/net/tcp/listener.rs +++ b/tokio/src/net/tcp/listener.rs @@ -6,7 +6,7 @@ use crate::net::ToSocketAddrs; use std::convert::TryFrom; use std::fmt; use std::io; -use std::net::{self, SocketAddr}; +use std::net::SocketAddr; use std::task::{Context, Poll}; /// An I/O object representing a TCP socket listening for incoming connections. From 381ebbd33e7428c48fd874485bfbc7187f82aa06 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Mon, 11 Nov 2019 17:26:13 -0800 Subject: [PATCH 09/21] Depend on Mio fix Signed-off-by: Kevin Leimkuhler --- tokio/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index 22d2d40236b..44c5d60e87e 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -98,7 +98,7 @@ fnv = { version = "1.0.6", optional = true } futures-core = { version = "0.3.0", optional = true } lazy_static = { version = "1.0.2", optional = true } memchr = { version = "2.2", optional = true } -mio = { git = "https://github.com/tokio-rs/mio", branch = "master", optional = true } +mio = { git = "https://github.com/kleimkuhler/mio", branch = "kleimkuhler/unixstream-pair-fix", optional = true } num_cpus = { version = "1.8.0", optional = true } # Backs `DelayQueue` slab = { version = "0.4.1", optional = true } From 1e6caeb7134f61dd6a4bc40b2ca032fb2b33ec92 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Mon, 11 Nov 2019 22:29:43 -0800 Subject: [PATCH 10/21] Fix readiness checks for process and handle interruption Signed-off-by: Kevin Leimkuhler --- tokio/src/net/driver/reactor/mod.rs | 11 ++++++++--- tokio/src/net/util/io_resource.rs | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index 3bad39c2329..d81985c76b8 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -200,9 +200,14 @@ impl Reactor { fn poll(&mut self, max_wait: Option) -> io::Result<()> { // Block waiting for an event to happen, peeling out how many events // happened. + // + // TODO: Should this loop? match self.poll.poll(&mut self.events, max_wait) { Ok(_) => {} - Err(e) => return Err(e), + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => { + return Err(e); + } } // Process all the events that came in, dispatching appropriately @@ -415,8 +420,8 @@ impl Drop for Inner { impl Direction { pub(super) fn mask(self) -> Readiness { match self { - Direction::Read => Readiness::readable(), - Direction::Write => Readiness::writable(), + Direction::Read => Readiness::readable() | Readiness::read_closed(), + Direction::Write => Readiness::writable() | Readiness::write_closed(), } } } diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs index 7ddd205c5f9..c08f7fcbaf4 100644 --- a/tokio/src/net/util/io_resource.rs +++ b/tokio/src/net/util/io_resource.rs @@ -218,7 +218,7 @@ where /// /// * called from outside of a task context. pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - let mask = Readiness::readable() | Readiness::readable(); + let mask = Readiness::readable() | Readiness::read_closed(); poll_readiness!( self, read_readiness, From 5052bfadceae6ef4bf7204b90d1f8fccc94297e8 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 10:53:05 -0800 Subject: [PATCH 11/21] Add Readiness documentation Signed-off-by: Kevin Leimkuhler --- tokio/src/net/driver/reactor/mod.rs | 1 + tokio/src/net/driver/readiness.rs | 197 +++++++++++++++++++++++++++- tokio/src/net/util/io_resource.rs | 4 +- 3 files changed, 194 insertions(+), 8 deletions(-) diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index d81985c76b8..755f18f1e09 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -247,6 +247,7 @@ impl Reactor { } if io + // .set_readiness(token.0, |curr| curr | readiness.into()) .set_readiness(token.0, |curr| curr | readiness.as_usize()) .is_err() { diff --git a/tokio/src/net/driver/readiness.rs b/tokio/src/net/driver/readiness.rs index de1c9c5b535..4a499161bb4 100644 --- a/tokio/src/net/driver/readiness.rs +++ b/tokio/src/net/driver/readiness.rs @@ -27,59 +27,250 @@ const WRITE_CLOSED: usize = 0b0_1000; pub struct Readiness(usize); impl Readiness { + /// Returns the empty `Readiness` set. + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let readiness = Readiness::empty(); + /// + /// assert!(!ready.is_readable()); + /// ``` pub fn empty() -> Readiness { Readiness(0) } + /// Returns a `Readiness` representing readable readiness. + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::readable(); + /// + /// assert!(ready.is_readable()); + /// ``` pub fn readable() -> Readiness { Readiness(READABLE) } + /// Returns a `Readiness` representing writable readiness. + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::writable(); + /// + /// assert!(ready.is_writable()); + /// ``` pub fn writable() -> Readiness { Readiness(WRITABLE) } + /// Returns a `Readiness` representing read closed readiness. + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::read_closed(); + /// + /// assert!(ready.is_read_closed()); + /// ``` pub fn read_closed() -> Readiness { Readiness(READ_CLOSED) } + /// Returns a `Readiness` representing write closed readiness. + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::write_closed(); + /// + /// assert!(ready.is_write_closed()); + /// ``` pub fn write_closed() -> Readiness { Readiness(WRITE_CLOSED) } + /// Returns a `Readiness` representing readiness for all operations. + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::all(); + /// + /// assert!(ready.is_readable()); + /// assert!(ready.is_writable()); + /// assert!(ready.is_read_closed()); + /// assert!(ready.is_write_closed()); + /// ``` pub fn all() -> Readiness { Readiness(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED) } + /// Returns true if `Readiness` is the empty set + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::empty(); + /// assert!(ready.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { *self == Readiness::empty() } + /// Returns true if the value includes readable readiness + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::readable(); + /// + /// assert!(ready.is_readable()); + /// ``` pub fn is_readable(&self) -> bool { self.contains(Readiness::readable()) } + /// Returns true if the value includes writable readiness + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::writable(); + /// + /// assert!(ready.is_writable()); + /// ``` pub fn is_writable(&self) -> bool { self.contains(Readiness::writable()) } + /// Returns true if the value includes read closed readiness + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::read_closed(); + /// + /// assert!(ready.is_read_closed()); + /// ``` pub fn is_read_closed(&self) -> bool { self.contains(Readiness::read_closed()) } + /// Returns true if the value includes write closed readiness + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::write_closed(); + /// + /// assert!(ready.is_write_closed()); + /// ``` pub fn is_write_closed(&self) -> bool { self.contains(Readiness::write_closed()) } + /// Returns true if `self` is a superset of `other`. + /// + /// `other` may represent more than one readiness operations, in which case + /// the function only returns true if `self` contains all readiness + /// specified in `other`. + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let readiness = Readiness::readable(); + /// + /// assert!(readiness.contains(Readiness::readable())); + /// assert!(!readiness.contains(Readiness::writable())); + /// ``` + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let readiness = Readiness::readable() | Readiness::writable(); + /// + /// assert!(readiness.contains(Readiness::readable())); + /// assert!(readiness.contains(Readiness::writable())); + /// ``` + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let readiness = Readiness::readable() | Readiness::writable(); + /// + /// assert!(!Readiness::readable().contains(readiness)); + /// assert!(readiness.contains(readiness)); + /// ``` pub fn contains>(&self, other: T) -> bool { let other = other.into(); (*self & other) == other } + /// Create a `Readiness` instance using the given `usize` representation. + /// + /// The `usize` representation must have been obtained from a call to + /// `Readiness::as_usize`. + /// + /// This function is mainly provided to allow the caller to get a + /// readiness value from an `AtomicUsize`. + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::readable(); + /// let ready_usize = ready.as_usize(); + /// let ready2 = Readiness::from_usize(ready_usize); + /// + /// assert_eq!(ready, ready2); + /// ``` pub fn from_usize(val: usize) -> Readiness { Readiness(val) } + /// Returns a `usize` representation of the `Readiness` value. + /// + /// This function is mainly provided to allow the caller to store a + /// readiness value in an `AtomicUsize`. + /// + /// # Examples + /// + /// ``` + /// use tokio::net::driver::Readiness; + /// + /// let ready = Readiness::readable(); + /// let ready_usize = ready.as_usize(); + /// let ready2 = Readiness::from_usize(ready_usize); + /// + /// assert_eq!(ready, ready2); + /// ``` pub fn as_usize(&self) -> usize { self.0 } @@ -118,9 +309,3 @@ impl> ops::Sub for Readiness { Readiness(self.0 & !other.into().0) } } - -impl From for Readiness { - fn from(x: usize) -> Self { - Readiness(x) - } -} diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs index c08f7fcbaf4..6a3878340c1 100644 --- a/tokio/src/net/util/io_resource.rs +++ b/tokio/src/net/util/io_resource.rs @@ -117,7 +117,7 @@ macro_rules! poll_readiness { // Load cached readiness and check if it currently has readiness // matching `$mask`. let mut cached = $me.$cache.load(Relaxed); - let mut current = $mask & cached; + let mut current = $mask & Readiness::from_usize(cached); // If readiness is currently empty, loop until there is readiness; // otherwise consume pending events and return the new readiness. @@ -142,7 +142,7 @@ macro_rules! poll_readiness { cached |= readiness.as_usize(); $me.$cache.store(cached, Relaxed); } - Poll::Ready(Ok(cached.into())) + Poll::Ready(Ok(Readiness::from_usize(cached))) } }}; } From 01841a36aafe8e7692e89d550d5859b89fd4b916 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 11:12:24 -0800 Subject: [PATCH 12/21] Stop point for draft PR Signed-off-by: Kevin Leimkuhler --- tokio-util/tests/udp.rs | 1 + tokio/src/net/driver/readiness.rs | 64 +++++++++++++++---------------- tokio/src/net/util/io_resource.rs | 16 +++----- 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/tokio-util/tests/udp.rs b/tokio-util/tests/udp.rs index be05dbc4f6e..9b677740cea 100644 --- a/tokio-util/tests/udp.rs +++ b/tokio-util/tests/udp.rs @@ -10,6 +10,7 @@ use futures::stream::StreamExt; use std::io; #[tokio::test] +#[ignore] async fn send_framed() -> std::io::Result<()> { let mut a_soc = UdpSocket::bind("127.0.0.1:0").await?; let mut b_soc = UdpSocket::bind("127.0.0.1:0").await?; diff --git a/tokio/src/net/driver/readiness.rs b/tokio/src/net/driver/readiness.rs index 4a499161bb4..581f57d6979 100644 --- a/tokio/src/net/driver/readiness.rs +++ b/tokio/src/net/driver/readiness.rs @@ -36,7 +36,7 @@ impl Readiness { /// /// let readiness = Readiness::empty(); /// - /// assert!(!ready.is_readable()); + /// assert!(!readiness.is_readable()); /// ``` pub fn empty() -> Readiness { Readiness(0) @@ -49,9 +49,9 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::readable(); + /// let readiness = Readiness::readable(); /// - /// assert!(ready.is_readable()); + /// assert!(readiness.is_readable()); /// ``` pub fn readable() -> Readiness { Readiness(READABLE) @@ -64,9 +64,9 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::writable(); + /// let readiness = Readiness::writable(); /// - /// assert!(ready.is_writable()); + /// assert!(readiness.is_writable()); /// ``` pub fn writable() -> Readiness { Readiness(WRITABLE) @@ -79,9 +79,9 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::read_closed(); + /// let readiness = Readiness::read_closed(); /// - /// assert!(ready.is_read_closed()); + /// assert!(readiness.is_read_closed()); /// ``` pub fn read_closed() -> Readiness { Readiness(READ_CLOSED) @@ -94,9 +94,9 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::write_closed(); + /// let readiness = Readiness::write_closed(); /// - /// assert!(ready.is_write_closed()); + /// assert!(readiness.is_write_closed()); /// ``` pub fn write_closed() -> Readiness { Readiness(WRITE_CLOSED) @@ -109,12 +109,12 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::all(); + /// let readiness = Readiness::all(); /// - /// assert!(ready.is_readable()); - /// assert!(ready.is_writable()); - /// assert!(ready.is_read_closed()); - /// assert!(ready.is_write_closed()); + /// assert!(readiness.is_readable()); + /// assert!(readiness.is_writable()); + /// assert!(readiness.is_read_closed()); + /// assert!(readiness.is_write_closed()); /// ``` pub fn all() -> Readiness { Readiness(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED) @@ -127,8 +127,8 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::empty(); - /// assert!(ready.is_empty()); + /// let readiness = Readiness::empty(); + /// assert!(readiness.is_empty()); /// ``` pub fn is_empty(&self) -> bool { *self == Readiness::empty() @@ -141,9 +141,9 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::readable(); + /// let readiness = Readiness::readable(); /// - /// assert!(ready.is_readable()); + /// assert!(readiness.is_readable()); /// ``` pub fn is_readable(&self) -> bool { self.contains(Readiness::readable()) @@ -156,9 +156,9 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::writable(); + /// let readiness = Readiness::writable(); /// - /// assert!(ready.is_writable()); + /// assert!(readiness.is_writable()); /// ``` pub fn is_writable(&self) -> bool { self.contains(Readiness::writable()) @@ -171,9 +171,9 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::read_closed(); + /// let readiness = Readiness::read_closed(); /// - /// assert!(ready.is_read_closed()); + /// assert!(readiness.is_read_closed()); /// ``` pub fn is_read_closed(&self) -> bool { self.contains(Readiness::read_closed()) @@ -186,9 +186,9 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::write_closed(); + /// let readiness = Readiness::write_closed(); /// - /// assert!(ready.is_write_closed()); + /// assert!(readiness.is_write_closed()); /// ``` pub fn is_write_closed(&self) -> bool { self.contains(Readiness::write_closed()) @@ -245,11 +245,11 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::readable(); - /// let ready_usize = ready.as_usize(); - /// let ready2 = Readiness::from_usize(ready_usize); + /// let readiness = Readiness::readable(); + /// let readiness_usize = readiness.as_usize(); + /// let readiness2 = Readiness::from_usize(readiness_usize); /// - /// assert_eq!(ready, ready2); + /// assert_eq!(readiness, readiness2); /// ``` pub fn from_usize(val: usize) -> Readiness { Readiness(val) @@ -265,11 +265,11 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let ready = Readiness::readable(); - /// let ready_usize = ready.as_usize(); - /// let ready2 = Readiness::from_usize(ready_usize); + /// let readiness = Readiness::readable(); + /// let readiness_usize = readiness.as_usize(); + /// let readiness2 = Readiness::from_usize(readiness_usize); /// - /// assert_eq!(ready, ready2); + /// assert_eq!(readiness, readiness2); /// ``` pub fn as_usize(&self) -> usize { self.0 diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs index 6a3878340c1..315e022d993 100644 --- a/tokio/src/net/util/io_resource.rs +++ b/tokio/src/net/util/io_resource.rs @@ -58,25 +58,25 @@ use std::task::{Context, Poll}; /// use tokio::net::util::IoResource; /// /// use futures_core::ready; -/// use mio::Ready; +/// use tokio::net::driver::Readiness; /// use mio::net::{TcpStream, TcpListener}; /// use std::io; /// use std::task::{Context, Poll}; /// /// struct MyListener { -/// poll_evented: PollEvented, +/// poll_evented: IoResource, /// } /// /// impl MyListener { /// pub fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { -/// let ready = Ready::readable(); +/// let readiness = Readiness::readable(); /// -/// ready!(self.poll_evented.poll_read_ready(cx, ready))?; +/// ready!(self.poll_evented.poll_read_ready(cx))?; /// /// match self.poll_evented.get_ref().accept() { /// Ok((socket, _)) => Poll::Ready(Ok(socket)), /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { -/// self.poll_evented.clear_read_ready(cx, ready)?; +/// self.poll_evented.clear_read_ready(cx)?; /// Poll::Pending /// } /// Err(e) => Poll::Ready(Err(e)), @@ -86,11 +86,7 @@ use std::task::{Context, Poll}; /// ``` /// /// TODO -/// ## Platform-specific events -/// -/// `PollEvented` also allows receiving platform-specific `mio::Ready` events. -/// These events are included as part of the read readiness event stream. The -/// write readiness event stream is only for `Ready::writable()` events. +/// ## Platform-specific events? /// /// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html /// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html From c9177e758dbd048242fbe47f2e35fcc1fe9f42e3 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 11:14:51 -0800 Subject: [PATCH 13/21] Change windows process to use IoResource Signed-off-by: Kevin Leimkuhler --- tokio/src/process/windows.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tokio/src/process/windows.rs b/tokio/src/process/windows.rs index d25807d6310..1d1a866445c 100644 --- a/tokio/src/process/windows.rs +++ b/tokio/src/process/windows.rs @@ -15,7 +15,7 @@ //! `RegisterWaitForSingleObject` and then wait on the other end of the oneshot //! from then on out. -use crate::net::util::PollEvented; +use crate::net::util::IoResource; use crate::process::kill::Kill; use crate::process::SpawnedChild; use crate::sync::oneshot; @@ -174,11 +174,11 @@ pub(crate) fn try_wait(child: &StdChild) -> io::Result> { } } -pub(crate) type ChildStdin = PollEvented; -pub(crate) type ChildStdout = PollEvented; -pub(crate) type ChildStderr = PollEvented; +pub(crate) type ChildStdin = IoResource; +pub(crate) type ChildStdout = IoResource; +pub(crate) type ChildStderr = IoResource; -fn stdio(option: Option) -> Option> +fn stdio(option: Option) -> Option> where T: IntoRawHandle, { @@ -187,5 +187,5 @@ where None => return None, }; let pipe = unsafe { NamedPipe::from_raw_handle(io.into_raw_handle()) }; - PollEvented::new(pipe).ok() + IoResource::new(pipe).ok() } From d0391e345d3a5c0ed0131ef2a49e167106c19efc Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 11:36:22 -0800 Subject: [PATCH 14/21] Clippy and use mio master Signed-off-by: Kevin Leimkuhler --- tokio/Cargo.toml | 2 +- tokio/src/net/driver/readiness.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index 44c5d60e87e..22d2d40236b 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -98,7 +98,7 @@ fnv = { version = "1.0.6", optional = true } futures-core = { version = "0.3.0", optional = true } lazy_static = { version = "1.0.2", optional = true } memchr = { version = "2.2", optional = true } -mio = { git = "https://github.com/kleimkuhler/mio", branch = "kleimkuhler/unixstream-pair-fix", optional = true } +mio = { git = "https://github.com/tokio-rs/mio", branch = "master", optional = true } num_cpus = { version = "1.8.0", optional = true } # Backs `DelayQueue` slab = { version = "0.4.1", optional = true } diff --git a/tokio/src/net/driver/readiness.rs b/tokio/src/net/driver/readiness.rs index 581f57d6979..3a2efa4cb88 100644 --- a/tokio/src/net/driver/readiness.rs +++ b/tokio/src/net/driver/readiness.rs @@ -130,8 +130,8 @@ impl Readiness { /// let readiness = Readiness::empty(); /// assert!(readiness.is_empty()); /// ``` - pub fn is_empty(&self) -> bool { - *self == Readiness::empty() + pub fn is_empty(self) -> bool { + self == Readiness::empty() } /// Returns true if the value includes readable readiness @@ -145,7 +145,7 @@ impl Readiness { /// /// assert!(readiness.is_readable()); /// ``` - pub fn is_readable(&self) -> bool { + pub fn is_readable(self) -> bool { self.contains(Readiness::readable()) } @@ -160,7 +160,7 @@ impl Readiness { /// /// assert!(readiness.is_writable()); /// ``` - pub fn is_writable(&self) -> bool { + pub fn is_writable(self) -> bool { self.contains(Readiness::writable()) } @@ -175,7 +175,7 @@ impl Readiness { /// /// assert!(readiness.is_read_closed()); /// ``` - pub fn is_read_closed(&self) -> bool { + pub fn is_read_closed(self) -> bool { self.contains(Readiness::read_closed()) } @@ -190,7 +190,7 @@ impl Readiness { /// /// assert!(readiness.is_write_closed()); /// ``` - pub fn is_write_closed(&self) -> bool { + pub fn is_write_closed(self) -> bool { self.contains(Readiness::write_closed()) } @@ -227,9 +227,9 @@ impl Readiness { /// assert!(!Readiness::readable().contains(readiness)); /// assert!(readiness.contains(readiness)); /// ``` - pub fn contains>(&self, other: T) -> bool { + pub fn contains>(self, other: T) -> bool { let other = other.into(); - (*self & other) == other + (self & other) == other } /// Create a `Readiness` instance using the given `usize` representation. @@ -271,7 +271,7 @@ impl Readiness { /// /// assert_eq!(readiness, readiness2); /// ``` - pub fn as_usize(&self) -> usize { + pub fn as_usize(self) -> usize { self.0 } } From e0000e9d9313774a15efa87a6f0dcbd499fb2b76 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 15:23:48 -0800 Subject: [PATCH 15/21] send_framed is flakey on master as well Signed-off-by: Kevin Leimkuhler --- tokio-util/tests/udp.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tokio-util/tests/udp.rs b/tokio-util/tests/udp.rs index 9b677740cea..be05dbc4f6e 100644 --- a/tokio-util/tests/udp.rs +++ b/tokio-util/tests/udp.rs @@ -10,7 +10,6 @@ use futures::stream::StreamExt; use std::io; #[tokio::test] -#[ignore] async fn send_framed() -> std::io::Result<()> { let mut a_soc = UdpSocket::bind("127.0.0.1:0").await?; let mut b_soc = UdpSocket::bind("127.0.0.1:0").await?; From 43b885ea7beb1c7fdbbc7bd9f35de30cb2aade5b Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 16:07:37 -0800 Subject: [PATCH 16/21] Remove PollEvented references Signed-off-by: Kevin Leimkuhler --- tokio/src/net/driver/mod.rs | 10 +++++----- tokio/src/net/driver/reactor/mod.rs | 2 +- tokio/src/net/tcp/listener.rs | 4 ++-- tokio/src/net/tcp/stream.rs | 10 +++++----- tokio/src/net/udp/socket.rs | 10 +++++----- tokio/src/net/unix/datagram.rs | 10 +++++----- tokio/src/net/unix/listener.rs | 2 +- tokio/src/net/unix/stream.rs | 8 ++++---- tokio/src/net/util/io_resource.rs | 10 +++++----- 9 files changed, 33 insertions(+), 33 deletions(-) diff --git a/tokio/src/net/driver/mod.rs b/tokio/src/net/driver/mod.rs index b666ea3a41d..8acddd696ca 100644 --- a/tokio/src/net/driver/mod.rs +++ b/tokio/src/net/driver/mod.rs @@ -96,8 +96,8 @@ //! //! There are a couple of ways to do this. //! -//! If the custom I/O resource implements [`mio::Evented`] and implements -//! [`std::io::Read`] and / or [`std::io::Write`], then [`PollEvented`] is the +//! If the custom I/O resource implements [`mio::event::Source`] and implements +//! [`std::io::Read`] and / or [`std::io::Write`], then [`IoResource`] is the //! most suited. //! //! Otherwise, [`Registration`] can be used directly. This provides the lowest @@ -119,9 +119,9 @@ //! [`Handle::current`]: struct.Handle.html#method.current //! [`mio`]: https://github.com/carllerche/mio //! [`Reactor::poll`]: struct.Reactor.html#method.poll -//! [`Poll::poll`]: https://docs.rs/mio/0.6/mio/struct.Poll.html#method.poll -//! [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html -//! [`PollEvented`]: struct.PollEvented.html +//! [`Poll::poll`]: TODO +//! [`mio::event::Source`]: TODO +//! [`IoResource`]: struct.IoResource.html //! [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html //! [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html #[cfg(all(loom, test))] diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index 755f18f1e09..f43b72280f2 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -432,7 +432,7 @@ mod tests { use super::*; use loom::thread; - // No-op `Evented` impl just so we can have something to pass to `add_source`. + // No-op `Source` impl just so we can have something to pass to `add_source`. struct NotSource; impl mio::event::Source for NotSource { diff --git a/tokio/src/net/tcp/listener.rs b/tokio/src/net/tcp/listener.rs index 3b94bd046a7..2d99540b5a4 100644 --- a/tokio/src/net/tcp/listener.rs +++ b/tokio/src/net/tcp/listener.rs @@ -292,10 +292,10 @@ impl TryFrom for mio::net::TcpListener { /// Consumes value, returning the mio I/O object. /// - /// See [`PollEvented::into_inner`] for more details about + /// See [`IoResource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`PollEvented::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoResource::into_inner`]: crate::util::IoResource::into_inner fn try_from(value: TcpListener) -> Result { value.io.into_inner() } diff --git a/tokio/src/net/tcp/stream.rs b/tokio/src/net/tcp/stream.rs index ec2b4eb0880..ff699d64887 100644 --- a/tokio/src/net/tcp/stream.rs +++ b/tokio/src/net/tcp/stream.rs @@ -353,11 +353,11 @@ impl TcpStream { // == Poll IO functions that takes `&self` == // - // They are not public because (taken from the doc of `PollEvented`): + // They are not public because (taken from the doc of `IoResource`): // - // While `PollEvented` is `Sync` (if the underlying I/O type is `Sync`), the + // While `IoResource` is `Sync` (if the underlying I/O type is `Sync`), the // caller must ensure that there are at most two tasks that use a - // `PollEvented` instance concurrently. One for reading and one for writing. + // `IoResource` instance concurrently. One for reading and one for writing. // While violating this requirement is "safe" from a Rust memory model point // of view, it will result in unexpected behavior in the form of lost // notifications and tasks hanging. @@ -442,10 +442,10 @@ impl TryFrom for mio::net::TcpStream { /// Consumes value, returning the mio I/O object. /// - /// See [`PollEvented::into_inner`] for more details about + /// See [`IoResource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`PollEvented::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoResource::into_inner`]: crate::util::IoResource::into_inner fn try_from(value: TcpStream) -> Result { value.io.into_inner() } diff --git a/tokio/src/net/udp/socket.rs b/tokio/src/net/udp/socket.rs index eda5743345b..bdef4bb4516 100644 --- a/tokio/src/net/udp/socket.rs +++ b/tokio/src/net/udp/socket.rs @@ -111,11 +111,11 @@ impl UdpSocket { // Poll IO functions that takes `&self` are provided for the split API. // - // They are not public because (taken from the doc of `PollEvented`): + // They are not public because (taken from the doc of `IoResource`): // - // While `PollEvented` is `Sync` (if the underlying I/O type is `Sync`), the + // While `IoResource` is `Sync` (if the underlying I/O type is `Sync`), the // caller must ensure that there are at most two tasks that use a - // `PollEvented` instance concurrently. One for reading and one for writing. + // `IoResource` instance concurrently. One for reading and one for writing. // While violating this requirement is "safe" from a Rust memory model point // of view, it will result in unexpected behavior in the form of lost // notifications and tasks hanging. @@ -364,10 +364,10 @@ impl TryFrom for mio::net::UdpSocket { /// Consumes value, returning the mio I/O object. /// - /// See [`PollEvented::into_inner`] for more details about + /// See [`IoResource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`PollEvented::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoResource::into_inner`]: crate::util::IoResource::into_inner fn try_from(value: UdpSocket) -> Result { value.io.into_inner() } diff --git a/tokio/src/net/unix/datagram.rs b/tokio/src/net/unix/datagram.rs index 65a20825d40..2eb39e97551 100644 --- a/tokio/src/net/unix/datagram.rs +++ b/tokio/src/net/unix/datagram.rs @@ -76,11 +76,11 @@ impl UnixDatagram { // Poll IO functions that takes `&self` are provided for the split API. // - // They are not public because (taken from the doc of `PollEvented`): + // They are not public because (taken from the doc of `IoResource`): // - // While `PollEvented` is `Sync` (if the underlying I/O type is `Sync`), the + // While `IoResource` is `Sync` (if the underlying I/O type is `Sync`), the // caller must ensure that there are at most two tasks that use a - // `PollEvented` instance concurrently. One for reading and one for writing. + // `IoResource` instance concurrently. One for reading and one for writing. // While violating this requirement is "safe" from a Rust memory model point // of view, it will result in unexpected behavior in the form of lost // notifications and tasks hanging. @@ -202,10 +202,10 @@ impl TryFrom for mio::net::UnixDatagram { /// Consumes value, returning the mio I/O object. /// - /// See [`PollEvented::into_inner`] for more details about + /// See [`IoResource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`PollEvented::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoResource::into_inner`]: crate::util::IoResource::into_inner fn try_from(value: UnixDatagram) -> Result { value.io.into_inner() } diff --git a/tokio/src/net/unix/listener.rs b/tokio/src/net/unix/listener.rs index 5df0dfa0f70..8a4443888d9 100644 --- a/tokio/src/net/unix/listener.rs +++ b/tokio/src/net/unix/listener.rs @@ -90,7 +90,7 @@ impl TryFrom for mio::net::UnixListener { /// See [`IoResource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`IoResource::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoResource::into_inner`]: crate::util::IoResource::into_inner fn try_from(value: UnixListener) -> Result { value.io.into_inner() } diff --git a/tokio/src/net/unix/stream.rs b/tokio/src/net/unix/stream.rs index 5c551268b1a..09726b742c5 100644 --- a/tokio/src/net/unix/stream.rs +++ b/tokio/src/net/unix/stream.rs @@ -118,7 +118,7 @@ impl TryFrom for mio::net::UnixStream { /// See [`IoResource::into_inner`] for more details about /// resource deregistration that happens during the call. /// - /// [`IoResource::into_inner`]: crate::util::PollEvented::into_inner + /// [`IoResource::into_inner`]: crate::util::IoResource::into_inner fn try_from(value: UnixStream) -> Result { value.io.into_inner() } @@ -187,11 +187,11 @@ impl AsyncWrite for UnixStream { impl UnixStream { // == Poll IO functions that takes `&self` == // - // They are not public because (taken from the doc of `PollEvented`): + // They are not public because (taken from the doc of `IoResource`): // - // While `PollEvented` is `Sync` (if the underlying I/O type is `Sync`), the + // While `IoResource` is `Sync` (if the underlying I/O type is `Sync`), the // caller must ensure that there are at most two tasks that use a - // `PollEvented` instance concurrently. One for reading and one for writing. + // `IoResource` instance concurrently. One for reading and one for writing. // While violating this requirement is "safe" from a Rust memory model point // of view, it will result in unexpected behavior in the form of lost // notifications and tasks hanging. diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs index 315e022d993..dd788765539 100644 --- a/tokio/src/net/util/io_resource.rs +++ b/tokio/src/net/util/io_resource.rs @@ -64,19 +64,19 @@ use std::task::{Context, Poll}; /// use std::task::{Context, Poll}; /// /// struct MyListener { -/// poll_evented: IoResource, +/// io_resource: IoResource, /// } /// /// impl MyListener { /// pub fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { /// let readiness = Readiness::readable(); /// -/// ready!(self.poll_evented.poll_read_ready(cx))?; +/// ready!(self.io_resource.poll_read_ready(cx))?; /// -/// match self.poll_evented.get_ref().accept() { +/// match self.io_resource.get_ref().accept() { /// Ok((socket, _)) => Poll::Ready(Ok(socket)), /// Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { -/// self.poll_evented.clear_read_ready(cx)?; +/// self.io_resource.clear_read_ready(cx)?; /// Poll::Pending /// } /// Err(e) => Poll::Ready(Err(e)), @@ -92,7 +92,7 @@ use std::task::{Context, Poll}; /// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html /// [`AsyncRead`]: ../io/trait.AsyncRead.html /// [`AsyncWrite`]: ../io/trait.AsyncWrite.html -/// [`mio::Evented`]: https://docs.rs/mio/0.6/mio/trait.Evented.html +/// [`mio::event::Source`]: TODO /// [`Registration`]: struct.Registration.html /// [`TcpListener`]: ../net/struct.TcpListener.html /// [`clear_read_ready`]: #method.clear_read_ready From b0de5ec8f06e02aa78977c51ae62916dd2fd10f3 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 16:55:40 -0800 Subject: [PATCH 17/21] Make Readiness functions associated consts Signed-off-by: Kevin Leimkuhler --- .../reactor/dispatch/page/scheduled_io.rs | 2 +- tokio/src/net/driver/reactor/mod.rs | 18 ++--- tokio/src/net/driver/readiness.rs | 78 ++++++++----------- tokio/src/net/driver/registration.rs | 3 +- tokio/src/net/util/io_resource.rs | 8 +- 5 files changed, 48 insertions(+), 61 deletions(-) diff --git a/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs b/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs index 3693e590628..c3f2e9cfacc 100644 --- a/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs +++ b/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs @@ -149,7 +149,7 @@ impl ScheduledIo { } // Mask out the generation bits so that the modifying function // doesn't see them. - let mask = Readiness::all(); + let mask = Readiness::ALL; let current_readiness = current & mask.as_usize(); let new = f(current_readiness); debug_assert!( diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index f43b72280f2..339af8f9ab2 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -232,18 +232,18 @@ impl Reactor { None => return, }; - let mut readiness = Readiness::empty(); + let mut readiness = Readiness::EMPTY; if event.is_readable() { - readiness |= Readiness::readable() + readiness |= Readiness::READABLE } if event.is_writable() { - readiness |= Readiness::writable() + readiness |= Readiness::WRITABLE } if event.is_read_closed() { - readiness |= Readiness::read_closed() + readiness |= Readiness::READ_CLOSED } if event.is_write_closed() { - readiness |= Readiness::write_closed() + readiness |= Readiness::WRITE_CLOSED } if io @@ -395,8 +395,8 @@ impl Inner { .unwrap_or_else(|| panic!("token {} no longer valid!", token)); let (waker, interest) = match dir { - Direction::Read => (&sched.reader, Readiness::readable()), - Direction::Write => (&sched.writer, Readiness::writable()), + Direction::Read => (&sched.reader, Readiness::READABLE), + Direction::Write => (&sched.writer, Readiness::WRITABLE), }; waker.register(w); @@ -421,8 +421,8 @@ impl Drop for Inner { impl Direction { pub(super) fn mask(self) -> Readiness { match self { - Direction::Read => Readiness::readable() | Readiness::read_closed(), - Direction::Write => Readiness::writable() | Readiness::write_closed(), + Direction::Read => Readiness::READABLE | Readiness::READ_CLOSED, + Direction::Write => Readiness::WRITABLE | Readiness::WRITE_CLOSED, } } } diff --git a/tokio/src/net/driver/readiness.rs b/tokio/src/net/driver/readiness.rs index 3a2efa4cb88..bcb4f065841 100644 --- a/tokio/src/net/driver/readiness.rs +++ b/tokio/src/net/driver/readiness.rs @@ -19,7 +19,7 @@ const WRITE_CLOSED: usize = 0b0_1000; /// ```rust /// use tokio::net::driver::Readiness; /// -/// let readiness = Readiness::readable() | Readiness::read_closed(); +/// let readiness = Readiness::READABLE | Readiness::READ_CLOSED; /// assert!(readiness.is_readable()); /// assert!(readiness.is_read_closed()); /// ``` @@ -34,13 +34,11 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::empty(); + /// let readiness = Readiness::EMPTY; /// /// assert!(!readiness.is_readable()); /// ``` - pub fn empty() -> Readiness { - Readiness(0) - } + pub const EMPTY: Readiness = Readiness(0); /// Returns a `Readiness` representing readable readiness. /// @@ -49,13 +47,11 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::readable(); + /// let readiness = Readiness::READABLE; /// /// assert!(readiness.is_readable()); /// ``` - pub fn readable() -> Readiness { - Readiness(READABLE) - } + pub const READABLE: Readiness = Readiness(READABLE); /// Returns a `Readiness` representing writable readiness. /// @@ -64,13 +60,11 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::writable(); + /// let readiness = Readiness::WRITABLE; /// /// assert!(readiness.is_writable()); /// ``` - pub fn writable() -> Readiness { - Readiness(WRITABLE) - } + pub const WRITABLE: Readiness = Readiness(WRITABLE); /// Returns a `Readiness` representing read closed readiness. /// @@ -79,13 +73,11 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::read_closed(); + /// let readiness = Readiness::READ_CLOSED; /// /// assert!(readiness.is_read_closed()); /// ``` - pub fn read_closed() -> Readiness { - Readiness(READ_CLOSED) - } + pub const READ_CLOSED: Readiness = Readiness(READ_CLOSED); /// Returns a `Readiness` representing write closed readiness. /// @@ -94,13 +86,11 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::write_closed(); + /// let readiness = Readiness::WRITE_CLOSED; /// /// assert!(readiness.is_write_closed()); /// ``` - pub fn write_closed() -> Readiness { - Readiness(WRITE_CLOSED) - } + pub const WRITE_CLOSED: Readiness = Readiness(WRITE_CLOSED); /// Returns a `Readiness` representing readiness for all operations. /// @@ -109,16 +99,14 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::all(); + /// let readiness = Readiness::ALL; /// /// assert!(readiness.is_readable()); /// assert!(readiness.is_writable()); /// assert!(readiness.is_read_closed()); /// assert!(readiness.is_write_closed()); /// ``` - pub fn all() -> Readiness { - Readiness(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED) - } + pub const ALL: Readiness = Readiness(READABLE | WRITABLE | READ_CLOSED | WRITE_CLOSED); /// Returns true if `Readiness` is the empty set /// @@ -127,11 +115,11 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::empty(); + /// let readiness = Readiness::EMPTY; /// assert!(readiness.is_empty()); /// ``` pub fn is_empty(self) -> bool { - self == Readiness::empty() + self == Readiness::EMPTY } /// Returns true if the value includes readable readiness @@ -141,12 +129,12 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::readable(); + /// let readiness = Readiness::READABLE; /// /// assert!(readiness.is_readable()); /// ``` pub fn is_readable(self) -> bool { - self.contains(Readiness::readable()) + self.contains(Readiness::READABLE) } /// Returns true if the value includes writable readiness @@ -156,12 +144,12 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::writable(); + /// let readiness = Readiness::WRITABLE; /// /// assert!(readiness.is_writable()); /// ``` pub fn is_writable(self) -> bool { - self.contains(Readiness::writable()) + self.contains(Readiness::WRITABLE) } /// Returns true if the value includes read closed readiness @@ -171,12 +159,12 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::read_closed(); + /// let readiness = Readiness::READ_CLOSED; /// /// assert!(readiness.is_read_closed()); /// ``` pub fn is_read_closed(self) -> bool { - self.contains(Readiness::read_closed()) + self.contains(Readiness::READ_CLOSED) } /// Returns true if the value includes write closed readiness @@ -186,12 +174,12 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::write_closed(); + /// let readiness = Readiness::WRITE_CLOSED; /// /// assert!(readiness.is_write_closed()); /// ``` pub fn is_write_closed(self) -> bool { - self.contains(Readiness::write_closed()) + self.contains(Readiness::WRITE_CLOSED) } /// Returns true if `self` is a superset of `other`. @@ -204,27 +192,27 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::readable(); + /// let readiness = Readiness::READABLE; /// - /// assert!(readiness.contains(Readiness::readable())); - /// assert!(!readiness.contains(Readiness::writable())); + /// assert!(readiness.contains(Readiness::READABLE)); + /// assert!(!readiness.contains(Readiness::WRITABLE)); /// ``` /// /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::readable() | Readiness::writable(); + /// let readiness = Readiness::READABLE | Readiness::WRITABLE; /// - /// assert!(readiness.contains(Readiness::readable())); - /// assert!(readiness.contains(Readiness::writable())); + /// assert!(readiness.contains(Readiness::READABLE)); + /// assert!(readiness.contains(Readiness::WRITABLE)); /// ``` /// /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::readable() | Readiness::writable(); + /// let readiness = Readiness::READABLE | Readiness::WRITABLE; /// - /// assert!(!Readiness::readable().contains(readiness)); + /// assert!(!Readiness::READABLE.contains(readiness)); /// assert!(readiness.contains(readiness)); /// ``` pub fn contains>(self, other: T) -> bool { @@ -245,7 +233,7 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::readable(); + /// let readiness = Readiness::READABLE; /// let readiness_usize = readiness.as_usize(); /// let readiness2 = Readiness::from_usize(readiness_usize); /// @@ -265,7 +253,7 @@ impl Readiness { /// ``` /// use tokio::net::driver::Readiness; /// - /// let readiness = Readiness::readable(); + /// let readiness = Readiness::READABLE; /// let readiness_usize = readiness.as_usize(); /// let readiness2 = Readiness::from_usize(readiness_usize); /// diff --git a/tokio/src/net/driver/registration.rs b/tokio/src/net/driver/registration.rs index 09b5b905517..6081c8a96ea 100644 --- a/tokio/src/net/driver/registration.rs +++ b/tokio/src/net/driver/registration.rs @@ -211,8 +211,7 @@ impl Registration { } let mask = direction.mask(); - let mask_no_closed = - (mask - (Readiness::read_closed() - Readiness::write_closed())).as_usize(); + let mask_no_closed = (mask - (Readiness::READ_CLOSED - Readiness::WRITE_CLOSED)).as_usize(); let sched = inner.io_dispatch.get(self.token).unwrap(); diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs index dd788765539..e7733a30d0b 100644 --- a/tokio/src/net/util/io_resource.rs +++ b/tokio/src/net/util/io_resource.rs @@ -214,7 +214,7 @@ where /// /// * called from outside of a task context. pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - let mask = Readiness::readable() | Readiness::read_closed(); + let mask = Readiness::READABLE | Readiness::READ_CLOSED; poll_readiness!( self, read_readiness, @@ -239,7 +239,7 @@ where clear_readiness!( self, read_readiness, - Readiness::readable(), + Readiness::READABLE, self.registration.poll_read_ready(cx), cx.waker() ) @@ -263,7 +263,7 @@ where /// /// * called from outside of a task context. pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - let mask = Readiness::writable() | Readiness::write_closed(); + let mask = Readiness::WRITABLE | Readiness::WRITE_CLOSED; poll_readiness!( self, write_readiness, @@ -287,7 +287,7 @@ where clear_readiness!( self, write_readiness, - Readiness::writable(), + Readiness::WRITABLE, self.registration.poll_write_ready(cx), cx.waker() ) From 79586ecc86f06b6f1c31c76ba79e55cf6cedbe0b Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 17:18:24 -0800 Subject: [PATCH 18/21] Fix doc example Signed-off-by: Kevin Leimkuhler --- tokio/src/net/util/io_resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs index e7733a30d0b..d8998115dbd 100644 --- a/tokio/src/net/util/io_resource.rs +++ b/tokio/src/net/util/io_resource.rs @@ -69,7 +69,7 @@ use std::task::{Context, Poll}; /// /// impl MyListener { /// pub fn poll_accept(&mut self, cx: &mut Context<'_>) -> Poll> { -/// let readiness = Readiness::readable(); +/// let readiness = Readiness::READABLE; /// /// ready!(self.io_resource.poll_read_ready(cx))?; /// From b3b4b046b460e9d4f3df9127a4cd8c3b5189da7b Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 17:32:47 -0800 Subject: [PATCH 19/21] Misc edits Signed-off-by: Kevin Leimkuhler --- tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs | 3 +-- tokio/src/net/driver/reactor/mod.rs | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs b/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs index c3f2e9cfacc..15d25e67476 100644 --- a/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs +++ b/tokio/src/net/driver/reactor/dispatch/page/scheduled_io.rs @@ -149,8 +149,7 @@ impl ScheduledIo { } // Mask out the generation bits so that the modifying function // doesn't see them. - let mask = Readiness::ALL; - let current_readiness = current & mask.as_usize(); + let current_readiness = current & Readiness::ALL.as_usize(); let new = f(current_readiness); debug_assert!( new < Generation::ONE, diff --git a/tokio/src/net/driver/reactor/mod.rs b/tokio/src/net/driver/reactor/mod.rs index 339af8f9ab2..2afea923202 100644 --- a/tokio/src/net/driver/reactor/mod.rs +++ b/tokio/src/net/driver/reactor/mod.rs @@ -247,7 +247,6 @@ impl Reactor { } if io - // .set_readiness(token.0, |curr| curr | readiness.into()) .set_readiness(token.0, |curr| curr | readiness.as_usize()) .is_err() { From 16ab8733d9d43f9f1d99fef404dfe56e641a3807 Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Tue, 12 Nov 2019 20:01:27 -0800 Subject: [PATCH 20/21] Remove unnecessary readiness Signed-off-by: Kevin Leimkuhler --- tokio/src/net/util/io_resource.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs index d8998115dbd..6cc7ebe8783 100644 --- a/tokio/src/net/util/io_resource.rs +++ b/tokio/src/net/util/io_resource.rs @@ -214,7 +214,7 @@ where /// /// * called from outside of a task context. pub fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll> { - let mask = Readiness::READABLE | Readiness::READ_CLOSED; + let mask = Readiness::READABLE; poll_readiness!( self, read_readiness, @@ -263,7 +263,7 @@ where /// /// * called from outside of a task context. pub fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll> { - let mask = Readiness::WRITABLE | Readiness::WRITE_CLOSED; + let mask = Readiness::WRITABLE; poll_readiness!( self, write_readiness, From 78c17dfc825e7a3a10c66649d1a56c1ba173eb8c Mon Sep 17 00:00:00 2001 From: Kevin Leimkuhler Date: Mon, 18 Nov 2019 14:15:59 -0800 Subject: [PATCH 21/21] Remove ready import after rebase Signed-off-by: Kevin Leimkuhler --- tokio/src/net/util/io_resource.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tokio/src/net/util/io_resource.rs b/tokio/src/net/util/io_resource.rs index 6cc7ebe8783..96bc86a804c 100644 --- a/tokio/src/net/util/io_resource.rs +++ b/tokio/src/net/util/io_resource.rs @@ -1,7 +1,6 @@ use crate::io::{AsyncRead, AsyncWrite}; use crate::net::driver::{Readiness, Registration}; -use futures_core::ready; use mio; use std::fmt; use std::io::{self, Read, Write};