mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-09-28 12:10:37 +00:00

Introduce a tokio-sync crate containing useful synchronization primitives for programs written using Tokio. The initial release contains: * An mpsc channel * A oneshot channel * A semaphore implementation * An `AtomicTask` primitive. The `oneshot` and `mpsc` channels are new implementations providing improved performance characteristics. In some benchmarks, the new mpsc channel shows up to 7x improvement over the version provided by the `futures` crate. Unfortunately, the `oneshot` implementation only provides a slight performance improvement as it is mostly limited by the `futures` 0.1 task system. Once updated to the `std` version of `Future` (currently nightly only), much greater performance improvements should be achievable by `oneshot`. Additionally, he implementations provided here are checked using [Loom](http://github.com/carllerche/loom/), which provides greater confidence of correctness.
135 lines
2.9 KiB
Rust
135 lines
2.9 KiB
Rust
#[macro_use]
|
|
extern crate futures;
|
|
#[macro_use]
|
|
extern crate loom;
|
|
|
|
#[path = "../src/semaphore.rs"]
|
|
#[allow(warnings)]
|
|
mod semaphore;
|
|
|
|
use semaphore::*;
|
|
|
|
use futures::{future, Future, Async, Poll};
|
|
use loom::thread;
|
|
use loom::futures::block_on;
|
|
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::AtomicUsize;
|
|
use std::sync::atomic::Ordering::SeqCst;
|
|
|
|
#[test]
|
|
fn basic_usage() {
|
|
const NUM: usize = 2;
|
|
|
|
struct Actor {
|
|
waiter: Permit,
|
|
shared: Arc<Shared>,
|
|
}
|
|
|
|
struct Shared {
|
|
semaphore: Semaphore,
|
|
active: AtomicUsize,
|
|
}
|
|
|
|
impl Future for Actor {
|
|
type Item = ();
|
|
type Error = ();
|
|
|
|
fn poll(&mut self) -> Poll<(), ()> {
|
|
try_ready!(
|
|
self.waiter.poll_acquire(&self.shared.semaphore)
|
|
.map_err(|_| ()));
|
|
|
|
let actual = self.shared.active.fetch_add(1, SeqCst);
|
|
assert!(actual <= NUM-1);
|
|
|
|
let actual = self.shared.active.fetch_sub(1, SeqCst);
|
|
assert!(actual <= NUM);
|
|
|
|
self.waiter.release(&self.shared.semaphore);
|
|
|
|
Ok(Async::Ready(()))
|
|
}
|
|
}
|
|
|
|
loom::fuzz(|| {
|
|
let shared = Arc::new(Shared {
|
|
semaphore: Semaphore::new(NUM),
|
|
active: AtomicUsize::new(0),
|
|
});
|
|
|
|
for _ in 0..NUM {
|
|
let shared = shared.clone();
|
|
|
|
thread::spawn(move || {
|
|
block_on(Actor {
|
|
waiter: Permit::new(),
|
|
shared,
|
|
}).unwrap();
|
|
});
|
|
}
|
|
|
|
block_on(Actor {
|
|
waiter: Permit::new(),
|
|
shared
|
|
}).unwrap();
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn basic_closing() {
|
|
const NUM: usize = 2;
|
|
|
|
loom::fuzz(|| {
|
|
let semaphore = Arc::new(Semaphore::new(1));
|
|
|
|
for _ in 0..NUM {
|
|
let semaphore = semaphore.clone();
|
|
|
|
thread::spawn(move || {
|
|
let mut permit = Permit::new();
|
|
|
|
for _ in 0..2 {
|
|
block_on(future::poll_fn(|| {
|
|
permit.poll_acquire(&semaphore)
|
|
.map_err(|_| ())
|
|
}))?;
|
|
permit.release(&semaphore);
|
|
}
|
|
|
|
Ok::<(), ()>(())
|
|
});
|
|
}
|
|
|
|
semaphore.close();
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn concurrent_close() {
|
|
const NUM: usize = 3;
|
|
|
|
loom::fuzz(|| {
|
|
let semaphore = Arc::new(Semaphore::new(1));
|
|
|
|
for _ in 0..NUM {
|
|
let semaphore = semaphore.clone();
|
|
|
|
thread::spawn(move || {
|
|
let mut permit = Permit::new();
|
|
|
|
block_on(future::poll_fn(|| {
|
|
permit.poll_acquire(&semaphore)
|
|
.map_err(|_| ())
|
|
}))?;
|
|
|
|
permit.release(&semaphore);
|
|
|
|
semaphore.close();
|
|
|
|
Ok::<(), ()>(())
|
|
});
|
|
}
|
|
});
|
|
}
|