use std::sync::{Arc, Condvar, Mutex};
#[derive(Clone)]
pub struct WaitGroup {
inner: Arc<(Mutex<usize>, Condvar)>,
}
impl WaitGroup {
#[must_use]
pub fn new() -> Self {
Self {
inner: Arc::new((Mutex::new(0), Condvar::new())),
}
}
pub fn add(&self, n: usize) {
let (lock, _) = &*self.inner;
let mut count = lock.lock().unwrap_or_else(|p| p.into_inner());
*count += n;
}
pub fn done(&self) {
let (lock, cond) = &*self.inner;
let mut count = lock.lock().unwrap_or_else(|p| p.into_inner());
debug_assert!(*count > 0, "WaitGroup::done() called more times than add()");
*count -= 1;
if *count == 0 {
cond.notify_all();
}
}
pub fn wait(&self) {
let (lock, cond) = &*self.inner;
let mut count = lock.lock().unwrap_or_else(|p| p.into_inner());
while *count != 0 {
count = cond.wait(count).unwrap_or_else(|p| p.into_inner());
}
}
#[must_use]
pub fn wait_timeout(&self, timeout: std::time::Duration) -> bool {
let (lock, cond) = &*self.inner;
let deadline = std::time::Instant::now() + timeout;
let mut count = lock.lock().unwrap_or_else(|p| p.into_inner());
while *count != 0 {
let Some(remaining) = deadline.checked_duration_since(std::time::Instant::now()) else {
return false;
};
let (next, res) = cond
.wait_timeout(count, remaining)
.unwrap_or_else(|p| p.into_inner());
count = next;
if res.timed_out() && *count != 0 {
return false;
}
}
true
}
#[must_use]
pub fn guard(&self) -> WaitGroupGuard {
WaitGroupGuard(self.clone())
}
}
impl Default for WaitGroup {
fn default() -> Self {
Self::new()
}
}
pub struct WaitGroupGuard(WaitGroup);
impl Drop for WaitGroupGuard {
fn drop(&mut self) {
self.0.done();
}
}