1use std::sync::{Condvar, Mutex};
2
3pub struct Semaphore {
11 state: Mutex<usize>,
12 cond: Condvar,
13 max: usize,
14}
15
16pub struct SemaphorePermit<'a> {
17 sem: &'a Semaphore,
18}
19
20impl Drop for SemaphorePermit<'_> {
21 fn drop(&mut self) {
22 let mut count = self.sem.state.lock().unwrap();
23 *count -= 1;
24 self.sem.cond.notify_one();
25 }
26}
27
28impl Semaphore {
29 pub fn new(max: usize) -> Self {
30 Semaphore { state: Mutex::new(0), cond: Condvar::new(), max }
31 }
32
33 pub fn acquire(&self) -> SemaphorePermit<'_> {
36 let mut count = self.state.lock().unwrap();
37 while *count >= self.max {
38 count = self.cond.wait(count).unwrap();
39 }
40 *count += 1;
41 SemaphorePermit { sem: self }
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use std::sync::atomic::{AtomicUsize, Ordering};
49 use std::sync::Arc;
50 use std::thread;
51 use std::time::Duration;
52
53 #[test]
54 fn never_exceeds_max_concurrent_holders() {
55 let sem = Arc::new(Semaphore::new(2));
56 let current = Arc::new(AtomicUsize::new(0));
57 let max_observed = Arc::new(AtomicUsize::new(0));
58
59 let handles: Vec<_> = (0..8)
60 .map(|_| {
61 let sem = Arc::clone(&sem);
62 let current = Arc::clone(¤t);
63 let max_observed = Arc::clone(&max_observed);
64 thread::spawn(move || {
65 let _permit = sem.acquire();
66 let now = current.fetch_add(1, Ordering::SeqCst) + 1;
67 max_observed.fetch_max(now, Ordering::SeqCst);
68 thread::sleep(Duration::from_millis(20));
69 current.fetch_sub(1, Ordering::SeqCst);
70 })
71 })
72 .collect();
73 for h in handles {
74 h.join().unwrap();
75 }
76
77 assert!(max_observed.load(Ordering::SeqCst) <= 2);
78 }
79}