Add PollEvented::poll_ready

This commit adds a general-purpose method for querying the readiness of a
`PollEvented` type. This new method, `poll_ready`, takes a blanket `mio::Ready`
and tests if any part of it is ready. The purpose of this is to expose
platform-specific events through `PollReady` such as `hup` and `error` events
other than just the platform-agnostic readable/writable events.

The semanatics of this method are:

* The `poll_ready` function takes a mask, and the return value is either
  `Async::Ready` with a subset of these events that are ready or `None` if none
  of them are ready.
* There can be up to two tasks blocked on a `PollEvented`, so we need to pick
  which one is suitable for these new events. Currently all events are routed to
  the `read` task unless the writable bit is set. This is mostly only relevant
  for multi-task usage or if you're manually calling `need_read` and/or
  `need_write`, and hopefully the docs will cover this now.
This commit is contained in:
Alex Crichton
2017-05-19 11:23:00 -07:00
parent b92fd2d22a
commit 4dd3d30f2a
2 changed files with 177 additions and 49 deletions
+73 -26
View File
@@ -139,13 +139,6 @@ enum Message {
Run(Box<FnBox>),
}
#[repr(usize)]
#[derive(Clone, Copy, Debug, PartialEq)]
enum Readiness {
Readable = 1,
Writable = 2,
}
const TOKEN_MESSAGES: mio::Token = mio::Token(0);
const TOKEN_FUTURE: mio::Token = mio::Token(1);
const TOKEN_START: usize = 2;
@@ -330,15 +323,12 @@ impl Core {
let mut writer = None;
let mut inner = self.inner.borrow_mut();
if let Some(io) = inner.io_dispatch.get_mut(token) {
if ready.is_readable() || platform::is_hup(&ready) {
reader = io.reader.take();
io.readiness.fetch_or(Readiness::Readable as usize,
Ordering::Relaxed);
}
io.readiness.fetch_or(ready2usize(ready), Ordering::Relaxed);
if ready.is_writable() {
writer = io.writer.take();
io.readiness.fetch_or(Readiness::Writable as usize,
Ordering::Relaxed);
}
if !(ready & (!mio::Ready::writable())).is_empty() {
reader = io.reader.take();
}
}
drop(inner);
@@ -497,14 +487,16 @@ impl Inner {
-> Option<Task> {
debug!("scheduling direction for: {}", token);
let sched = self.io_dispatch.get_mut(token).unwrap();
let (slot, bit) = match dir {
Direction::Read => (&mut sched.reader, Readiness::Readable as usize),
Direction::Write => (&mut sched.writer, Readiness::Writable as usize),
let (slot, ready) = match dir {
Direction::Read => (&mut sched.reader, !mio::Ready::writable()),
Direction::Write => (&mut sched.writer, mio::Ready::writable()),
};
if sched.readiness.load(Ordering::SeqCst) & bit != 0 {
if sched.readiness.load(Ordering::SeqCst) & ready2usize(ready) != 0 {
debug!("cancelling block");
*slot = None;
Some(wake)
} else {
debug!("blocking");
*slot = Some(wake);
None
}
@@ -754,29 +746,84 @@ impl<F: FnOnce(&Core) + Send + 'static> FnBox for F {
}
}
fn read_ready() -> mio::Ready {
mio::Ready::readable() | platform::hup()
}
const READ: usize = 1 << 0;
const WRITE: usize = 1 << 1;
fn ready2usize(ready: mio::Ready) -> usize {
let mut bits = 0;
if ready.is_readable() {
bits |= READ;
}
if ready.is_writable() {
bits |= WRITE;
}
bits | platform::ready2usize(ready)
}
fn usize2ready(bits: usize) -> mio::Ready {
let mut ready = mio::Ready::empty();
if bits & READ != 0 {
ready.insert(mio::Ready::readable());
}
if bits & WRITE != 0 {
ready.insert(mio::Ready::writable());
}
ready | platform::usize2ready(bits)
}
#[cfg(unix)]
mod platform {
use mio::Ready;
use mio::unix::UnixReady;
pub fn is_hup(event: &Ready) -> bool {
UnixReady::from(*event).is_hup()
}
pub fn hup() -> Ready {
UnixReady::hup().into()
}
const HUP: usize = 1 << 2;
const ERROR: usize = 1 << 3;
pub fn ready2usize(ready: Ready) -> usize {
let ready = UnixReady::from(ready);
let mut bits = 0;
if ready.is_error() {
bits |= ERROR;
}
if ready.is_hup() {
bits |= HUP;
}
bits
}
pub fn usize2ready(bits: usize) -> Ready {
let mut ready = UnixReady::from(Ready::empty());
if bits & HUP != 0 {
ready.insert(UnixReady::hup());
}
if bits & ERROR != 0 {
ready.insert(UnixReady::error());
}
ready.into()
}
}
#[cfg(windows)]
mod platform {
use mio::Ready;
pub fn is_hup(_event: &Ready) -> bool {
false
}
pub fn hup() -> Ready {
Ready::empty()
}
pub fn ready2usize(_r: Ready) -> usize {
0
}
pub fn usize2ready(_r: usize) -> Ready {
Ready::empty()
}
}