mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-27 00:00:12 +02:00
@@ -1,118 +0,0 @@
|
||||
//! A thin wrapper around a mpsc queue and mio-based channel information
|
||||
//!
|
||||
//! Normally the standard library's channels would suffice but we unfortunately
|
||||
//! need the `Sender<T>` half to be `Sync`, so to accomplish this for now we
|
||||
//! just vendor the same mpsc queue as the one in the standard library and then
|
||||
//! we pair that with the `mio::channel` module's Ctl pairs to control the
|
||||
//! readiness notifications on the channel.
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::io;
|
||||
use std::marker;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mio;
|
||||
use mio::channel::{ctl_pair, SenderCtl, ReceiverCtl};
|
||||
|
||||
use mpsc_queue::{Queue, PopResult};
|
||||
|
||||
pub struct Sender<T> {
|
||||
ctl: SenderCtl,
|
||||
inner: Arc<Queue<T>>,
|
||||
}
|
||||
|
||||
pub struct Receiver<T> {
|
||||
ctl: ReceiverCtl,
|
||||
inner: Arc<Queue<T>>,
|
||||
_marker: marker::PhantomData<Cell<()>>, // this type is not Sync
|
||||
}
|
||||
|
||||
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
|
||||
let inner = Arc::new(Queue::new());
|
||||
let (tx, rx) = ctl_pair();
|
||||
|
||||
let tx = Sender {
|
||||
ctl: tx,
|
||||
inner: inner.clone(),
|
||||
};
|
||||
let rx = Receiver {
|
||||
ctl: rx,
|
||||
inner: inner.clone(),
|
||||
_marker: marker::PhantomData,
|
||||
};
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
pub fn send(&self, data: T) -> io::Result<()> {
|
||||
self.inner.push(data);
|
||||
self.ctl.inc()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Receiver<T> {
|
||||
pub fn recv(&self) -> io::Result<Option<T>> {
|
||||
// Note that the underlying method is `unsafe` because it's only safe
|
||||
// if one thread accesses it at a time.
|
||||
//
|
||||
// We, however, are the only thread with a `Receiver<T>` because this
|
||||
// type is not `Sync`. and we never handed out another instance.
|
||||
match unsafe { self.inner.pop() } {
|
||||
PopResult::Data(t) => {
|
||||
try!(self.ctl.dec());
|
||||
Ok(Some(t))
|
||||
}
|
||||
|
||||
// If the queue is either in an inconsistent or empty state, then
|
||||
// we return `None` for both instances. Note that the standard
|
||||
// library performs a yield loop in the event of `Inconsistent`,
|
||||
// which means that there's data in the queue but a sender hasn't
|
||||
// finished their operation yet.
|
||||
//
|
||||
// We do this because the queue will continue to be readable as
|
||||
// the thread performing the push will eventually call `inc`, so
|
||||
// if we return `None` and the event loop just loops aruond calling
|
||||
// this method then we'll eventually get back to the same spot
|
||||
// and due the retry.
|
||||
//
|
||||
// Basically, the inconsistent state doesn't mean we need to busy
|
||||
// wait, but instead we can forge ahead and assume by the time we
|
||||
// go to the kernel and come back we'll no longer be in an
|
||||
// inconsistent state.
|
||||
PopResult::Empty |
|
||||
PopResult::Inconsistent => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Just delegate everything to `self.ctl`
|
||||
impl<T> mio::Evented for Receiver<T> {
|
||||
fn register(&self,
|
||||
poll: &mio::Poll,
|
||||
token: mio::Token,
|
||||
interest: mio::Ready,
|
||||
opts: mio::PollOpt) -> io::Result<()> {
|
||||
self.ctl.register(poll, token, interest, opts)
|
||||
}
|
||||
|
||||
fn reregister(&self,
|
||||
poll: &mio::Poll,
|
||||
token: mio::Token,
|
||||
interest: mio::Ready,
|
||||
opts: mio::PollOpt) -> io::Result<()> {
|
||||
self.ctl.reregister(poll, token, interest, opts)
|
||||
}
|
||||
|
||||
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
|
||||
self.ctl.deregister(poll)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Sender<T> {
|
||||
fn clone(&self) -> Sender<T> {
|
||||
Sender {
|
||||
ctl: self.ctl.clone(),
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
-20
@@ -15,16 +15,15 @@ use std::time::{Instant, Duration};
|
||||
|
||||
use futures::{self, Future, IntoFuture, Async};
|
||||
use futures::executor::{self, Spawn, Unpark};
|
||||
use futures::sync::mpsc;
|
||||
use futures::task::Task;
|
||||
use mio;
|
||||
use slab::Slab;
|
||||
|
||||
use heap::{Heap, Slot};
|
||||
|
||||
mod channel;
|
||||
mod io_token;
|
||||
mod timeout_token;
|
||||
use self::channel::{Sender, Receiver, channel};
|
||||
|
||||
mod poll_evented;
|
||||
mod timeout;
|
||||
@@ -45,8 +44,11 @@ scoped_thread_local!(static CURRENT_LOOP: Core);
|
||||
// TODO: expand this
|
||||
pub struct Core {
|
||||
events: mio::Events,
|
||||
tx: Sender<Message>,
|
||||
rx: Receiver<Message>,
|
||||
tx: mpsc::UnboundedSender<Message>,
|
||||
rx: RefCell<Spawn<mpsc::UnboundedReceiver<Message>>>,
|
||||
_rx_registration: mio::Registration,
|
||||
rx_readiness: Arc<MySetReadiness>,
|
||||
|
||||
inner: Rc<RefCell<Inner>>,
|
||||
|
||||
// Used for determining when the future passed to `run` is ready. Once the
|
||||
@@ -82,7 +84,7 @@ struct Inner {
|
||||
#[derive(Clone)]
|
||||
pub struct Remote {
|
||||
id: usize,
|
||||
tx: Sender<Message>,
|
||||
tx: mpsc::UnboundedSender<Message>,
|
||||
}
|
||||
|
||||
/// A non-sendable handle to an event loop, useful for manufacturing instances
|
||||
@@ -133,20 +135,26 @@ impl Core {
|
||||
/// Creates a new event loop, returning any error that happened during the
|
||||
/// creation.
|
||||
pub fn new() -> io::Result<Core> {
|
||||
let (tx, rx) = channel();
|
||||
let io = try!(mio::Poll::new());
|
||||
try!(io.register(&rx,
|
||||
TOKEN_MESSAGES,
|
||||
mio::Ready::readable(),
|
||||
mio::PollOpt::edge()));
|
||||
let future_pair = mio::Registration::new(&io,
|
||||
TOKEN_FUTURE,
|
||||
mio::Ready::readable(),
|
||||
mio::PollOpt::level());
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
let channel_pair = mio::Registration::new(&io,
|
||||
TOKEN_MESSAGES,
|
||||
mio::Ready::readable(),
|
||||
mio::PollOpt::level());
|
||||
let rx_readiness = Arc::new(MySetReadiness(channel_pair.1));
|
||||
rx_readiness.unpark();
|
||||
|
||||
Ok(Core {
|
||||
events: mio::Events::with_capacity(1024),
|
||||
tx: tx,
|
||||
rx: rx,
|
||||
rx: RefCell::new(executor::spawn(rx)),
|
||||
_rx_registration: channel_pair.0,
|
||||
rx_readiness: rx_readiness,
|
||||
|
||||
_future_registration: future_pair.0,
|
||||
future_readiness: Arc::new(MySetReadiness(future_pair.1)),
|
||||
|
||||
@@ -274,6 +282,7 @@ impl Core {
|
||||
trace!("event {:?} {:?}", event.kind(), event.token());
|
||||
|
||||
if token == TOKEN_MESSAGES {
|
||||
self.rx_readiness.0.set_readiness(mio::Ready::none()).unwrap();
|
||||
CURRENT_LOOP.set(&self, || self.consume_queue());
|
||||
} else if token == TOKEN_FUTURE {
|
||||
self.future_readiness.0.set_readiness(mio::Ready::none()).unwrap();
|
||||
@@ -377,8 +386,13 @@ impl Core {
|
||||
fn consume_queue(&self) {
|
||||
debug!("consuming notification queue");
|
||||
// TODO: can we do better than `.unwrap()` here?
|
||||
while let Some(msg) = self.rx.recv().unwrap() {
|
||||
self.notify(msg);
|
||||
let unpark = self.rx_readiness.clone();
|
||||
loop {
|
||||
match self.rx.borrow_mut().poll_stream(unpark.clone()).unwrap() {
|
||||
Async::Ready(Some(msg)) => self.notify(msg),
|
||||
Async::NotReady |
|
||||
Async::Ready(None) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,15 +539,15 @@ impl Remote {
|
||||
lp.notify(msg);
|
||||
}
|
||||
None => {
|
||||
match self.tx.send(msg) {
|
||||
// TODO: shouldn't have to `clone` here, can we upstream
|
||||
// that &self works with `UnboundedSender`?
|
||||
match mpsc::UnboundedSender::send(&mut self.tx.clone(), msg) {
|
||||
Ok(()) => {}
|
||||
|
||||
// This should only happen when there was an error
|
||||
// writing to the pipe to wake up the event loop,
|
||||
// hopefully that never happens
|
||||
Err(e) => {
|
||||
panic!("error sending message to event loop: {}", e)
|
||||
}
|
||||
// TODO: this error should punt upwards and we should
|
||||
// notify the caller that the message wasn't
|
||||
// received. This is tokio-core#17
|
||||
Err(e) => drop(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user