mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
Add a Handle::wakeup method (#59)
This method is intended to be used to wake up the reactor from a remote thread if necessary, forcing it to return from a blocked call of `turn` or otherwise prevent the next call to `turn` to from blocking.
This commit is contained in:
committed by
Carl Lerche
parent
a577bfc033
commit
849771ecfa
+16
-5
@@ -1,22 +1,28 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use reactor::{Reactor, Handle};
|
use reactor::{Reactor, Handle};
|
||||||
|
|
||||||
pub struct HelperThread {
|
pub struct HelperThread {
|
||||||
thread: Option<thread::JoinHandle<()>>,
|
thread: Option<thread::JoinHandle<()>>,
|
||||||
reactor: Handle,
|
reactor: Handle,
|
||||||
|
done: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HelperThread {
|
impl HelperThread {
|
||||||
pub fn new() -> io::Result<HelperThread> {
|
pub fn new() -> io::Result<HelperThread> {
|
||||||
let reactor = Reactor::new()?;
|
let reactor = Reactor::new()?;
|
||||||
let reactor_handle = reactor.handle().clone();
|
let reactor_handle = reactor.handle().clone();
|
||||||
let thread = thread::Builder::new().spawn(move || run(reactor))?;
|
let done = Arc::new(AtomicBool::new(false));
|
||||||
|
let done2 = done.clone();
|
||||||
|
let thread = thread::Builder::new().spawn(move || run(reactor, done))?;
|
||||||
|
|
||||||
Ok(HelperThread {
|
Ok(HelperThread {
|
||||||
thread: Some(thread),
|
thread: Some(thread),
|
||||||
reactor: reactor_handle,
|
reactor: reactor_handle,
|
||||||
|
done: done2,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,13 +37,18 @@ impl HelperThread {
|
|||||||
|
|
||||||
impl Drop for HelperThread {
|
impl Drop for HelperThread {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// TODO: kill the reactor thread and wait for it to exit, needs
|
let thread = match self.thread.take() {
|
||||||
// `Handle::wakeup` to be implemented in a future PR
|
Some(thread) => thread,
|
||||||
|
None => return
|
||||||
|
};
|
||||||
|
self.done.store(true, Ordering::SeqCst);
|
||||||
|
self.reactor.wakeup();
|
||||||
|
thread.join().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(mut reactor: Reactor) {
|
fn run(mut reactor: Reactor, done: Arc<AtomicBool>) {
|
||||||
loop {
|
while !done.load(Ordering::SeqCst) {
|
||||||
reactor.turn(None);
|
reactor.turn(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-3
@@ -51,6 +51,8 @@ pub struct Reactor {
|
|||||||
|
|
||||||
/// State shared between the reactor and the handles.
|
/// State shared between the reactor and the handles.
|
||||||
inner: Arc<Inner>,
|
inner: Arc<Inner>,
|
||||||
|
|
||||||
|
_wakeup_registration: mio::Registration,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Inner {
|
struct Inner {
|
||||||
@@ -59,6 +61,9 @@ struct Inner {
|
|||||||
|
|
||||||
/// Dispatch slabs for I/O and futures events
|
/// Dispatch slabs for I/O and futures events
|
||||||
io_dispatch: RwLock<Slab<ScheduledIo>>,
|
io_dispatch: RwLock<Slab<ScheduledIo>>,
|
||||||
|
|
||||||
|
/// Used to wake up the reactor from a call to `turn`
|
||||||
|
wakeup: mio::SetReadiness
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handle to an event loop.
|
/// A handle to an event loop.
|
||||||
@@ -82,6 +87,7 @@ enum Direction {
|
|||||||
Write,
|
Write,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TOKEN_WAKEUP: mio::Token = mio::Token(0);
|
||||||
const TOKEN_START: usize = 1;
|
const TOKEN_START: usize = 1;
|
||||||
|
|
||||||
fn _assert_kinds() {
|
fn _assert_kinds() {
|
||||||
@@ -94,14 +100,21 @@ impl Reactor {
|
|||||||
/// Creates a new event loop, returning any error that happened during the
|
/// Creates a new event loop, returning any error that happened during the
|
||||||
/// creation.
|
/// creation.
|
||||||
pub fn new() -> io::Result<Reactor> {
|
pub fn new() -> io::Result<Reactor> {
|
||||||
// Create the I/O poller
|
let io = mio::Poll::new()?;
|
||||||
let io = try!(mio::Poll::new());
|
let wakeup_pair = mio::Registration::new2();
|
||||||
|
|
||||||
|
io.register(&wakeup_pair.0,
|
||||||
|
TOKEN_WAKEUP,
|
||||||
|
mio::Ready::readable(),
|
||||||
|
mio::PollOpt::level())?;
|
||||||
|
|
||||||
Ok(Reactor {
|
Ok(Reactor {
|
||||||
events: mio::Events::with_capacity(1024),
|
events: mio::Events::with_capacity(1024),
|
||||||
|
_wakeup_registration: wakeup_pair.0,
|
||||||
inner: Arc::new(Inner {
|
inner: Arc::new(Inner {
|
||||||
io: io,
|
io: io,
|
||||||
io_dispatch: RwLock::new(Slab::with_capacity(1)),
|
io_dispatch: RwLock::new(Slab::with_capacity(1)),
|
||||||
|
wakeup: wakeup_pair.1,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -146,7 +159,12 @@ impl Reactor {
|
|||||||
let token = event.token();
|
let token = event.token();
|
||||||
trace!("event {:?} {:?}", event.readiness(), event.token());
|
trace!("event {:?} {:?}", event.readiness(), event.token());
|
||||||
|
|
||||||
self.dispatch(token, event.readiness());
|
|
||||||
|
if token == TOKEN_WAKEUP {
|
||||||
|
self.inner.wakeup.set_readiness(mio::Ready::empty()).unwrap();
|
||||||
|
} else {
|
||||||
|
self.dispatch(token, event.readiness());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,6 +299,21 @@ impl Handle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Forces a reactor blocked in a call to `turn` to wakeup, or otherwise
|
||||||
|
/// makes the next call to `turn` return immediately.
|
||||||
|
///
|
||||||
|
/// This method is intended to be used in situations where a notification
|
||||||
|
/// needs to otherwise be sent to the main reactor. If the reactor is
|
||||||
|
/// currently blocked inside of `turn` then it will wake up and soon return
|
||||||
|
/// after this method has been called. If the reactor is not currently
|
||||||
|
/// blocked in `turn`, then the next call to `turn` will not block and
|
||||||
|
/// return immediately.
|
||||||
|
pub fn wakeup(&self) {
|
||||||
|
if let Some(inner) = self.inner() {
|
||||||
|
inner.wakeup.set_readiness(mio::Ready::readable()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn into_usize(self) -> usize {
|
fn into_usize(self) -> usize {
|
||||||
unsafe {
|
unsafe {
|
||||||
mem::transmute::<Weak<Inner>, usize>(self.inner)
|
mem::transmute::<Weak<Inner>, usize>(self.inner)
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
extern crate tokio;
|
||||||
|
|
||||||
|
use std::thread;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
|
||||||
|
use tokio::reactor::Reactor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn works() {
|
||||||
|
let mut r = Reactor::new().unwrap();
|
||||||
|
r.handle().wakeup();
|
||||||
|
r.turn(None);
|
||||||
|
|
||||||
|
let now = Instant::now();
|
||||||
|
let mut n = 0;
|
||||||
|
while now.elapsed() < Duration::from_millis(10) {
|
||||||
|
n += 1;
|
||||||
|
r.turn(Some(Duration::from_millis(10)));
|
||||||
|
}
|
||||||
|
assert!(n < 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wakes() {
|
||||||
|
const N: usize = 1_000;
|
||||||
|
|
||||||
|
let mut r = Reactor::new().unwrap();
|
||||||
|
let handle = r.handle();
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let t = thread::spawn(move || {
|
||||||
|
for _ in 0..N {
|
||||||
|
rx.recv().unwrap();
|
||||||
|
handle.wakeup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for _ in 0..N {
|
||||||
|
tx.send(()).unwrap();
|
||||||
|
r.turn(None);
|
||||||
|
}
|
||||||
|
t.join().unwrap();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user