diff --git a/examples/echo.rs b/examples/echo.rs index 1116d6830..4c8417c8f 100644 --- a/examples/echo.rs +++ b/examples/echo.rs @@ -9,8 +9,9 @@ use std::net::SocketAddr; use futures::Future; use futures::stream::Stream; -use tokio_core::Loop; use tokio_core::io::{copy, TaskIo}; +use tokio_core::net::TcpListener; +use tokio_core::reactor::Core; fn main() { env_logger::init().unwrap(); @@ -18,11 +19,11 @@ fn main() { let addr = addr.parse::().unwrap(); // Create the event loop that will drive this server - let mut l = Loop::new().unwrap(); + let mut l = Core::new().unwrap(); let pin = l.pin(); // Create a TCP listener which will listen for incoming connections - let server = l.handle().tcp_listen(&addr); + let server = TcpListener::bind(&addr, &l.handle()); let done = server.and_then(move |socket| { // Once we've got the TCP listener, inform that we have it diff --git a/examples/sink.rs b/examples/sink.rs index 21ba71d28..c825274c3 100644 --- a/examples/sink.rs +++ b/examples/sink.rs @@ -14,14 +14,16 @@ use std::net::SocketAddr; use futures::Future; use futures::stream::{self, Stream}; use tokio_core::io::IoFuture; +use tokio_core::net::{TcpListener, TcpStream}; +use tokio_core::reactor::Core; fn main() { env_logger::init().unwrap(); let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string()); let addr = addr.parse::().unwrap(); - let mut l = tokio_core::Loop::new().unwrap(); - let server = l.handle().tcp_listen(&addr).and_then(|socket| { + let mut l = Core::new().unwrap(); + let server = TcpListener::bind(&addr, &l.handle()).and_then(|socket| { socket.incoming().and_then(|(socket, addr)| { println!("got a socket: {}", addr); write(socket).or_else(|_| Ok(())) @@ -34,7 +36,7 @@ fn main() { l.run(server).unwrap(); } -fn write(socket: tokio_core::TcpStream) -> IoFuture<()> { +fn write(socket: TcpStream) -> IoFuture<()> { static BUF: &'static [u8] = &[0; 64 * 1024]; let iter = iter::repeat(()).map(|()| Ok(())); stream::iter(iter).fold(socket, |socket, ()| { diff --git a/src/channel.rs b/src/channel.rs index c0c03b5eb..151abf956 100644 --- a/src/channel.rs +++ b/src/channel.rs @@ -1,3 +1,8 @@ +//! In-memory evented channels. +//! +//! This module contains a `Sender` and `Receiver` pair types which can be used +//! to send messages between different future tasks. + use std::io; use std::sync::mpsc::TryRecvError; @@ -5,15 +10,15 @@ use futures::{Future, Poll, Async}; use futures::stream::Stream; use mio::channel; -use {ReadinessStream, LoopHandle}; use io::IoFuture; +use reactor::{Handle, PollEvented}; /// The transmission half of a channel used for sending messages to a receiver. /// /// A `Sender` can be `clone`d to have multiple threads or instances sending /// messages to one receiver. /// -/// This type is created by the `LoopHandle::channel` method. +/// This type is created by the `channel` function. pub struct Sender { tx: channel::Sender, } @@ -24,32 +29,36 @@ pub struct Sender { /// A `Receiver` cannot be cloned, so only one thread can receive messages at a /// time. /// -/// This type is created by the `LoopHandle::channel` method and implements the -/// `Stream` trait to represent received messages. +/// This type is created by the `channel` function and implements the `Stream` +/// trait to represent received messages. pub struct Receiver { - rx: ReadinessStream>, + rx: PollEvented>, } -impl LoopHandle { - /// Creates a new in-memory channel used for sending data across `Send + - /// 'static` boundaries, frequently threads. - /// - /// This type can be used to conveniently send messages between futures. - /// Unlike the futures crate `channel` method and types, the returned tx/rx - /// pair is a multi-producer single-consumer (mpsc) channel *with no - /// backpressure*. Currently it's left up to the application to implement a - /// mechanism, if necessary, to avoid messages piling up. - /// - /// The returned `Sender` can be used to send messages that are processed by - /// the returned `Receiver`. The `Sender` can be cloned to send messages - /// from multiple sources simultaneously. - pub fn channel(self) -> (Sender, IoFuture>) - where T: Send + 'static, - { - let (tx, rx) = channel::channel(); - let rx = ReadinessStream::new(self, rx).map(|rx| Receiver { rx: rx }); - (Sender { tx: tx }, rx.boxed()) - } +/// Future returned by the `channel` function which will resolve to a +/// `Receiver`. +pub struct ReceiverNew { + inner: IoFuture>, +} + +/// Creates a new in-memory channel used for sending data across `Send + +/// 'static` boundaries, frequently threads. +/// +/// This type can be used to conveniently send messages between futures. +/// Unlike the futures crate `channel` method and types, the returned tx/rx +/// pair is a multi-producer single-consumer (mpsc) channel *with no +/// backpressure*. Currently it's left up to the application to implement a +/// mechanism, if necessary, to avoid messages piling up. +/// +/// The returned `Sender` can be used to send messages that are processed by +/// the returned `Receiver`. The `Sender` can be cloned to send messages +/// from multiple sources simultaneously. +pub fn channel(handle: &Handle) -> (Sender, ReceiverNew) + where T: Send + 'static, +{ + let (tx, rx) = channel::channel(); + let rx = PollEvented::new(rx, handle).map(|rx| Receiver { rx: rx }); + (Sender { tx: tx }, ReceiverNew { inner: rx.boxed() }) } impl Sender { @@ -87,7 +96,9 @@ impl Stream for Receiver { type Error = io::Error; fn poll(&mut self) -> Poll, io::Error> { - try_ready!(self.rx.poll_read()); + if let Async::NotReady = self.rx.poll_read() { + return Ok(Async::NotReady) + } match self.rx.get_ref().try_recv() { Ok(t) => Ok(Async::Ready(Some(t))), Err(TryRecvError::Empty) => { @@ -98,3 +109,12 @@ impl Stream for Receiver { } } } + +impl Future for ReceiverNew { + type Item = Receiver; + type Error = io::Error; + + fn poll(&mut self) -> Poll, io::Error> { + self.inner.poll() + } +} diff --git a/src/io/mod.rs b/src/io/mod.rs index 6eb97686a..a9f3a8f2e 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -3,9 +3,9 @@ //! Contains various combinators to work with I/O objects and type definitions //! as well. -use std::io; +use std::io::{self, Read, Write}; -use futures::BoxFuture; +use futures::{BoxFuture, Async}; use futures::stream::BoxStream; /// A convenience typedef around a `Future` whose error component is `io::Error` @@ -45,3 +45,74 @@ pub use self::read_to_end::{read_to_end, ReadToEnd}; pub use self::task::{TaskIo, TaskIoRead, TaskIoWrite}; pub use self::window::Window; pub use self::write_all::{write_all, WriteAll}; + +/// A trait for read/write I/O objects +/// +/// This trait represents I/O object which are readable and writable. +/// Additionally, they're associated with the ability to test whether they're +/// readable or writable. +/// +/// Imporantly, the methods of this trait are intended to be used in conjuction +/// with the current task of a future. Namely whenever any of them return a +/// value that indicates "would block" the current future's task is arranged to +/// receive a notification when the method would otherwise not indicate that it +/// would block. +pub trait Io: Read + Write { + /// Tests to see if this I/O object may be readable. + /// + /// This method returns an `Async<()>` indicating whether the object + /// **might** be readable. It is possible that even if this method returns + /// `Async::Ready` that a call to `read` would return a `WouldBlock` error. + /// + /// There is a default implementation for this function which always + /// indicates that an I/O object is readable, but objects which can + /// implement a finer grained version of this are recommended to do so. + /// + /// If this function returns `Async::NotReady` then the current future's + /// task is arranged to receive a notification when it might not return + /// `NotReady`. + /// + /// # Panics + /// + /// This method is likely to panic if called from outside the context of a + /// future's task. + fn poll_read(&mut self) -> Async<()> { + Async::Ready(()) + } + + /// Tests to see if this I/O object may be writable. + /// + /// This method returns an `Async<()>` indicating whether the object + /// **might** be writable. It is possible that even if this method returns + /// `Async::Ready` that a call to `write` would return a `WouldBlock` error. + /// + /// There is a default implementation for this function which always + /// indicates that an I/O object is writable, but objects which can + /// implement a finer grained version of this are recommended to do so. + /// + /// If this function returns `Async::NotReady` then the current future's + /// task is arranged to receive a notification when it might not return + /// `NotReady`. + /// + /// # Panics + /// + /// This method is likely to panic if called from outside the context of a + /// future's task. + fn poll_write(&mut self) -> Async<()> { + Async::Ready(()) + } + + /// Helper method for splitting this read/write object into two halves. + /// + /// The two halves returned implement the `Read` and `Write` traits, + /// respectively, but are only usable on the current task. + /// + /// # Panics + /// + /// This method will panic if there is not currently an active future task. + fn task_split(self) -> (TaskIoRead, TaskIoWrite) + where Self: Sized + { + TaskIo::new(self).split() + } +} diff --git a/src/lib.rs b/src/lib.rs index 0b0864f65..c31e15043 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,98 @@ -//! Mio bindings with streams and futures +//! `Future`-powered I/O at the core of Tokio //! -//! This crate uses the `futures_io` and `futures` crates to provide a thin -//! binding on top of mio of TCP and UDP sockets. +//! This crate uses the `futures` crate to provide an event loop ("reactor +//! core") which can be used to drive I/O like TCP and UDP, spawned future +//! tasks, and other events like channels/timeouts. All asynchronous I/O is +//! powered by the `mio` crate. +//! +//! The concrete types provided in this crate are relatively bare bones but are +//! intended to be the essential foundation for further projects needing an +//! event loop. In this crate you'll find: +//! +//! * TCP, both streams and listeners +//! * UDP sockets +//! * Message queues +//! * Timeouts +//! +//! More functionality is likely to be added over time, but otherwise the crate +//! is intended to be flexible with the `PollEvented` type which accepts any +//! type which implements `mio::Evented`. Using this if you'd like Unix domain +//! sockets, for example, the `tokio-uds` is built externally to offer this +//! functionality. +//! +//! Some other important tasks covered by this crate are: +//! +//! * The ability to spawn futures into an even loop. The `Handle` and `Pinned` +//! types have a `spawn` method which allows executing a future on an event +//! loop. The `Pinned::spawn` method crucially does not require the future +//! itself to be `Send`. +//! +//! * The `Io` trait serves as an abstraction for future crates to build on top +//! of. This packages up `Read` and `Write` functionality as well as the +//! ability to poll for readiness on both ends. +//! +//! * All I/O is futures-aware. If any action in this crate returns "not ready" +//! or "would block", then the current future task is scheduled to receive a +//! notification when it would otherwise make progress. +//! +//! # Examples +//! +//! A simple TCP echo server: +//! +//! ```no_run +//! extern crate futures; +//! extern crate tokio_core; +//! +//! use std::env; +//! use std::net::SocketAddr; +//! +//! use futures::Future; +//! use futures::stream::Stream; +//! use tokio_core::io::{copy, Io}; +//! use tokio_core::net::TcpListener; +//! use tokio_core::reactor::Core; +//! +//! fn main() { +//! let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string()); +//! let addr = addr.parse::().unwrap(); +//! +//! // Create the event loop that will drive this server +//! let mut l = Core::new().unwrap(); +//! let pin = l.pin(); +//! +//! // Create a TCP listener which will listen for incoming connections +//! let server = TcpListener::bind(&addr, pin.handle()); +//! +//! let done = server.and_then(|socket| { +//! // Once we've got the TCP listener, inform that we have it +//! println!("Listening on: {}", addr); +//! +//! // Pull out the stream of incoming connections and then for each new +//! // one spin up a new task copying data. +//! // +//! // We use the `io::copy` future to copy all data from the +//! // reading half onto the writing half. +//! socket.incoming().for_each(|(socket, addr)| { +//! let pair = futures::lazy(|| Ok(socket.task_split())); +//! let amt = pair.and_then(|(reader, writer)| copy(reader, writer)); +//! +//! // Once all that is done we print out how much we wrote, and then +//! // critically we *spawn* this future which allows it to run +//! // concurrently with other connections. +//! pin.spawn(amt.then(move |result| { +//! println!("wrote {:?} bytes to {}", result, addr); +//! Ok(()) +//! })); +//! +//! Ok(()) +//! }) +//! }); +//! +//! // Execute our server (modeled as a future) and wait for it to +//! // complete. +//! l.run(done).unwrap(); +//! } +//! ``` #![deny(missing_docs)] @@ -22,19 +113,8 @@ mod lock; #[macro_use] pub mod io; -mod channel; -mod event_loop; mod mpsc_queue; -mod readiness_stream; -mod tcp; -mod timeout; mod timer_wheel; -mod udp; - -pub use channel::{Sender, Receiver}; -pub use event_loop::{Loop, LoopPin, LoopHandle, AddSource, AddTimeout}; -pub use event_loop::{TimeoutToken, IoToken}; -pub use readiness_stream::ReadinessStream; -pub use tcp::{TcpListener, TcpStream}; -pub use timeout::Timeout; -pub use udp::UdpSocket; +pub mod channel; +pub mod net; +pub mod reactor; diff --git a/src/net/mod.rs b/src/net/mod.rs new file mode 100644 index 000000000..296ff873e --- /dev/null +++ b/src/net/mod.rs @@ -0,0 +1,11 @@ +//! TCP/UDP bindings for `tokio-core` +//! +//! This module contains the TCP/UDP networking types, similar to the standard +//! library, which can be used to implement networking protocols. + +mod tcp; +mod udp; + +pub use self::tcp::{TcpStream, TcpStreamNew}; +pub use self::tcp::{TcpListener, TcpListenerNew, Incoming}; +pub use self::udp::{UdpSocket, UdpSocketNew}; diff --git a/src/tcp.rs b/src/net/tcp.rs similarity index 77% rename from src/tcp.rs rename to src/net/tcp.rs index be94931b3..463a042db 100644 --- a/src/tcp.rs +++ b/src/net/tcp.rs @@ -7,30 +7,45 @@ use futures::stream::Stream; use futures::{Future, IntoFuture, failed, Poll, Async}; use mio; -use {ReadinessStream, LoopHandle}; -use io::{IoFuture, IoStream}; +use io::{Io, IoFuture, IoStream}; +use reactor::{Handle, PollEvented}; /// An I/O object representing a TCP socket listening for incoming connections. /// /// This object can be converted into a stream of incoming connections for /// various forms of processing. pub struct TcpListener { - io: ReadinessStream, + io: PollEvented, +} + +/// Future which will resolve to a `TcpListener` +pub struct TcpListenerNew { + inner: IoFuture, +} + +/// Stream returned by the `TcpListener::incoming` function representing the +/// stream of sockets received from a listener. +pub struct Incoming { + inner: IoStream<(TcpStream, SocketAddr)>, } impl TcpListener { - fn new(listener: mio::tcp::TcpListener, - handle: LoopHandle) -> IoFuture { - ReadinessStream::new(handle, listener).map(|io| { - TcpListener { - io: io, - } - }).boxed() + /// Create a new TCP listener associated with this event loop. + /// + /// The TCP listener will bind to the provided `addr` address, if available, + /// and will be returned as a future. The returned future, if resolved + /// successfully, can then be used to accept incoming connections. + pub fn bind(addr: &SocketAddr, handle: &Handle) -> TcpListenerNew { + let future = match mio::tcp::TcpListener::bind(addr) { + Ok(l) => TcpListener::new(l, handle), + Err(e) => failed(e).boxed(), + }; + TcpListenerNew { inner: future } } /// Create a new TCP listener from the standard library's TCP listener. /// - /// This method can be used when the `LoopHandle::tcp_listen` method isn't + /// This method can be used when the `Handle::tcp_listen` method isn't /// sufficient because perhaps some more configuration is needed in terms of /// before the calls to `bind` and `listen`. /// @@ -57,15 +72,23 @@ impl TcpListener { /// well (same for IPv6). pub fn from_listener(listener: net::TcpListener, addr: &SocketAddr, - handle: LoopHandle) -> IoFuture { + handle: &Handle) -> IoFuture { + let handle = handle.clone(); mio::tcp::TcpListener::from_listener(listener, addr) .into_future() - .and_then(|l| TcpListener::new(l, handle)) + .and_then(move |l| TcpListener::new(l, &handle)) .boxed() } + fn new(listener: mio::tcp::TcpListener, handle: &Handle) + -> IoFuture { + PollEvented::new(listener, handle).map(|io| { + TcpListener { io: io } + }).boxed() + } + /// Test whether this socket is ready to be read or not. - pub fn poll_read(&self) -> Poll<(), io::Error> { + pub fn poll_read(&self) -> Async<()> { self.io.poll_read() } @@ -82,17 +105,19 @@ impl TcpListener { /// /// This method returns an implementation of the `Stream` trait which /// resolves to the sockets the are accepted on this listener. - pub fn incoming(self) -> IoStream<(TcpStream, SocketAddr)> { - struct Incoming { + pub fn incoming(self) -> Incoming { + struct MyIncoming { inner: TcpListener, } - impl Stream for Incoming { + impl Stream for MyIncoming { type Item = (mio::tcp::TcpStream, SocketAddr); type Error = io::Error; fn poll(&mut self) -> Poll, io::Error> { - try_ready!(self.inner.io.poll_read()); + if let Async::NotReady = self.inner.io.poll_read() { + return Ok(Async::NotReady) + } match self.inner.io.get_ref().accept() { Ok(pair) => Ok(Async::Ready(Some(pair))), Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { @@ -104,13 +129,15 @@ impl TcpListener { } } - let loop_handle = self.io.loop_handle().clone(); - Incoming { inner: self } - .and_then(move |(tcp, addr)| { - ReadinessStream::new(loop_handle.clone(), tcp).map(move |io| { + let handle = self.io.handle().clone(); + let stream = MyIncoming { inner: self }; + Incoming { + inner: stream.and_then(move |(tcp, addr)| { + PollEvented::new(tcp, &handle).map(move |io| { (TcpStream { io: io }, addr) }) - }).boxed() + }).boxed(), + } } /// Sets the value for the `IP_TTL` option on this socket. @@ -158,6 +185,24 @@ impl fmt::Debug for TcpListener { } } +impl Future for TcpListenerNew { + type Item = TcpListener; + type Error = io::Error; + + fn poll(&mut self) -> Poll { + self.inner.poll() + } +} + +impl Stream for Incoming { + type Item = (TcpStream, SocketAddr); + type Error = io::Error; + + fn poll(&mut self) -> Poll, io::Error> { + self.inner.poll() + } +} + /// An I/O object representing a TCP stream connected to a remote endpoint. /// /// A TCP stream can either be created by connecting to an endpoint or by @@ -165,27 +210,21 @@ impl fmt::Debug for TcpListener { /// raw underlying I/O object as well as streams for the read/write /// notifications on the stream itself. pub struct TcpStream { - io: ReadinessStream, + io: PollEvented, } -enum TcpStreamNew { +/// Future returned by `TcpStream::connect` which will resolve to a `TcpStream` +/// when the stream is connected. +pub struct TcpStreamNew { + inner: IoFuture, +} + +enum TcpStreamConnect { Waiting(TcpStream), Empty, } -impl LoopHandle { - /// Create a new TCP listener associated with this event loop. - /// - /// The TCP listener will bind to the provided `addr` address, if available, - /// and will be returned as a future. The returned future, if resolved - /// successfully, can then be used to accept incoming connections. - pub fn tcp_listen(self, addr: &SocketAddr) -> IoFuture { - match mio::tcp::TcpListener::bind(addr) { - Ok(l) => TcpListener::new(l, self), - Err(e) => failed(e).boxed(), - } - } - +impl TcpStream { /// Create a new TCP stream connected to the specified address. /// /// This function will create a new TCP socket and attempt to connect it to @@ -193,20 +232,18 @@ impl LoopHandle { /// stream has successfully connected. If an error happens during the /// connection or during the socket creation, that error will be returned to /// the future instead. - pub fn tcp_connect(self, addr: &SocketAddr) -> IoFuture { - match mio::tcp::TcpStream::connect(addr) { - Ok(tcp) => TcpStream::new(tcp, self), + pub fn connect(addr: &SocketAddr, handle: &Handle) -> TcpStreamNew { + let future = match mio::tcp::TcpStream::connect(addr) { + Ok(tcp) => TcpStream::new(tcp, handle), Err(e) => failed(e).boxed(), - } + }; + TcpStreamNew { inner: future } } -} -impl TcpStream { - fn new(connected_stream: mio::tcp::TcpStream, - handle: LoopHandle) + fn new(connected_stream: mio::tcp::TcpStream, handle: &Handle) -> IoFuture { - ReadinessStream::new(handle, connected_stream).and_then(|io| { - TcpStreamNew::Waiting(TcpStream { io: io }) + PollEvented::new(connected_stream, handle).and_then(|io| { + TcpStreamConnect::Waiting(TcpStream { io: io }) }).boxed() } @@ -230,7 +267,7 @@ impl TcpStream { /// (perhaps to `INADDR_ANY`) before this method is called. pub fn connect_stream(stream: net::TcpStream, addr: &SocketAddr, - handle: LoopHandle) -> IoFuture { + handle: &Handle) -> IoFuture { match mio::tcp::TcpStream::connect_stream(stream, addr) { Ok(tcp) => TcpStream::new(tcp, handle), Err(e) => failed(e).boxed(), @@ -243,7 +280,7 @@ impl TcpStream { /// get a notification when the socket does become readable. That is, this /// is only suitable for calling in a `Future::poll` method and will /// automatically handle ensuring a retry once the socket is readable again. - pub fn poll_read(&self) -> Poll<(), io::Error> { + pub fn poll_read(&self) -> Async<()> { self.io.poll_read() } @@ -253,7 +290,7 @@ impl TcpStream { /// get a notification when the socket does become writable. That is, this /// is only suitable for calling in a `Future::poll` method and will /// automatically handle ensuring a retry once the socket is writable again. - pub fn poll_write(&self) -> Poll<(), io::Error> { + pub fn poll_write(&self) -> Async<()> { self.io.poll_write() } @@ -340,15 +377,81 @@ impl TcpStream { } } +impl Read for TcpStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.io.read(buf) + } +} + +impl Write for TcpStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.io.write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.io.flush() + } +} + +impl Io for TcpStream { + fn poll_read(&mut self) -> Async<()> { + ::poll_read(self) + } + + fn poll_write(&mut self) -> Async<()> { + ::poll_write(self) + } +} + +impl<'a> Read for &'a TcpStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (&self.io).read(buf) + } +} + +impl<'a> Write for &'a TcpStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + (&self.io).write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + (&self.io).flush() + } +} + +impl<'a> Io for &'a TcpStream { + fn poll_read(&mut self) -> Async<()> { + ::poll_read(self) + } + + fn poll_write(&mut self) -> Async<()> { + ::poll_write(self) + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.io.get_ref().fmt(f) + } +} + impl Future for TcpStreamNew { type Item = TcpStream; type Error = io::Error; + fn poll(&mut self) -> Poll { + self.inner.poll() + } +} + +impl Future for TcpStreamConnect { + type Item = TcpStream; + type Error = io::Error; + fn poll(&mut self) -> Poll { { let stream = match *self { - TcpStreamNew::Waiting(ref s) => s, - TcpStreamNew::Empty => panic!("can't poll TCP stream twice"), + TcpStreamConnect::Waiting(ref s) => s, + TcpStreamConnect::Empty => panic!("can't poll TCP stream twice"), }; // Once we've connected, wait for the stream to be writable as @@ -357,83 +460,20 @@ impl Future for TcpStreamNew { // actually hit an error or not. // // If all that succeeded then we ship everything on up. - try_ready!(stream.io.poll_write()); + if let Async::NotReady = stream.io.poll_write() { + return Ok(Async::NotReady) + } if let Some(e) = try!(stream.io.get_ref().take_error()) { return Err(e) } } - match mem::replace(self, TcpStreamNew::Empty) { - TcpStreamNew::Waiting(stream) => Ok(Async::Ready(stream)), - TcpStreamNew::Empty => panic!(), + match mem::replace(self, TcpStreamConnect::Empty) { + TcpStreamConnect::Waiting(stream) => Ok(Async::Ready(stream)), + TcpStreamConnect::Empty => panic!(), } } } -impl Read for TcpStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - <&TcpStream>::read(&mut &*self, buf) - } -} - -impl Write for TcpStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - <&TcpStream>::write(&mut &*self, buf) - } - fn flush(&mut self) -> io::Result<()> { - <&TcpStream>::flush(&mut &*self) - } -} - -impl<'a> Read for &'a TcpStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - if let Async::NotReady = try!(self.io.poll_read()) { - return Err(mio::would_block()) - } - let r = self.io.get_ref().read(buf); - if is_wouldblock(&r) { - self.io.need_read(); - } - r - } -} - -impl<'a> Write for &'a TcpStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - if let Async::NotReady = try!(self.io.poll_write()) { - return Err(mio::would_block()) - } - let r = self.io.get_ref().write(buf); - if is_wouldblock(&r) { - self.io.need_write(); - } - r - } - - fn flush(&mut self) -> io::Result<()> { - if let Async::NotReady = try!(self.io.poll_write()) { - return Err(mio::would_block()) - } - let r = self.io.get_ref().flush(); - if is_wouldblock(&r) { - self.io.need_write(); - } - r - } -} - -fn is_wouldblock(r: &io::Result) -> bool { - match *r { - Ok(_) => false, - Err(ref e) => e.kind() == io::ErrorKind::WouldBlock, - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.io.get_ref().fmt(f) - } -} - #[cfg(unix)] mod sys { use std::os::unix::prelude::*; diff --git a/src/udp.rs b/src/net/udp.rs similarity index 89% rename from src/udp.rs rename to src/net/udp.rs index 73afcd669..e11ed8733 100644 --- a/src/udp.rs +++ b/src/net/udp.rs @@ -5,33 +5,36 @@ use std::fmt; use futures::{Future, failed, Poll, Async}; use mio; -use {ReadinessStream, LoopHandle}; use io::IoFuture; +use reactor::{Handle, PollEvented}; /// An I/O object representing a UDP socket. pub struct UdpSocket { - io: ReadinessStream, + io: PollEvented, } -impl LoopHandle { +/// Future returned from `UdpSocket::bind` which will resolve to a `UdpSocket`. +pub struct UdpSocketNew { + inner: IoFuture, +} + +impl UdpSocket { /// Create a new UDP socket bound to the specified address. /// /// This function will create a new UDP socket and attempt to bind it to the /// `addr` provided. The returned future will be resolved once the socket /// has successfully bound. If an error happens during the binding or during /// the socket creation, that error will be returned to the future instead. - pub fn udp_bind(self, addr: &SocketAddr) -> IoFuture { - match mio::udp::UdpSocket::bind(addr) { - Ok(udp) => UdpSocket::new(udp, self), + pub fn bind(addr: &SocketAddr, handle: &Handle) -> UdpSocketNew { + let future = match mio::udp::UdpSocket::bind(addr) { + Ok(udp) => UdpSocket::new(udp, handle), Err(e) => failed(e).boxed(), - } + }; + UdpSocketNew { inner: future } } -} -impl UdpSocket { - fn new(socket: mio::udp::UdpSocket, handle: LoopHandle) - -> IoFuture { - ReadinessStream::new(handle, socket).map(|io| { + fn new(socket: mio::udp::UdpSocket, handle: &Handle) -> IoFuture { + PollEvented::new(socket, handle).map(|io| { UdpSocket { io: io } }).boxed() } @@ -46,9 +49,9 @@ 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_socket(socket: net::UdpSocket, - handle: LoopHandle) -> IoFuture { + handle: &Handle) -> IoFuture { match mio::udp::UdpSocket::from_socket(socket) { - Ok(tcp) => UdpSocket::new(tcp, handle), + Ok(udp) => UdpSocket::new(udp, handle), Err(e) => failed(e).boxed(), } } @@ -64,7 +67,7 @@ impl UdpSocket { /// get a notification when the socket does become readable. That is, this /// is only suitable for calling in a `Future::poll` method and will /// automatically handle ensuring a retry once the socket is readable again. - pub fn poll_read(&self) -> Poll<(), io::Error> { + pub fn poll_read(&self) -> Async<()> { self.io.poll_read() } @@ -74,7 +77,7 @@ impl UdpSocket { /// get a notification when the socket does become writable. That is, this /// is only suitable for calling in a `Future::poll` method and will /// automatically handle ensuring a retry once the socket is writable again. - pub fn poll_write(&self) -> Poll<(), io::Error> { + pub fn poll_write(&self) -> Async<()> { self.io.poll_write() } @@ -84,14 +87,14 @@ impl UdpSocket { /// Address type can be any implementor of `ToSocketAddrs` trait. See its /// documentation for concrete examples. pub fn send_to(&self, buf: &[u8], target: &SocketAddr) -> io::Result { - if let Async::NotReady = try!(self.io.poll_write()) { + if let Async::NotReady = self.io.poll_write() { return Err(mio::would_block()) } match self.io.get_ref().send_to(buf, target) { Ok(Some(n)) => Ok(n), Ok(None) => { self.io.need_write(); - Err(io::Error::new(io::ErrorKind::WouldBlock, "would block")) + Err(mio::would_block()) } Err(e) => Err(e), } @@ -100,14 +103,14 @@ impl UdpSocket { /// Receives data from the socket. On success, returns the number of bytes /// read and the address from whence the data came. pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - if let Async::NotReady = try!(self.io.poll_read()) { + if let Async::NotReady = self.io.poll_read() { return Err(mio::would_block()) } match self.io.get_ref().recv_from(buf) { Ok(Some(n)) => Ok(n), Ok(None) => { self.io.need_read(); - Err(io::Error::new(io::ErrorKind::WouldBlock, "would block")) + Err(mio::would_block()) } Err(e) => Err(e), } @@ -260,6 +263,15 @@ impl fmt::Debug for UdpSocket { } } +impl Future for UdpSocketNew { + type Item = UdpSocket; + type Error = io::Error; + + fn poll(&mut self) -> Poll { + self.inner.poll() + } +} + #[cfg(unix)] mod sys { use std::os::unix::prelude::*; diff --git a/src/event_loop/channel.rs b/src/reactor/channel.rs similarity index 100% rename from src/event_loop/channel.rs rename to src/reactor/channel.rs diff --git a/src/event_loop/source.rs b/src/reactor/io_token.rs similarity index 87% rename from src/event_loop/source.rs rename to src/reactor/io_token.rs index 12c841a93..dbd6c70d1 100644 --- a/src/event_loop/source.rs +++ b/src/reactor/io_token.rs @@ -6,14 +6,14 @@ use futures::{Future, Poll}; use futures::task; use mio; -use event_loop::{Message, LoopHandle, LoopFuture, Direction, Loop}; +use reactor::{Message, Handle, CoreFuture, Direction, Core}; /// A future which will resolve a unique `tok` token for an I/O object. /// -/// Created through the `LoopHandle::add_source` method, this future can also +/// Created through the `Handle::add_source` method, this future can also /// resolve to an error if there's an issue communicating with the event loop. -pub struct AddSource { - inner: LoopFuture<(E, (Arc, usize)), E>, +pub struct IoTokenNew { + inner: CoreFuture<(E, (Arc, usize)), E>, } /// A token that identifies an active timeout. @@ -23,7 +23,7 @@ pub struct IoToken { readiness: Arc, } -impl LoopHandle { +impl IoToken { /// Add a new source to an event loop, returning a future which will resolve /// to the token that can be used to identify this source. /// @@ -40,18 +40,37 @@ impl LoopHandle { /// The returned future will panic if the event loop this handle is /// associated with has gone away, or if there is an error communicating /// with the event loop. - pub fn add_source(&self, source: E) -> AddSource + pub fn new(source: E, handle: &Handle) -> IoTokenNew where E: mio::Evented + Send + 'static, { - AddSource { - inner: LoopFuture { - loop_handle: self.clone(), + IoTokenNew { + inner: CoreFuture { + handle: handle.clone(), data: Some(source), result: None, - } + }, } } + /// Consumes the last readiness notification the token this source is for + /// registered. + /// + /// Currently sources receive readiness notifications on an edge-basis. That + /// is, once you receive a notification that an object can be read, you + /// won't receive any more notifications until all of that data has been + /// read. + /// + /// The event loop will fill in this information and then inform futures + /// that they're ready to go with the `schedule` method, and then the `poll` + /// method can use this to figure out what happened. + /// + /// > **Note**: This method should generally not be used directly, but + /// > rather the `ReadinessStream` type should be used instead. + // TODO: this should really return a proper newtype/enum, not a usize + pub fn take_readiness(&self) -> usize { + self.readiness.swap(0, Ordering::SeqCst) + } + /// Schedule the current future task to receive a notification when the /// corresponding I/O object is readable. /// @@ -74,8 +93,8 @@ impl LoopHandle { /// /// This function will also panic if there is not a currently running future /// task. - pub fn schedule_read(&self, tok: &IoToken) { - self.send(Message::Schedule(tok.token, task::park(), Direction::Read)); + pub fn schedule_read(&self, handle: &Handle) { + handle.send(Message::Schedule(self.token, task::park(), Direction::Read)); } /// Schedule the current future task to receive a notification when the @@ -101,8 +120,8 @@ impl LoopHandle { /// /// This function will also panic if there is not a currently running future /// task. - pub fn schedule_write(&self, tok: &IoToken) { - self.send(Message::Schedule(tok.token, task::park(), Direction::Write)); + pub fn schedule_write(&self, handle: &Handle) { + handle.send(Message::Schedule(self.token, task::park(), Direction::Write)); } /// Unregister all information associated with a token on an event loop, @@ -127,33 +146,12 @@ impl LoopHandle { /// This function will panic if the event loop this handle is associated /// with has gone away, or if there is an error communicating with the event /// loop. - pub fn drop_source(&self, tok: &IoToken) { - self.send(Message::DropSource(tok.token)); + pub fn drop_source(&self, handle: &Handle) { + handle.send(Message::DropSource(self.token)); } } -impl IoToken { - /// Consumes the last readiness notification the token this source is for - /// registered. - /// - /// Currently sources receive readiness notifications on an edge-basis. That - /// is, once you receive a notification that an object can be read, you - /// won't receive any more notifications until all of that data has been - /// read. - /// - /// The event loop will fill in this information and then inform futures - /// that they're ready to go with the `schedule` method, and then the `poll` - /// method can use this to figure out what happened. - /// - /// > **Note**: This method should generally not be used directly, but - /// > rather the `ReadinessStream` type should be used instead. - // TODO: this should really return a proper newtype/enum, not a usize - pub fn take_readiness(&self) -> usize { - self.readiness.swap(0, Ordering::SeqCst) - } -} - -impl Future for AddSource +impl Future for IoTokenNew where E: mio::Evented + Send + 'static, { type Item = (E, IoToken); @@ -164,7 +162,7 @@ impl Future for AddSource let pair = try!(lp.add_source(&io)); Ok((io, pair)) }, |io, slot| { - Message::Run(Box::new(move |lp: &Loop| { + Message::Run(Box::new(move |lp: &Core| { let res = lp.add_source(&io).map(|p| (io, p)); slot.try_produce(res).ok() .expect("add source try_produce intereference"); diff --git a/src/event_loop/mod.rs b/src/reactor/mod.rs similarity index 91% rename from src/event_loop/mod.rs rename to src/reactor/mod.rs index 88c58cadf..6ebdad22f 100644 --- a/src/event_loop/mod.rs +++ b/src/reactor/mod.rs @@ -1,7 +1,13 @@ +//! The core reactor driving all I/O +//! +//! This module contains the `Core` type which is the reactor for all I/O +//! happening in `tokio-core`. This reactor (or event loop) is used to run +//! futures, schedule tasks, issue I/O requests, etc. + use std::cell::RefCell; use std::io::{self, ErrorKind}; use std::mem; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use std::time::{Instant, Duration}; @@ -12,17 +18,20 @@ use mio; use slab::Slab; use slot::{self, Slot}; -use timer_wheel::{TimerWheel, Timeout}; +use timer_wheel::{TimerWheel, Timeout as WheelTimeout}; mod channel; -mod source; -mod timeout; -pub use self::source::{AddSource, IoToken}; -pub use self::timeout::{AddTimeout, TimeoutToken}; +mod io_token; +mod timeout_token; use self::channel::{Sender, Receiver, channel}; +mod poll_evented; +mod timeout; +pub use self::poll_evented::{PollEvented, PollEventedNew}; +pub use self::timeout::{Timeout, TimeoutNew}; + static NEXT_LOOP_ID: AtomicUsize = ATOMIC_USIZE_INIT; -scoped_thread_local!(static CURRENT_LOOP: Loop); +scoped_thread_local!(static CURRENT_LOOP: Core); const SLAB_CAPACITY: usize = 1024 * 64; @@ -33,11 +42,11 @@ const SLAB_CAPACITY: usize = 1024 * 64; /// multiple handles pointing to it, each of which can then be used to create /// various I/O objects to interact with the event loop in interesting ways. // TODO: expand this -pub struct Loop { +pub struct Core { id: usize, io: mio::Poll, events: mio::Events, - tx: Arc>, + tx: Sender, rx: Receiver, io_dispatch: RefCell>, task_dispatch: RefCell>, @@ -59,7 +68,7 @@ pub struct Loop { // state of the timeout itself. The `TimeoutToken` type is an index into the // `timeouts` slab. timer_wheel: RefCell>, - timeouts: RefCell>, + timeouts: RefCell>, } /// Handle to an event loop, used to construct I/O objects, send messages, and @@ -68,17 +77,17 @@ pub struct Loop { /// Handles can be cloned, and when cloned they will still refer to the /// same underlying event loop. #[derive(Clone)] -pub struct LoopHandle { +pub struct Handle { id: usize, - tx: Arc>, + tx: Sender, } /// A non-sendable handle to an event loop, useful for manufacturing instances /// of `LoopData`. #[derive(Clone)] -pub struct LoopPin { - handle: LoopHandle, - futures: Rc, +pub struct Pinned { + handle: Handle, + futures: Weak, } struct ScheduledIo { @@ -123,10 +132,10 @@ const TOKEN_FUTURE: mio::Token = mio::Token(1); const TOKEN_NEW_FUTURES: mio::Token = mio::Token(2); const TOKEN_START: usize = 3; -impl Loop { +impl Core { /// Creates a new event loop, returning any error that happened during the /// creation. - pub fn new() -> io::Result { + pub fn new() -> io::Result { let (tx, rx) = channel(); let io = try!(mio::Poll::new()); try!(io.register(&rx, @@ -141,11 +150,11 @@ impl Loop { TOKEN_NEW_FUTURES, mio::Ready::readable(), mio::PollOpt::level()); - Ok(Loop { + Ok(Core { id: NEXT_LOOP_ID.fetch_add(1, Ordering::Relaxed), io: io, events: mio::Events::with_capacity(1024), - tx: Arc::new(tx), + tx: tx, rx: rx, io_dispatch: RefCell::new(Slab::with_capacity(SLAB_CAPACITY)), task_dispatch: RefCell::new(Slab::with_capacity(SLAB_CAPACITY)), @@ -166,8 +175,8 @@ impl Loop { /// /// Handles to an event loop are cloneable as well and clones will always /// refer to the same event loop. - pub fn handle(&self) -> LoopHandle { - LoopHandle { + pub fn handle(&self) -> Handle { + Handle { id: self.id, tx: self.tx.clone(), } @@ -177,12 +186,12 @@ impl Loop { /// but can be used as a proxy to the event loop itself. /// /// Currently the primary use for this is to use as a handle to add data - /// to the event loop directly. The `LoopPin::add_loop_data` method can + /// to the event loop directly. The `Pinned::add_loop_data` method can /// be used to immediately create instances of `LoopData` structures. - pub fn pin(&self) -> LoopPin { - LoopPin { + pub fn pin(&self) -> Pinned { + Pinned { handle: self.handle(), - futures: self.new_futures.clone(), + futures: Rc::downgrade(&self.new_futures), } } @@ -501,7 +510,7 @@ impl Loop { } } -impl LoopHandle { +impl Handle { fn send(&self, msg: Message) { self.with_loop(|lp| { match lp { @@ -528,7 +537,7 @@ impl LoopHandle { } fn with_loop(&self, f: F) -> R - where F: FnOnce(Option<&Loop>) -> R + where F: FnOnce(Option<&Core>) -> R { if CURRENT_LOOP.is_set() { CURRENT_LOOP.with(|lp| { @@ -552,20 +561,20 @@ impl LoopHandle { /// Note that while the closure, `F`, requires the `Send` bound as it might /// cross threads, the future `R` does not. pub fn spawn(&self, f: F) - where F: FnOnce(&LoopPin) -> R + Send + 'static, + where F: FnOnce(&Pinned) -> R + Send + 'static, R: IntoFuture, R::Future: 'static, { - self.send(Message::Run(Box::new(|lp: &Loop| { + self.send(Message::Run(Box::new(|lp: &Core| { let f = f(&lp.pin()); lp.spawn(Box::new(f.into_future())); }))); } } -impl LoopPin { +impl Pinned { /// Returns a reference to the underlying handle to the event loop. - pub fn handle(&self) -> &LoopHandle { + pub fn handle(&self) -> &Handle { &self.handle } @@ -573,22 +582,26 @@ impl LoopPin { pub fn spawn(&self, f: F) where F: Future + 'static, { - self.futures.queue.borrow_mut().push(Box::new(f)); - self.futures.ready.set_readiness(mio::Ready::readable()).unwrap(); + let inner = match self.futures.upgrade() { + Some(inner) => inner, + None => return, + }; + inner.queue.borrow_mut().push(Box::new(f)); + inner.ready.set_readiness(mio::Ready::readable()).unwrap(); } } -struct LoopFuture { - loop_handle: LoopHandle, +struct CoreFuture { + handle: Handle, data: Option, result: Option<(Arc>>, slot::Token)>, } -impl LoopFuture +impl CoreFuture where T: 'static, { fn poll(&mut self, f: F, g: G) -> Poll - where F: FnOnce(&Loop, U) -> io::Result, + where F: FnOnce(&Core, U) -> io::Result, G: FnOnce(U, Arc>>) -> Message, { match self.result { @@ -607,7 +620,7 @@ impl LoopFuture } None => { let data = &mut self.data; - let ret = self.loop_handle.with_loop(|lp| { + let ret = self.handle.with_loop(|lp| { lp.map(|lp| f(lp, data.take().unwrap())) }); if let Some(ret) = ret { @@ -622,7 +635,7 @@ impl LoopFuture task.unpark(); }); self.result = Some((result.clone(), token)); - self.loop_handle.send(g(data.take().unwrap(), result)); + self.handle.send(g(data.take().unwrap(), result)); Ok(Async::NotReady) } } @@ -658,11 +671,11 @@ impl Unpark for MySetReadiness { } trait FnBox: Send + 'static { - fn call_box(self: Box, lp: &Loop); + fn call_box(self: Box, lp: &Core); } -impl FnBox for F { - fn call_box(self: Box, lp: &Loop) { +impl FnBox for F { + fn call_box(self: Box, lp: &Core) { (*self)(lp) } } diff --git a/src/readiness_stream.rs b/src/reactor/poll_evented.rs similarity index 52% rename from src/readiness_stream.rs rename to src/reactor/poll_evented.rs index 15c7720fe..90bc0881b 100644 --- a/src/readiness_stream.rs +++ b/src/reactor/poll_evented.rs @@ -1,15 +1,25 @@ -use std::io; +//! Readiness tracking streams, backing I/O objects. +//! +//! This module contains the core type which is used to back all I/O on object +//! in `tokio-core`. The `PollEvented` type is the implementation detail of +//! all I/O. Each `PollEvented` manages registration with a reactor, +//! acquisition of a token, and tracking of the readiness state on the +//! underlying I/O primitive. + +use std::io::{self, Read, Write}; use std::sync::atomic::{AtomicUsize, Ordering}; use futures::{Future, Poll, Async}; use mio; -use event_loop::{IoToken, LoopHandle, AddSource}; +use io::Io; +use reactor::Handle; +use reactor::io_token::{IoToken, IoTokenNew}; /// A concrete implementation of a stream of readiness notifications for I/O /// objects that originates from an event loop. /// -/// Created by the `ReadinessStream::new` method, each `ReadinessStream` is +/// Created by the `PollEvented::new` method, each `PollEvented` is /// associated with a specific event loop and source of events that will be /// registered with an event loop. /// @@ -22,19 +32,21 @@ use event_loop::{IoToken, LoopHandle, AddSource}; /// It's the responsibility of the wrapper to inform the readiness stream when a /// "would block" I/O event is seen. The readiness stream will then take care of /// any scheduling necessary to get notified when the event is ready again. -pub struct ReadinessStream { +pub struct PollEvented { token: IoToken, - handle: LoopHandle, + handle: Handle, readiness: AtomicUsize, io: E, } -pub struct ReadinessStreamNew { - inner: AddSource, - handle: LoopHandle, +/// Future returned from `PollEvented::new` which will resolve to a +/// `PollEvented`. +pub struct PollEventedNew { + inner: IoTokenNew, + handle: Handle, } -impl ReadinessStream +impl PollEvented where E: mio::Evented + Send + 'static, { /// Creates a new readiness stream associated with the provided @@ -42,15 +54,15 @@ impl ReadinessStream /// /// This method returns a future which will resolve to the readiness stream /// when it's ready. - pub fn new(loop_handle: LoopHandle, source: E) -> ReadinessStreamNew { - ReadinessStreamNew { - inner: loop_handle.add_source(source), - handle: loop_handle, + pub fn new(source: E, handle: &Handle) -> PollEventedNew { + PollEventedNew { + inner: IoToken::new(source, handle), + handle: handle.clone(), } } } -impl ReadinessStream { +impl PollEvented { /// Tests to see if this source is ready to be read from or not. /// /// If this stream is not ready for a read then `NotReady` will be returned @@ -58,16 +70,16 @@ impl ReadinessStream { /// the stream is readable again. In other words, this method is only safe /// to call from within the context of a future's task, typically done in a /// `Future::poll` method. - pub fn poll_read(&self) -> Poll<(), io::Error> { + pub fn poll_read(&self) -> Async<()> { if self.readiness.load(Ordering::SeqCst) & 1 != 0 { - return Ok(Async::Ready(())) + return Async::Ready(()) } self.readiness.fetch_or(self.token.take_readiness(), Ordering::SeqCst); if self.readiness.load(Ordering::SeqCst) & 1 != 0 { - Ok(Async::Ready(())) + Async::Ready(()) } else { - self.handle.schedule_read(&self.token); - Ok(Async::NotReady) + self.token.schedule_read(&self.handle); + Async::NotReady } } @@ -78,16 +90,16 @@ impl ReadinessStream { /// the stream is writable again. In other words, this method is only safe /// to call from within the context of a future's task, typically done in a /// `Future::poll` method. - pub fn poll_write(&self) -> Poll<(), io::Error> { + pub fn poll_write(&self) -> Async<()> { if self.readiness.load(Ordering::SeqCst) & 2 != 0 { - return Ok(Async::Ready(())) + return Async::Ready(()) } self.readiness.fetch_or(self.token.take_readiness(), Ordering::SeqCst); if self.readiness.load(Ordering::SeqCst) & 2 != 0 { - Ok(Async::Ready(())) + Async::Ready(()) } else { - self.handle.schedule_write(&self.token); - Ok(Async::NotReady) + self.token.schedule_write(&self.handle); + Async::NotReady } } @@ -104,7 +116,7 @@ impl ReadinessStream { /// then again readable. pub fn need_read(&self) { self.readiness.fetch_and(!1, Ordering::SeqCst); - self.handle.schedule_read(&self.token); + self.token.schedule_read(&self.handle) } /// Indicates to this source of events that the corresponding I/O object is @@ -120,12 +132,12 @@ impl ReadinessStream { /// then again writable. pub fn need_write(&self) { self.readiness.fetch_and(!2, Ordering::SeqCst); - self.handle.schedule_write(&self.token); + self.token.schedule_write(&self.handle) } /// Returns a reference to the event loop handle that this readiness stream /// is associated with. - pub fn loop_handle(&self) -> &LoopHandle { + pub fn handle(&self) -> &Handle { &self.handle } @@ -142,15 +154,128 @@ impl ReadinessStream { } } -impl Future for ReadinessStreamNew +impl Read for PollEvented { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + if let Async::NotReady = self.poll_read() { + return Err(mio::would_block()) + } + let r = self.get_mut().read(buf); + if is_wouldblock(&r) { + self.need_read(); + } + return r + } +} + +impl Write for PollEvented { + fn write(&mut self, buf: &[u8]) -> io::Result { + if let Async::NotReady = self.poll_write() { + return Err(mio::would_block()) + } + let r = self.get_mut().write(buf); + if is_wouldblock(&r) { + self.need_write(); + } + return r + } + + fn flush(&mut self) -> io::Result<()> { + if let Async::NotReady = self.poll_write() { + return Err(mio::would_block()) + } + let r = self.get_mut().flush(); + if is_wouldblock(&r) { + self.need_write(); + } + return r + } +} + +impl Io for PollEvented { + fn poll_read(&mut self) -> Async<()> { + >::poll_read(self) + } + + fn poll_write(&mut self) -> Async<()> { + >::poll_write(self) + } +} + +impl<'a, E> Read for &'a PollEvented + where &'a E: Read, +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + if let Async::NotReady = self.poll_read() { + return Err(mio::would_block()) + } + let r = self.get_ref().read(buf); + if is_wouldblock(&r) { + self.need_read(); + } + return r + } +} + +impl<'a, E> Write for &'a PollEvented + where &'a E: Write, +{ + fn write(&mut self, buf: &[u8]) -> io::Result { + if let Async::NotReady = self.poll_write() { + return Err(mio::would_block()) + } + let r = self.get_ref().write(buf); + if is_wouldblock(&r) { + self.need_write(); + } + return r + } + + fn flush(&mut self) -> io::Result<()> { + if let Async::NotReady = self.poll_write() { + return Err(mio::would_block()) + } + let r = self.get_ref().flush(); + if is_wouldblock(&r) { + self.need_write(); + } + return r + } +} + +impl<'a, E> Io for &'a PollEvented + where &'a E: Read + Write, +{ + fn poll_read(&mut self) -> Async<()> { + >::poll_read(self) + } + + fn poll_write(&mut self) -> Async<()> { + >::poll_write(self) + } +} + +fn is_wouldblock(r: &io::Result) -> bool { + match *r { + Ok(_) => false, + Err(ref e) => e.kind() == io::ErrorKind::WouldBlock, + } +} + +impl Drop for PollEvented { + fn drop(&mut self) { + self.token.drop_source(&self.handle); + } +} + +impl Future for PollEventedNew where E: mio::Evented + Send + 'static, { - type Item = ReadinessStream; + type Item = PollEvented; type Error = io::Error; - fn poll(&mut self) -> Poll, io::Error> { + fn poll(&mut self) -> Poll, io::Error> { let (io, token) = try_ready!(self.inner.poll()); - Ok(ReadinessStream { + Ok(PollEvented { token: token, handle: self.handle.clone(), io: io, @@ -158,9 +283,3 @@ impl Future for ReadinessStreamNew }.into()) } } - -impl Drop for ReadinessStream { - fn drop(&mut self) { - self.handle.drop_source(&self.token) - } -} diff --git a/src/timeout.rs b/src/reactor/timeout.rs similarity index 57% rename from src/timeout.rs rename to src/reactor/timeout.rs index da0bf21b1..79d59c1ac 100644 --- a/src/timeout.rs +++ b/src/reactor/timeout.rs @@ -1,11 +1,16 @@ +//! Support for creating futures that represent timeouts. +//! +//! This module contains the `Timeout` type which is a future that will resolve +//! at a particular point in the future. + use std::io; use std::time::{Duration, Instant}; use futures::{Future, Poll, Async}; -use LoopHandle; +use reactor::Handle; +use reactor::timeout_token::TimeoutToken; use io::IoFuture; -use event_loop::TimeoutToken; /// A future representing the notification that a timeout has occurred. /// @@ -16,17 +21,23 @@ use event_loop::TimeoutToken; /// otherwise indicated to fire at. pub struct Timeout { token: TimeoutToken, - handle: LoopHandle, + handle: Handle, } -impl LoopHandle { +/// Future returned from `Timeout::new` and `Timeout::new_at` which will resolve +/// to the actual `Timeout` itself. +pub struct TimeoutNew { + inner: IoFuture, +} + +impl Timeout { /// Creates a new timeout which will fire at `dur` time into the future. /// /// This function will return a future that will resolve to the actual /// timeout object. The timeout object itself is then a future which will be /// set to fire at the specified point in the future. - pub fn timeout(self, dur: Duration) -> IoFuture { - self.timeout_at(Instant::now() + dur) + pub fn new(dur: Duration, handle: &Handle) -> TimeoutNew { + Timeout::new_at(Instant::now() + dur, handle) } /// Creates a new timeout which will fire at the time specified by `at`. @@ -34,13 +45,16 @@ impl LoopHandle { /// This function will return a future that will resolve to the actual /// timeout object. The timeout object itself is then a future which will be /// set to fire at the specified point in the future. - pub fn timeout_at(self, at: Instant) -> IoFuture { - self.add_timeout(at).map(move |token| { - Timeout { - token: token, - handle: self, - } - }).boxed() + pub fn new_at(at: Instant, handle: &Handle) -> TimeoutNew { + let handle = handle.clone(); + TimeoutNew { + inner: TimeoutToken::new(at, &handle).map(move |token| { + Timeout { + token: token, + handle: handle, + } + }).boxed(), + } } } @@ -54,14 +68,23 @@ impl Future for Timeout { if *self.token.when() <= now { Ok(Async::Ready(())) } else { - self.handle.update_timeout(&self.token); + self.token.update_timeout(&self.handle); Ok(Async::NotReady) } } } -impl Drop for Timeout { - fn drop(&mut self) { - self.handle.cancel_timeout(&self.token); +impl Future for TimeoutNew { + type Item = Timeout; + type Error = io::Error; + + fn poll(&mut self) -> Poll { + self.inner.poll() + } +} + +impl Drop for Timeout { + fn drop(&mut self) { + self.token.cancel_timeout(&self.handle); } } diff --git a/src/event_loop/timeout.rs b/src/reactor/timeout_token.rs similarity index 62% rename from src/event_loop/timeout.rs rename to src/reactor/timeout_token.rs index a4230dfd5..f7c02d480 100644 --- a/src/event_loop/timeout.rs +++ b/src/reactor/timeout_token.rs @@ -4,49 +4,12 @@ use std::time::Instant; use futures::{Future, Poll}; use futures::task; -use event_loop::{Message, Loop, LoopHandle, LoopFuture}; +use reactor::{Message, Core, Handle, CoreFuture}; -impl LoopHandle { - /// Adds a new timeout to get fired at the specified instant, notifying the - /// specified task. - pub fn add_timeout(&self, at: Instant) -> AddTimeout { - AddTimeout { - inner: LoopFuture { - loop_handle: self.clone(), - data: Some(at), - result: None, - }, - } - } - - /// Updates a previously added timeout to notify a new task instead. - /// - /// # Panics - /// - /// This method will panic if the timeout specified was not created by this - /// loop handle's `add_timeout` method. - pub fn update_timeout(&self, timeout: &TimeoutToken) { - self.send(Message::UpdateTimeout(timeout.token, task::park())) - } - - /// Cancel a previously added timeout. - /// - /// # Panics - /// - /// This method will panic if the timeout specified was not created by this - /// loop handle's `add_timeout` method. - pub fn cancel_timeout(&self, timeout: &TimeoutToken) { - debug!("cancel timeout {}", timeout.token); - self.send(Message::CancelTimeout(timeout.token)) - } -} - -/// Return value from the [`LoopHandle::add_timeout`] method, a future that will +/// Return value from the `Handle::add_timeout` method, a future that will /// resolve to a `TimeoutToken` to configure the behavior of that timeout. -/// -/// [`LoopHandle::add_timeout`]: struct.LoopHandle.html#method.add_timeout -pub struct AddTimeout { - inner: LoopFuture<(usize, Instant), Instant>, +pub struct TimeoutTokenNew { + inner: CoreFuture<(usize, Instant), Instant>, } /// A token that identifies an active timeout. @@ -55,21 +18,19 @@ pub struct TimeoutToken { when: Instant, } -impl Future for AddTimeout { - type Item = TimeoutToken; - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (t, i) = try_ready!(self.inner.poll(Loop::add_timeout, - Message::AddTimeout)); - Ok(TimeoutToken { - token: t, - when: i, - }.into()) - } -} - impl TimeoutToken { + /// Adds a new timeout to get fired at the specified instant, notifying the + /// specified task. + pub fn new(at: Instant, handle: &Handle) -> TimeoutTokenNew { + TimeoutTokenNew { + inner: CoreFuture { + handle: handle.clone(), + data: Some(at), + result: None, + }, + } + } + /// Returns the instant in time when this timeout token will "fire". /// /// Note that this instant may *not* be the instant that was passed in when @@ -79,5 +40,39 @@ impl TimeoutToken { pub fn when(&self) -> &Instant { &self.when } + + /// Updates a previously added timeout to notify a new task instead. + /// + /// # Panics + /// + /// This method will panic if the timeout specified was not created by this + /// loop handle's `add_timeout` method. + pub fn update_timeout(&self, handle: &Handle) { + handle.send(Message::UpdateTimeout(self.token, task::park())) + } + + /// Cancel a previously added timeout. + /// + /// # Panics + /// + /// This method will panic if the timeout specified was not created by this + /// loop handle's `add_timeout` method. + pub fn cancel_timeout(&self, handle: &Handle) { + debug!("cancel timeout {}", self.token); + handle.send(Message::CancelTimeout(self.token)) + } } +impl Future for TimeoutTokenNew { + type Item = TimeoutToken; + type Error = io::Error; + + fn poll(&mut self) -> Poll { + let (t, i) = try_ready!(self.inner.poll(Core::add_timeout, + Message::AddTimeout)); + Ok(TimeoutToken { + token: t, + when: i, + }.into()) + } +} diff --git a/tests/buffered.rs b/tests/buffered.rs index 3da24e85e..63aeca8a8 100644 --- a/tests/buffered.rs +++ b/tests/buffered.rs @@ -9,6 +9,8 @@ use std::io::{Read, Write, BufReader, BufWriter}; use futures::Future; use futures::stream::Stream; use tokio_core::io::copy; +use tokio_core::net::TcpListener; +use tokio_core::reactor::Core; macro_rules! t { ($e:expr) => (match $e { @@ -22,8 +24,8 @@ fn echo_server() { const N: usize = 1024; drop(env_logger::init()); - let mut l = t!(tokio_core::Loop::new()); - let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap()); + let mut l = t!(Core::new()); + let srv = TcpListener::bind(&t!("127.0.0.1:0".parse()), &l.handle()); let srv = t!(l.run(srv)); let addr = t!(srv.local_addr()); diff --git a/tests/chain.rs b/tests/chain.rs index daeb52ab6..a80eceedf 100644 --- a/tests/chain.rs +++ b/tests/chain.rs @@ -8,6 +8,8 @@ use std::io::{Write, Read}; use futures::Future; use futures::stream::Stream; use tokio_core::io::read_to_end; +use tokio_core::net::TcpListener; +use tokio_core::reactor::Core; macro_rules! t { ($e:expr) => (match $e { @@ -18,8 +20,8 @@ macro_rules! t { #[test] fn chain_clients() { - let mut l = t!(tokio_core::Loop::new()); - let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap()); + let mut l = t!(Core::new()); + let srv = TcpListener::bind(&t!("127.0.0.1:0".parse()), &l.handle()); let srv = t!(l.run(srv)); let addr = t!(srv.local_addr()); diff --git a/tests/echo.rs b/tests/echo.rs index a104beef5..a7a85a4c9 100644 --- a/tests/echo.rs +++ b/tests/echo.rs @@ -9,6 +9,8 @@ use std::thread; use futures::Future; use futures::stream::Stream; use tokio_core::io::{copy, TaskIo}; +use tokio_core::net::TcpListener; +use tokio_core::reactor::Core; macro_rules! t { ($e:expr) => (match $e { @@ -21,8 +23,8 @@ macro_rules! t { fn echo_server() { drop(env_logger::init()); - let mut l = t!(tokio_core::Loop::new()); - let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap()); + let mut l = t!(Core::new()); + let srv = TcpListener::bind(&t!("127.0.0.1:0".parse()), &l.handle()); let srv = t!(l.run(srv)); let addr = t!(srv.local_addr()); diff --git a/tests/limit.rs b/tests/limit.rs index d7caaad02..d1e4b0568 100644 --- a/tests/limit.rs +++ b/tests/limit.rs @@ -8,6 +8,8 @@ use std::io::{Write, Read}; use futures::Future; use futures::stream::Stream; use tokio_core::io::read_to_end; +use tokio_core::net::TcpListener; +use tokio_core::reactor::Core; macro_rules! t { ($e:expr) => (match $e { @@ -18,8 +20,8 @@ macro_rules! t { #[test] fn limit() { - let mut l = t!(tokio_core::Loop::new()); - let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap()); + let mut l = t!(Core::new()); + let srv = TcpListener::bind(&t!("127.0.0.1:0".parse()), &l.handle()); let srv = t!(l.run(srv)); let addr = t!(srv.local_addr()); diff --git a/tests/poll.rs b/tests/poll.rs deleted file mode 100644 index 3049c431d..000000000 --- a/tests/poll.rs +++ /dev/null @@ -1,64 +0,0 @@ -extern crate env_logger; -extern crate futures; -extern crate mio; -extern crate tokio_core; - -use futures::{Future, Poll, Async}; -use futures::task; -use tokio_core::{Loop, IoToken, LoopHandle}; - -struct Next(usize); - -impl Future for Next { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - if self.0 == 0 { - task::park().unpark(); - self.0 += 1; - Ok(Async::NotReady) - } else { - Ok(().into()) - } - } -} - -#[test] -fn poll_after_ready() { - drop(env_logger::init()); - - let mut lp = Loop::new().unwrap(); - let handle = lp.handle(); - let (tx, rx) = mio::channel::channel::(); - let (_rx, token) = lp.run(handle.add_source(rx)).unwrap(); - tx.send(2).unwrap(); - lp.run(Next(0)).unwrap(); - lp.run(ScheduleThenPoll { - token: token, - handle: handle, - n: 0, - }).unwrap(); - - struct ScheduleThenPoll { - token: IoToken, - handle: LoopHandle, - n: usize, - } - - impl Future for ScheduleThenPoll { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - if self.n == 0 { - self.handle.schedule_read(&self.token); - self.n += 1; - Ok(Async::NotReady) - } else { - assert!(self.token.take_readiness() & 1 != 0); - Ok(().into()) - } - } - } -} diff --git a/tests/spawn.rs b/tests/spawn.rs index e8b63962c..e3e0bbb55 100644 --- a/tests/spawn.rs +++ b/tests/spawn.rs @@ -3,12 +3,12 @@ extern crate env_logger; extern crate futures; use futures::Future; -use tokio_core::Loop; +use tokio_core::reactor::Core; #[test] fn simple() { drop(env_logger::init()); - let mut lp = Loop::new().unwrap(); + let mut lp = Core::new().unwrap(); let (tx1, rx1) = futures::oneshot(); let (tx2, rx2) = futures::oneshot(); @@ -29,7 +29,7 @@ fn simple() { #[test] fn spawn_in_poll() { drop(env_logger::init()); - let mut lp = Loop::new().unwrap(); + let mut lp = Core::new().unwrap(); let (tx1, rx1) = futures::oneshot(); let (tx2, rx2) = futures::oneshot(); diff --git a/tests/stream-buffered.rs b/tests/stream-buffered.rs index 8474619e6..b63618deb 100644 --- a/tests/stream-buffered.rs +++ b/tests/stream-buffered.rs @@ -9,6 +9,8 @@ use std::thread; use futures::Future; use futures::stream::Stream; use tokio_core::io::{copy, TaskIo}; +use tokio_core::net::TcpListener; +use tokio_core::reactor::Core; macro_rules! t { ($e:expr) => (match $e { @@ -21,8 +23,8 @@ macro_rules! t { fn echo_server() { drop(env_logger::init()); - let mut l = t!(tokio_core::Loop::new()); - let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap()); + let mut l = t!(Core::new()); + let srv = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &l.handle()); let srv = t!(l.run(srv)); let addr = t!(srv.local_addr()); diff --git a/tests/tcp.rs b/tests/tcp.rs index 5384d8b1d..ed76ec14f 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -2,12 +2,14 @@ extern crate env_logger; extern crate futures; extern crate tokio_core; -use std::net::{TcpListener, TcpStream}; +use std::net; use std::sync::mpsc::channel; use std::thread; use futures::Future; use futures::stream::Stream; +use tokio_core::reactor::Core; +use tokio_core::net::{TcpListener, TcpStream}; macro_rules! t { ($e:expr) => (match $e { @@ -19,14 +21,14 @@ macro_rules! t { #[test] fn connect() { drop(env_logger::init()); - let mut l = t!(tokio_core::Loop::new()); - let srv = t!(TcpListener::bind("127.0.0.1:0")); + let mut l = t!(Core::new()); + let srv = t!(net::TcpListener::bind("127.0.0.1:0")); let addr = t!(srv.local_addr()); let t = thread::spawn(move || { t!(srv.accept()).0 }); - let stream = l.handle().tcp_connect(&addr); + let stream = TcpStream::connect(&addr, &l.handle()); let mine = t!(l.run(stream)); let theirs = t.join().unwrap(); @@ -37,8 +39,8 @@ fn connect() { #[test] fn accept() { drop(env_logger::init()); - let mut l = t!(tokio_core::Loop::new()); - let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap()); + let mut l = t!(Core::new()); + let srv = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &l.handle()); let srv = t!(l.run(srv)); let addr = t!(srv.local_addr()); @@ -49,7 +51,7 @@ fn accept() { }).into_future().map_err(|e| e.0); assert!(rx.try_recv().is_err()); let t = thread::spawn(move || { - TcpStream::connect(&addr).unwrap() + net::TcpStream::connect(&addr).unwrap() }); let (mine, _remaining) = t!(l.run(client)); @@ -63,13 +65,13 @@ fn accept() { #[test] fn accept2() { drop(env_logger::init()); - let mut l = t!(tokio_core::Loop::new()); - let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap()); + let mut l = t!(Core::new()); + let srv = TcpListener::bind(&"127.0.0.1:0".parse().unwrap(), &l.handle()); let srv = t!(l.run(srv)); let addr = t!(srv.local_addr()); let t = thread::spawn(move || { - TcpStream::connect(&addr).unwrap() + net::TcpStream::connect(&addr).unwrap() }); let (tx, rx) = channel(); diff --git a/tests/timeout.rs b/tests/timeout.rs index a00b64dc0..c2beaef48 100644 --- a/tests/timeout.rs +++ b/tests/timeout.rs @@ -5,6 +5,7 @@ extern crate tokio_core; use std::time::{Instant, Duration}; use futures::Future; +use tokio_core::reactor::{Core, Timeout}; macro_rules! t { ($e:expr) => (match $e { @@ -16,9 +17,9 @@ macro_rules! t { #[test] fn smoke() { drop(env_logger::init()); - let mut l = t!(tokio_core::Loop::new()); + let mut l = t!(Core::new()); let dur = Duration::from_millis(10); - let timeout = l.handle().timeout(dur).and_then(|t| t); + let timeout = Timeout::new(dur, &l.handle()).and_then(|t| t); let start = Instant::now(); t!(l.run(timeout)); assert!(start.elapsed() >= dur); diff --git a/tests/udp.rs b/tests/udp.rs index 86b9ceee7..03e46da37 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -6,7 +6,8 @@ use std::io; use std::net::SocketAddr; use futures::{Future, Poll}; -use tokio_core::UdpSocket; +use tokio_core::net::UdpSocket; +use tokio_core::reactor::Core; macro_rules! t { ($e:expr) => (match $e { @@ -17,9 +18,9 @@ macro_rules! t { #[test] fn send_messages() { - let mut l = t!(tokio_core::Loop::new()); - let a = l.handle().udp_bind(&"127.0.0.1:0".parse().unwrap()); - let b = l.handle().udp_bind(&"127.0.0.1:0".parse().unwrap()); + let mut l = t!(Core::new()); + let a = UdpSocket::bind(&t!("127.0.0.1:0".parse()), &l.handle()); + let b = UdpSocket::bind(&t!("127.0.0.1:0".parse()), &l.handle()); let (a, b) = t!(l.run(a.join(b))); let a_addr = t!(a.local_addr()); let b_addr = t!(b.local_addr());