use std::sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc, Condvar, Mutex,
};
#[derive(Default)]
pub(crate) struct WaitGroup {
pending: AtomicUsize,
poisoned: AtomicBool,
lock: Mutex<()>,
condvar: Condvar,
}
impl WaitGroup {
pub fn submit(&self) {
self.pending.fetch_add(1, Ordering::SeqCst);
}
pub fn complete(&self) {
let old = self.pending.fetch_sub(1, Ordering::SeqCst);
if old == 1 {
let _guard = self.lock.lock().unwrap();
self.condvar.notify_all();
}
}
pub fn poison(&self) {
self.poisoned.store(true, Ordering::SeqCst);
self.complete();
}
pub fn join(&self) {
let mut lock = self.lock.lock().unwrap();
while self.pending.load(Ordering::SeqCst) > 0 {
lock = self.condvar.wait(lock).unwrap();
}
if self.poisoned.load(Ordering::SeqCst) {
panic!("Worker Pool was poisoned")
}
}
}
pub(crate) struct Sentinel(pub(crate) Option<Arc<WaitGroup>>);
impl Sentinel {
pub fn cancel(mut self) {
if let Some(wait) = self.0.take() {
wait.complete();
}
}
}
impl Drop for Sentinel {
fn drop(&mut self) {
if let Some(ref wait) = self.0.take() {
wait.poison();
}
}
}