#![deny(missing_docs)]
#![cfg_attr(feature = "nightly", feature(test))]
#[cfg(feature = "nightly")]
extern crate test;
extern crate parking_lot_core;
use std::sync::{atomic, Arc};
struct BarrierInner {
gsense: atomic::AtomicBool,
count: atomic::AtomicUsize,
max: usize,
}
pub struct Barrier {
inner: Arc<BarrierInner>,
lsense: bool,
used: bool,
}
pub struct BarrierWaitResult(bool);
impl Barrier {
pub fn new(n: usize) -> Self {
Barrier {
used: false,
lsense: true,
inner: Arc::new(BarrierInner {
gsense: atomic::AtomicBool::new(true),
count: atomic::AtomicUsize::new(n),
max: n,
}),
}
}
pub fn wait(&mut self) -> BarrierWaitResult {
self.used = true;
self.lsense = !self.lsense;
if self.inner.count.fetch_sub(1, atomic::Ordering::SeqCst) == 1 {
self.inner
.count
.store(self.inner.max, atomic::Ordering::SeqCst);
self.inner
.gsense
.store(self.lsense, atomic::Ordering::SeqCst);
BarrierWaitResult(true)
} else {
let mut wait = parking_lot_core::SpinWait::new();
while self.inner.gsense.load(atomic::Ordering::SeqCst) != self.lsense {
wait.spin();
}
BarrierWaitResult(false)
}
}
}
impl Clone for Barrier {
fn clone(&self) -> Self {
assert!(!self.used);
Barrier {
used: false,
lsense: self.lsense,
inner: self.inner.clone(),
}
}
}
impl BarrierWaitResult {
pub fn is_leader(&self) -> bool {
self.0
}
}
#[cfg(test)]
mod tests {
use super::Barrier;
use std::sync::mpsc::{channel, TryRecvError};
use std::thread;
#[cfg(feature = "nightly")]
use test::Bencher;
#[cfg(feature = "nightly")]
const BENCH_THREADS: usize = 4;
#[cfg(feature = "nightly")]
#[cfg_attr(feature = "nightly", bench)]
fn ours(b: &mut Bencher) {
let mut barrier = Barrier::new(BENCH_THREADS);
for _ in 0..(BENCH_THREADS - 1) {
let mut barrier = barrier.clone();
thread::spawn(move || loop {
barrier.wait();
});
}
b.iter(move || { barrier.wait(); })
}
#[cfg(feature = "nightly")]
#[cfg_attr(feature = "nightly", bench)]
fn std(b: &mut Bencher) {
use std::sync::{self, Arc};
let barrier = Arc::new(sync::Barrier::new(BENCH_THREADS));
for _ in 0..(BENCH_THREADS - 1) {
let barrier = barrier.clone();
thread::spawn(move || loop {
barrier.wait();
});
}
b.iter(move || { barrier.wait(); })
}
#[test]
fn test_barrier() {
const N: usize = 10;
let mut barrier = Barrier::new(N);
let (tx, rx) = channel();
for _ in 0..N - 1 {
let mut c = barrier.clone();
let tx = tx.clone();
thread::spawn(move || { tx.send(c.wait().is_leader()).unwrap(); });
}
assert!(match rx.try_recv() {
Err(TryRecvError::Empty) => true,
_ => false,
});
let mut leader_found = barrier.wait().is_leader();
for _ in 0..N - 1 {
if rx.recv().unwrap() {
assert!(!leader_found);
leader_found = true;
}
}
assert!(leader_found);
}
}