mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-26 00:00:16 +02:00
Fix race condition related bugs (#243)
* Fix races. This mostly pulls in changes from rust-lang-nursery/futures-rs#881, but also updates Registration to be a bit more obvious as to what is going on. * Reduce spurious wakeups caused by Reactor This patch adds an ABA guard on token values before registering them with Mio. This allows catching token reuse and avoid the notification. This is needed for OS X as the notification is used to determine that a TCP connect has completed. A spurious notification can potentially cause write failures.
This commit is contained in:
@@ -186,7 +186,7 @@ fn force_shutdown_drops_futures() {
|
||||
let a = num_inc.clone();
|
||||
let b = num_dec.clone();
|
||||
|
||||
let mut pool = Builder::new()
|
||||
let pool = Builder::new()
|
||||
.around_worker(move |w, _| {
|
||||
a.fetch_add(1, Relaxed);
|
||||
w.run();
|
||||
@@ -548,3 +548,103 @@ fn panic_in_task() {
|
||||
|
||||
await_shutdown(pool.shutdown_on_idle());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "unstable-futures"))]
|
||||
fn hammer() {
|
||||
use futures::future;
|
||||
use futures::sync::{oneshot, mpsc};
|
||||
|
||||
const N: usize = 1000;
|
||||
const ITER: usize = 20;
|
||||
|
||||
struct Counted<T> {
|
||||
cnt: Arc<AtomicUsize>,
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T: Future> Future for Counted<T> {
|
||||
type Item = T::Item;
|
||||
type Error = T::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<T::Item, T::Error> {
|
||||
self.inner.poll()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Counted<T> {
|
||||
fn drop(&mut self) {
|
||||
self.cnt.fetch_add(1, Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0.. ITER {
|
||||
println!("~~~ ITER {} ~~~", i);
|
||||
|
||||
let pool = Builder::new()
|
||||
// .pool_size(30)
|
||||
.build();
|
||||
|
||||
let cnt = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
let (listen_tx, listen_rx) = mpsc::unbounded::<oneshot::Sender<oneshot::Sender<()>>>();
|
||||
let mut listen_tx = listen_tx.wait();
|
||||
|
||||
pool.spawn({
|
||||
let c1 = cnt.clone();
|
||||
let c2 = cnt.clone();
|
||||
let pool = pool.sender().clone();
|
||||
let task = listen_rx
|
||||
.map_err(|e| panic!("accept error = {:?}", e))
|
||||
.for_each(move |tx| {
|
||||
let task = future::lazy(|| {
|
||||
let (tx2, rx2) = oneshot::channel();
|
||||
|
||||
tx.send(tx2).unwrap();
|
||||
rx2
|
||||
})
|
||||
.map_err(|e| panic!("e={:?}", e))
|
||||
.and_then(|_| {
|
||||
Ok(())
|
||||
});
|
||||
|
||||
pool.spawn(Counted {
|
||||
inner: task,
|
||||
cnt: c1.clone(),
|
||||
}).unwrap();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
Counted {
|
||||
inner: task,
|
||||
cnt: c2,
|
||||
}
|
||||
});
|
||||
|
||||
for _ in 0..N {
|
||||
let cnt = cnt.clone();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
listen_tx.send(tx).unwrap();
|
||||
|
||||
pool.spawn({
|
||||
let task = rx
|
||||
.map_err(|e| panic!("rx err={:?}", e))
|
||||
.and_then(|tx| {
|
||||
tx.send(()).unwrap();
|
||||
Ok(())
|
||||
});
|
||||
|
||||
Counted {
|
||||
inner: task,
|
||||
cnt,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
drop(listen_tx);
|
||||
|
||||
pool.shutdown_on_idle().wait().unwrap();
|
||||
assert_eq!(N * 2 + 1, cnt.load(Relaxed));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user