mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-16 00:00:12 +02: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.
175 lines
3.9 KiB
Rust
175 lines
3.9 KiB
Rust
extern crate futures;
|
|
extern crate tokio_mock_task;
|
|
extern crate tokio_sync;
|
|
|
|
use tokio_sync::semaphore::{Semaphore, Permit};
|
|
use tokio_mock_task::*;
|
|
|
|
macro_rules! assert_ready {
|
|
($e:expr) => {{
|
|
match $e {
|
|
Ok(futures::Async::Ready(v)) => v,
|
|
Ok(_) => panic!("not ready"),
|
|
Err(e) => panic!("error = {:?}", e),
|
|
}
|
|
}}
|
|
}
|
|
|
|
macro_rules! assert_not_ready {
|
|
($e:expr) => {{
|
|
match $e {
|
|
Ok(futures::Async::NotReady) => {},
|
|
Ok(futures::Async::Ready(v)) => panic!("ready; value = {:?}", v),
|
|
Err(e) => panic!("error = {:?}", e),
|
|
}
|
|
}}
|
|
}
|
|
|
|
#[test]
|
|
fn available_permits() {
|
|
let s = Semaphore::new(100);
|
|
assert_eq!(s.available_permits(), 100);
|
|
|
|
// Polling for a permit succeeds immediately
|
|
let mut permit = Permit::new();
|
|
assert!(!permit.is_acquired());
|
|
|
|
assert_ready!(permit.poll_acquire(&s));
|
|
assert_eq!(s.available_permits(), 99);
|
|
assert!(permit.is_acquired());
|
|
|
|
// Polling again on the same waiter does not claim a new permit
|
|
assert_ready!(permit.poll_acquire(&s));
|
|
assert_eq!(s.available_permits(), 99);
|
|
assert!(permit.is_acquired());
|
|
}
|
|
|
|
#[test]
|
|
fn unavailable_permits() {
|
|
let s = Semaphore::new(1);
|
|
|
|
let mut permit_1 = Permit::new();
|
|
let mut permit_2 = Permit::new();
|
|
|
|
// Acquire the first permit
|
|
assert_ready!(permit_1.poll_acquire(&s));
|
|
assert_eq!(s.available_permits(), 0);
|
|
|
|
let mut task = MockTask::new();
|
|
|
|
task.enter(|| {
|
|
// Try to acquire the second permit
|
|
assert_not_ready!(permit_2.poll_acquire(&s));
|
|
});
|
|
|
|
permit_1.release(&s);
|
|
|
|
assert_eq!(s.available_permits(), 0);
|
|
assert!(task.is_notified());
|
|
assert_ready!(permit_2.poll_acquire(&s));
|
|
|
|
permit_2.release(&s);
|
|
assert_eq!(s.available_permits(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn zero_permits() {
|
|
let s = Semaphore::new(0);
|
|
assert_eq!(s.available_permits(), 0);
|
|
|
|
let mut permit = Permit::new();
|
|
let mut task = MockTask::new();
|
|
|
|
// Try to acquire the permit
|
|
task.enter(|| {
|
|
assert_not_ready!(permit.poll_acquire(&s));
|
|
});
|
|
|
|
s.add_permits(1);
|
|
|
|
assert!(task.is_notified());
|
|
assert_ready!(permit.poll_acquire(&s));
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn validates_max_permits() {
|
|
use std::usize;
|
|
Semaphore::new((usize::MAX >> 2) + 1);
|
|
}
|
|
|
|
#[test]
|
|
fn close_semaphore_prevents_acquire() {
|
|
let s = Semaphore::new(1);
|
|
s.close();
|
|
|
|
assert_eq!(1, s.available_permits());
|
|
|
|
let mut permit = Permit::new();
|
|
|
|
assert!(permit.poll_acquire(&s).is_err());
|
|
assert_eq!(1, s.available_permits());
|
|
}
|
|
|
|
#[test]
|
|
fn close_semaphore_notifies_permit1() {
|
|
let s = Semaphore::new(0);
|
|
|
|
let mut permit = Permit::new();
|
|
let mut task = MockTask::new();
|
|
|
|
task.enter(|| {
|
|
assert_not_ready!(permit.poll_acquire(&s));
|
|
});
|
|
|
|
s.close();
|
|
|
|
assert!(task.is_notified());
|
|
assert!(permit.poll_acquire(&s).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn close_semaphore_notifies_permit2() {
|
|
let s = Semaphore::new(2);
|
|
|
|
let mut permit1 = Permit::new();
|
|
let mut permit2 = Permit::new();
|
|
let mut permit3 = Permit::new();
|
|
let mut permit4 = Permit::new();
|
|
|
|
// Acquire a couple of permits
|
|
assert_ready!(permit1.poll_acquire(&s));
|
|
assert_ready!(permit2.poll_acquire(&s));
|
|
|
|
let mut task1 = MockTask::new();
|
|
let mut task2 = MockTask::new();
|
|
|
|
task1.enter(|| {
|
|
assert_not_ready!(permit3.poll_acquire(&s));
|
|
});
|
|
|
|
task2.enter(|| {
|
|
assert_not_ready!(permit4.poll_acquire(&s));
|
|
});
|
|
|
|
s.close();
|
|
|
|
assert!(task1.is_notified());
|
|
assert!(task2.is_notified());
|
|
|
|
assert!(permit3.poll_acquire(&s).is_err());
|
|
assert!(permit4.poll_acquire(&s).is_err());
|
|
|
|
assert_eq!(0, s.available_permits());
|
|
|
|
permit1.release(&s);
|
|
|
|
assert_eq!(1, s.available_permits());
|
|
|
|
assert!(permit1.poll_acquire(&s).is_err());
|
|
|
|
permit2.release(&s);
|
|
|
|
assert_eq!(2, s.available_permits());
|
|
}
|