use crate::core::scondvar::SCondVar;
use crate::core::smutex::SMutex;
use std::sync::atomic;
use std::sync::atomic::{AtomicUsize, Ordering};
struct BarrierInner {
ref_count: AtomicUsize,
waiters: AtomicUsize,
bucket: usize,
mutex: SMutex,
cond: SCondVar,
}
#[repr(transparent)]
pub struct Barrier {
ptr: *const BarrierInner,
}
unsafe impl Send for Barrier {}
unsafe impl Sync for Barrier {}
impl Barrier {
pub fn new() -> Barrier {
Self::init(1, 0)
}
pub fn with_capacity(n: usize, bucket: usize) -> Barrier {
Self::init(n + 2, if bucket == 0 { 0 } else { bucket + 2 })
}
fn init(n: usize, bucket: usize) -> Barrier {
let ptr = Box::into_raw(Box::new(BarrierInner {
ref_count: AtomicUsize::new(1),
waiters: AtomicUsize::new(n),
bucket,
mutex: SMutex::new(),
cond: SCondVar::new(),
}));
if ptr.is_null() {
panic!("Invalid allocation for Barrier");
}
Self { ptr }
}
#[inline(always)]
fn inner(&self) -> &BarrierInner {
unsafe { &*self.ptr }
}
pub fn count(&self) -> usize {
self.inner().waiters.load(Ordering::Acquire)
}
pub fn wait(&self) {
let inner = self.inner();
let waiters = inner.waiters.load(Ordering::Acquire);
if waiters == 0 || inner.ref_count.load(Ordering::Acquire) == 1 {
return;
}
let guard = inner.mutex.lock();
if waiters > 1 {
let new_val = inner.waiters.fetch_sub(1, Ordering::AcqRel) - 1;
if new_val == 2 {
inner.waiters.store(inner.bucket, Ordering::Release);
self.release();
} else {
let _ = inner.cond.wait(guard);
}
} else {
let _ = inner.cond.wait(guard);
}
}
#[inline(always)]
pub fn release(&self) {
self.inner().cond.notify_all();
}
}
impl Clone for Barrier {
fn clone(&self) -> Self {
self.inner().ref_count.fetch_add(1, Ordering::Release);
Barrier { ptr: self.ptr }
}
}
impl Drop for Barrier {
fn drop(&mut self) {
if self.inner().ref_count.fetch_sub(1, Ordering::Release) == 1 {
atomic::fence(Ordering::Acquire); let ptr = self.ptr as *mut BarrierInner;
unsafe { drop(Box::from_raw(ptr)) };
}
}
}
impl Default for Barrier {
fn default() -> Self {
Self::new()
}
}