simple_semaphore/
lib.rs

1use std::sync::{
2    atomic::{AtomicUsize, Ordering},
3    Arc, Condvar, Mutex,
4};
5
6/// A Semaphore maintains the number of permits it is still allowed to give.
7///
8/// * When `acquire()` is called and the semaphore still has permits to give, it will return a `Permit`. If there are no permits that can be given, it will wait for one permit to be given back from a thread so that it can return a new `Permit`.
9/// * When `try_acquire()` is called and the semaphore still has permits to give, it will return `Some(Permit)`. If there are no permits that can be given, it will return `None`.
10#[derive(Debug)]
11pub struct Semaphore {
12    permits: Arc<AtomicUsize>,
13    condvar: Condvar,
14    mutex: Mutex<()>,
15}
16
17impl Semaphore {
18    /// Returns a new `Arc<Semaphore>` with the limit of permits chosen by you.
19    pub fn new(permits: usize) -> Arc<Self> {
20        Arc::new(Semaphore {
21            permits: Arc::new(AtomicUsize::new(permits)),
22            condvar: Condvar::new(),
23            mutex: Mutex::new(()),
24        })
25    }
26
27    /// Returns a new `Arc<Semaphore>` with the limit of permits set to the number of CPU cores the computer has.
28    pub fn default() -> Arc<Self> {
29        Arc::new(Semaphore {
30            permits: Arc::new(AtomicUsize::new(num_cpus::get())),
31            condvar: Condvar::new(),
32            mutex: Mutex::new(()),
33        })
34    }
35
36    /// Returns the number of available permits
37    pub fn available_permits(self: &Arc<Self>) -> usize {
38        self.permits.load(Ordering::Relaxed)
39    }
40
41    /// Tries to get a `Permit`. If no more permits can be given, it will wait for one permit to be given back from a thread so that it can return a new `Permit`.
42    #[allow(unused_must_use)]
43    pub fn acquire(self: &Arc<Self>) -> Permit {
44        loop {
45            if self.permits.load(Ordering::Acquire) != 0 {
46                self.permits.fetch_sub(1, Ordering::AcqRel);
47                return Permit {
48                    semaphore: Arc::clone(self),
49                };
50            }
51            let guard = self.mutex.lock().unwrap();
52            self.condvar.wait(guard).unwrap();
53        }
54    }
55
56    /// Tries to get a `Option<Permit>`. If no more permits can be given, it will return `None`.
57    pub fn try_acquire(self: &Arc<Self>) -> Option<Permit> {
58        if self.permits.load(Ordering::Acquire) != 0 {
59            self.permits.fetch_sub(1, Ordering::Release);
60            return Some(Permit {
61                semaphore: Arc::clone(self),
62            });
63        }
64        None
65    }
66
67    /// Releases a permit. This is what `drop()` on `Permit` calls, ideally use `drop(permit)`.
68    pub fn release(&self) {
69        self.permits.fetch_add(1, Ordering::Release);
70        self.condvar.notify_one();
71    }
72}
73
74/// A permit that holds the Semaphore, so that `drop(permit)` can be called.
75#[derive(Debug)]
76pub struct Permit {
77    semaphore: Arc<Semaphore>,
78}
79
80impl Drop for Permit {
81    /// Releases the permit.
82    fn drop(&mut self) {
83        self.semaphore.release();
84    }
85}