use crate::primitive::sync::atomic::{AtomicUsize, Ordering};
use crate::primitive::sync::{Arc, Condvar, Mutex};
use core::mem::ManuallyDrop;
use std::fmt;
pub struct WaitGroup {
inner: Arc<Inner>,
}
struct Inner {
cvar: Condvar,
lock: Mutex<()>,
count: AtomicUsize,
}
impl Default for WaitGroup {
fn default() -> Self {
Self {
inner: Arc::new(Inner {
cvar: Condvar::new(),
lock: Mutex::new(()),
count: AtomicUsize::new(1),
}),
}
}
}
impl WaitGroup {
pub fn new() -> Self {
Self::default()
}
pub fn wait(self) {
let inner = unsafe {
let slf = ManuallyDrop::new(self);
core::ptr::read(&slf.inner)
};
if inner.count.fetch_sub(1, Ordering::AcqRel) == 1 {
drop(inner.lock.lock().unwrap());
inner.cvar.notify_all();
return;
}
let mut guard = inner.lock.lock().unwrap();
while inner.count.load(Ordering::Acquire) != 0 {
guard = inner.cvar.wait(guard).unwrap();
}
}
}
impl Drop for WaitGroup {
fn drop(&mut self) {
if self.inner.count.fetch_sub(1, Ordering::Release) == 1 {
drop(self.inner.lock.lock().unwrap());
self.inner.cvar.notify_all();
}
}
}
impl Clone for WaitGroup {
fn clone(&self) -> Self {
self.inner.count.fetch_add(1, Ordering::Relaxed);
Self {
inner: self.inner.clone(),
}
}
}
impl fmt::Debug for WaitGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let count = self.inner.count.load(Ordering::Relaxed);
f.debug_struct("WaitGroup").field("count", &count).finish()
}
}