Skip to main content

moirai_sync/sync/
wait_group.rs

1#![expect(
2    clippy::unwrap_used,
3    reason = "ratchet MOIRAI-UNWRAP-1: pre-existing debt"
4)]
5
6use std::fmt;
7use std::sync::{Condvar, Mutex};
8
9/// A wait group for synchronizing multiple threads (Go-inspired).
10/// This provides value beyond standard library primitives.
11pub struct WaitGroup {
12    state: Mutex<WaitGroupState>,
13    cond: Condvar,
14}
15
16struct WaitGroupState {
17    counter: u64,
18}
19
20impl fmt::Debug for WaitGroup {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        let state = self.state.lock().unwrap();
23        f.debug_struct("WaitGroup")
24            .field("counter", &state.counter)
25            .finish()
26    }
27}
28
29impl Default for WaitGroup {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35impl WaitGroup {
36    /// Create a new wait group.
37    pub fn new() -> Self {
38        Self {
39            state: Mutex::new(WaitGroupState { counter: 0 }),
40            cond: Condvar::new(),
41        }
42    }
43
44    /// Add to the wait group counter.
45    pub fn add(&self, delta: u64) {
46        if delta == 0 {
47            return;
48        }
49        let mut state = self.state.lock().unwrap();
50        state.counter = state
51            .counter
52            .checked_add(delta)
53            .expect("WaitGroup counter overflow");
54    }
55
56    /// Decrement the wait group counter.
57    pub fn done(&self) {
58        let mut state = self.state.lock().unwrap();
59        if state.counter == 0 {
60            panic!("WaitGroup counter decremented below zero");
61        }
62        state.counter -= 1;
63        if state.counter == 0 {
64            self.cond.notify_all();
65        }
66    }
67
68    /// Wait for the counter to reach zero.
69    pub fn wait(&self) {
70        let mut state = self.state.lock().unwrap();
71        while state.counter > 0 {
72            state = self.cond.wait(state).unwrap();
73        }
74    }
75}