ndsd_playback/
semaphore.rs

1use std::sync::{
2    atomic::{AtomicUsize, Ordering},
3    Condvar, Mutex,
4};
5
6pub struct Semaphore {
7    permits: AtomicUsize,
8    // Used only to block/wake threads — never protects operational.
9    blocker: Condvar,
10    signal: Mutex<()>,
11}
12
13impl Semaphore {
14    pub const fn new(initial: usize) -> Self {
15        Self {
16            permits: AtomicUsize::new(initial),
17            blocker: Condvar::new(),
18            signal: Mutex::new(()),
19        }
20    }
21
22    pub fn acquire(&self) {
23        loop {
24            // Try fast path first
25            let current = self.permits.load(Ordering::Acquire);
26            if current > 0 {
27                if self.permits
28                    .compare_exchange(current, current - 1, Ordering::AcqRel, Ordering::Relaxed)
29                    .is_ok()
30                {
31                    return;
32                }
33            }
34
35            // Wait if no permits
36            let guard = self.signal.lock().unwrap();
37            // Spurious wakeups are fine; recheck condition in loop
38            let _unused = self.blocker.wait(guard).unwrap();
39        }
40    }
41
42    pub fn release(&self) {
43        self.permits.fetch_add(1, Ordering::Release);
44        // Wake one thread (if any) blocked in acquire
45        self.blocker.notify_one();
46    }
47}