Files
tokio/src/reactor/poll_evented.rs
T

262 lines
8.5 KiB
Rust
Raw Normal View History

2016-09-02 11:07:52 -07:00
//! 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};
2016-08-17 09:29:05 -07:00
use std::sync::atomic::{AtomicUsize, Ordering};
2016-07-30 17:53:12 -07:00
2016-09-07 16:11:19 -07:00
use futures::Async;
2016-08-20 22:59:14 -07:00
use mio;
2016-07-30 17:53:12 -07:00
2016-09-02 11:07:52 -07:00
use io::Io;
2016-09-07 16:11:19 -07:00
use reactor::{Handle, Remote};
use reactor::io_token::IoToken;
2016-07-30 17:53:12 -07:00
2016-08-01 17:41:58 -07:00
/// A concrete implementation of a stream of readiness notifications for I/O
/// objects that originates from an event loop.
///
2016-09-02 11:07:52 -07:00
/// Created by the `PollEvented::new` method, each `PollEvented` is
2016-08-01 17:41:58 -07:00
/// associated with a specific event loop and source of events that will be
/// registered with an event loop.
///
2016-08-18 10:21:25 -07:00
/// Each readiness stream has a number of methods to test whether the underlying
/// object is readable or writable. Once the methods return that an object is
/// readable/writable, then it will continue to do so until the `need_read` or
/// `need_write` methods are called.
2016-08-01 17:41:58 -07:00
///
2016-08-18 10:21:25 -07:00
/// That is, this object is typically wrapped in another form of I/O object.
/// 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.
2016-09-02 11:07:52 -07:00
pub struct PollEvented<E> {
2016-08-20 22:59:14 -07:00
token: IoToken,
2016-09-07 16:11:19 -07:00
handle: Remote,
2016-08-17 09:29:05 -07:00
readiness: AtomicUsize,
2016-08-20 22:59:14 -07:00
io: E,
2016-07-30 17:53:12 -07:00
}
2016-09-07 16:11:19 -07:00
impl<E: mio::Evented> PollEvented<E> {
2016-08-01 17:41:58 -07:00
/// Creates a new readiness stream associated with the provided
/// `loop_handle` and for the given `source`.
///
/// This method returns a future which will resolve to the readiness stream
/// when it's ready.
2016-09-07 16:11:19 -07:00
pub fn new(io: E, handle: &Handle) -> io::Result<PollEvented<E>> {
Ok(PollEvented {
token: try!(IoToken::new(&io, handle)),
handle: handle.remote().clone(),
readiness: AtomicUsize::new(0),
io: io,
})
}
2016-08-20 23:41:19 -07:00
}
2016-08-17 09:29:05 -07:00
2016-09-02 11:07:52 -07:00
impl<E> PollEvented<E> {
2016-08-17 09:29:05 -07:00
/// Tests to see if this source is ready to be read from or not.
2016-08-18 10:21:25 -07:00
///
/// If this stream is not ready for a read then `NotReady` will be returned
/// and the current task will be scheduled to receive a notification when
/// 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.
2016-09-02 11:07:52 -07:00
pub fn poll_read(&self) -> Async<()> {
2016-08-17 09:29:05 -07:00
if self.readiness.load(Ordering::SeqCst) & 1 != 0 {
2016-09-02 11:07:52 -07:00
return Async::Ready(())
2016-08-17 09:29:05 -07:00
}
2016-08-20 22:59:14 -07:00
self.readiness.fetch_or(self.token.take_readiness(), Ordering::SeqCst);
2016-08-17 09:29:05 -07:00
if self.readiness.load(Ordering::SeqCst) & 1 != 0 {
2016-09-02 11:07:52 -07:00
Async::Ready(())
2016-08-17 09:29:05 -07:00
} else {
2016-09-02 11:07:52 -07:00
self.token.schedule_read(&self.handle);
Async::NotReady
2016-08-17 09:29:05 -07:00
}
}
/// Tests to see if this source is ready to be written to or not.
2016-08-18 10:21:25 -07:00
///
/// If this stream is not ready for a write then `NotReady` will be returned
/// and the current task will be scheduled to receive a notification when
/// 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.
2016-09-02 11:07:52 -07:00
pub fn poll_write(&self) -> Async<()> {
2016-08-17 09:29:05 -07:00
if self.readiness.load(Ordering::SeqCst) & 2 != 0 {
2016-09-02 11:07:52 -07:00
return Async::Ready(())
2016-08-17 09:29:05 -07:00
}
2016-08-20 22:59:14 -07:00
self.readiness.fetch_or(self.token.take_readiness(), Ordering::SeqCst);
2016-08-17 09:29:05 -07:00
if self.readiness.load(Ordering::SeqCst) & 2 != 0 {
2016-09-02 11:07:52 -07:00
Async::Ready(())
2016-08-17 09:29:05 -07:00
} else {
2016-09-02 11:07:52 -07:00
self.token.schedule_write(&self.handle);
Async::NotReady
2016-08-17 09:29:05 -07:00
}
}
2016-08-18 10:21:25 -07:00
/// Indicates to this source of events that the corresponding I/O object is
/// no longer readable, but it needs to be.
///
/// This function, like `poll_read`, is only safe to call from the context
/// of a future's task (typically in a `Future::poll` implementation). It
/// informs this readiness stream that the underlying object is no longer
/// readable, typically because a "would block" error was seen.
///
/// The flag indicating that this stream is readable is unset and the
/// current task is scheduled to receive a notification when the stream is
/// then again readable.
2016-08-17 09:29:05 -07:00
pub fn need_read(&self) {
self.readiness.fetch_and(!1, Ordering::SeqCst);
2016-09-02 11:07:52 -07:00
self.token.schedule_read(&self.handle)
2016-08-17 09:29:05 -07:00
}
2016-08-18 10:21:25 -07:00
/// Indicates to this source of events that the corresponding I/O object is
/// no longer writable, but it needs to be.
///
/// This function, like `poll_write`, is only safe to call from the context
/// of a future's task (typically in a `Future::poll` implementation). It
/// informs this readiness stream that the underlying object is no longer
/// writable, typically because a "would block" error was seen.
///
/// The flag indicating that this stream is writable is unset and the
/// current task is scheduled to receive a notification when the stream is
/// then again writable.
2016-08-17 09:29:05 -07:00
pub fn need_write(&self) {
self.readiness.fetch_and(!2, Ordering::SeqCst);
2016-09-02 11:07:52 -07:00
self.token.schedule_write(&self.handle)
2016-08-20 22:59:14 -07:00
}
/// Returns a reference to the event loop handle that this readiness stream
/// is associated with.
2016-09-07 16:11:19 -07:00
pub fn remote(&self) -> &Remote {
2016-08-20 22:59:14 -07:00
&self.handle
}
/// Returns a shared reference to the underlying I/O object this readiness
/// stream is wrapping.
pub fn get_ref(&self) -> &E {
&self.io
}
/// Returns a mutable reference to the underlying I/O object this readiness
/// stream is wrapping.
pub fn get_mut(&mut self) -> &mut E {
&mut self.io
2016-08-17 09:29:05 -07:00
}
}
2016-09-02 11:07:52 -07:00
impl<E: Read> Read for PollEvented<E> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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<E: Write> Write for PollEvented<E> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
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<E: Read + Write> Io for PollEvented<E> {
fn poll_read(&mut self) -> Async<()> {
<PollEvented<E>>::poll_read(self)
}
fn poll_write(&mut self) -> Async<()> {
<PollEvented<E>>::poll_write(self)
}
}
impl<'a, E> Read for &'a PollEvented<E>
where &'a E: Read,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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<E>
where &'a E: Write,
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
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<E>
where &'a E: Read + Write,
{
fn poll_read(&mut self) -> Async<()> {
<PollEvented<E>>::poll_read(self)
}
fn poll_write(&mut self) -> Async<()> {
<PollEvented<E>>::poll_write(self)
}
}
fn is_wouldblock<T>(r: &io::Result<T>) -> bool {
match *r {
Ok(_) => false,
Err(ref e) => e.kind() == io::ErrorKind::WouldBlock,
}
}
impl<E> Drop for PollEvented<E> {
fn drop(&mut self) {
self.token.drop_source(&self.handle);
}
}