Skip to main content

async_lock/
semaphore.rs

1use core::fmt;
2use core::marker::PhantomPinned;
3use core::mem;
4use core::pin::Pin;
5use core::task::{ready, Poll};
6
7use crate::sync::atomic::{AtomicUsize, Ordering};
8
9use alloc::sync::Arc;
10
11use event_listener::{Event, EventListener};
12use event_listener_strategy::{easy_wrapper, EventListenerFuture, Strategy};
13
14/// A counter for limiting the number of concurrent operations.
15#[derive(Debug)]
16pub struct Semaphore {
17    count: AtomicUsize,
18    event: Event,
19}
20
21impl Semaphore {
22    const_fn! {
23        const_if: #[cfg(not(loom))];
24        /// Creates a new semaphore with a limit of `n` concurrent operations.
25        ///
26        /// # Examples
27        ///
28        /// ```
29        /// use async_lock::Semaphore;
30        ///
31        /// let s = Semaphore::new(5);
32        /// ```
33        pub const fn new(n: usize) -> Semaphore {
34            Semaphore {
35                count: AtomicUsize::new(n),
36                event: Event::new(),
37            }
38        }
39    }
40
41    /// Attempts to get a permit for a concurrent operation.
42    ///
43    /// If the permit could not be acquired at this time, then [`None`] is returned. Otherwise, a
44    /// guard is returned that releases the mutex when dropped.
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use async_lock::Semaphore;
50    ///
51    /// let s = Semaphore::new(2);
52    ///
53    /// let g1 = s.try_acquire().unwrap();
54    /// let g2 = s.try_acquire().unwrap();
55    ///
56    /// assert!(s.try_acquire().is_none());
57    /// drop(g2);
58    /// assert!(s.try_acquire().is_some());
59    /// ```
60    pub fn try_acquire(&self) -> Option<SemaphoreGuard<'_>> {
61        let mut count = self.count.load(Ordering::Acquire);
62        loop {
63            if count == 0 {
64                return None;
65            }
66
67            match self.count.compare_exchange_weak(
68                count,
69                count - 1,
70                Ordering::AcqRel,
71                Ordering::Acquire,
72            ) {
73                Ok(_) => return Some(SemaphoreGuard(self)),
74                Err(c) => count = c,
75            }
76        }
77    }
78
79    /// Waits for a permit for a concurrent operation.
80    ///
81    /// Returns a guard that releases the permit when dropped.
82    ///
83    /// # Examples
84    ///
85    /// ```
86    /// # futures_lite::future::block_on(async {
87    /// use async_lock::Semaphore;
88    ///
89    /// let s = Semaphore::new(2);
90    /// let guard = s.acquire().await;
91    /// # });
92    /// ```
93    pub fn acquire(&self) -> Acquire<'_> {
94        Acquire::_new(AcquireInner {
95            semaphore: self,
96            listener: None,
97            _pin: PhantomPinned,
98        })
99    }
100
101    /// Waits for a permit for a concurrent operation.
102    ///
103    /// Returns a guard that releases the permit when dropped.
104    ///
105    /// # Blocking
106    ///
107    /// Rather than using asynchronous waiting, like the [`acquire`][Semaphore::acquire] method,
108    /// this method will block the current thread until the permit is acquired.
109    ///
110    /// This method should not be used in an asynchronous context. It is intended to be
111    /// used in a way that a semaphore can be used in both asynchronous and synchronous contexts.
112    /// Calling this method in an asynchronous context may result in a deadlock.
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// use async_lock::Semaphore;
118    ///
119    /// let s = Semaphore::new(2);
120    /// let guard = s.acquire_blocking();
121    /// ```
122    #[cfg(all(feature = "std", not(target_family = "wasm")))]
123    #[inline]
124    pub fn acquire_blocking(&self) -> SemaphoreGuard<'_> {
125        self.acquire().wait()
126    }
127
128    /// Attempts to get an owned permit for a concurrent operation.
129    ///
130    /// If the permit could not be acquired at this time, then [`None`] is returned. Otherwise, an
131    /// owned guard is returned that releases the mutex when dropped.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use async_lock::Semaphore;
137    /// use std::sync::Arc;
138    ///
139    /// let s = Arc::new(Semaphore::new(2));
140    ///
141    /// let g1 = s.try_acquire_arc().unwrap();
142    /// let g2 = s.try_acquire_arc().unwrap();
143    ///
144    /// assert!(s.try_acquire_arc().is_none());
145    /// drop(g2);
146    /// assert!(s.try_acquire_arc().is_some());
147    /// ```
148    pub fn try_acquire_arc(self: &Arc<Self>) -> Option<SemaphoreGuardArc> {
149        let mut count = self.count.load(Ordering::Acquire);
150        loop {
151            if count == 0 {
152                return None;
153            }
154
155            match self.count.compare_exchange_weak(
156                count,
157                count - 1,
158                Ordering::AcqRel,
159                Ordering::Acquire,
160            ) {
161                Ok(_) => return Some(SemaphoreGuardArc(Some(self.clone()))),
162                Err(c) => count = c,
163            }
164        }
165    }
166
167    /// Waits for an owned permit for a concurrent operation.
168    ///
169    /// Returns a guard that releases the permit when dropped.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// # futures_lite::future::block_on(async {
175    /// use async_lock::Semaphore;
176    /// use std::sync::Arc;
177    ///
178    /// let s = Arc::new(Semaphore::new(2));
179    /// let guard = s.acquire_arc().await;
180    /// # });
181    /// ```
182    pub fn acquire_arc(self: &Arc<Self>) -> AcquireArc {
183        AcquireArc::_new(AcquireArcInner {
184            semaphore: self.clone(),
185            listener: None,
186            _pin: PhantomPinned,
187        })
188    }
189
190    /// Waits for an owned permit for a concurrent operation.
191    ///
192    /// Returns a guard that releases the permit when dropped.
193    ///
194    /// # Blocking
195    ///
196    /// Rather than using asynchronous waiting, like the [`acquire_arc`][Semaphore::acquire_arc] method,
197    /// this method will block the current thread until the permit is acquired.
198    ///
199    /// This method should not be used in an asynchronous context. It is intended to be
200    /// used in a way that a semaphore can be used in both asynchronous and synchronous contexts.
201    /// Calling this method in an asynchronous context may result in a deadlock.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use std::sync::Arc;
207    /// use async_lock::Semaphore;
208    ///
209    /// let s = Arc::new(Semaphore::new(2));
210    /// let guard = s.acquire_arc_blocking();
211    /// ```
212    #[cfg(all(feature = "std", not(target_family = "wasm")))]
213    #[inline]
214    pub fn acquire_arc_blocking(self: &Arc<Self>) -> SemaphoreGuardArc {
215        self.acquire_arc().wait()
216    }
217
218    /// Adds `n` additional permits to the semaphore.
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// use async_lock::Semaphore;
224    ///
225    /// # futures_lite::future::block_on(async {
226    /// let s = Semaphore::new(1);
227    ///
228    /// let _guard = s.acquire().await;
229    /// assert!(s.try_acquire().is_none());
230    ///
231    /// s.add_permits(2);
232    ///
233    /// let _guard = s.acquire().await;
234    /// let _guard = s.acquire().await;
235    /// # });
236    /// ```
237    pub fn add_permits(&self, n: usize) {
238        self.count.fetch_add(n, Ordering::AcqRel);
239        self.event.notify(n);
240    }
241
242    /// Returns the current number of available permits.
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use async_lock::Semaphore;
248    ///
249    /// let s = Semaphore::new(2);
250    /// assert_eq!(s.available_permits(), 2);
251    ///
252    /// let gaurd = s.try_acquire().unwrap();
253    /// assert_eq!(s.available_permits(), 1);
254    ///
255    /// drop(gaurd);
256    /// ```
257    pub fn available_permits(&self) -> usize {
258        self.count.load(Ordering::Acquire)
259    }
260}
261
262easy_wrapper! {
263    /// The future returned by [`Semaphore::acquire`].
264    pub struct Acquire<'a>(AcquireInner<'a> => SemaphoreGuard<'a>);
265    #[cfg(all(feature = "std", not(target_family = "wasm")))]
266    pub(crate) wait();
267}
268
269pin_project_lite::pin_project! {
270    struct AcquireInner<'a> {
271        // The semaphore being acquired.
272        semaphore: &'a Semaphore,
273
274        // The listener waiting on the semaphore.
275        listener: Option<EventListener>,
276
277        // Keeping this future `!Unpin` enables future optimizations.
278        #[pin]
279        _pin: PhantomPinned
280    }
281}
282
283impl fmt::Debug for Acquire<'_> {
284    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285        f.write_str("Acquire { .. }")
286    }
287}
288
289impl<'a> EventListenerFuture for AcquireInner<'a> {
290    type Output = SemaphoreGuard<'a>;
291
292    fn poll_with_strategy<'x, S: Strategy<'x>>(
293        self: Pin<&mut Self>,
294        strategy: &mut S,
295        cx: &mut S::Context,
296    ) -> Poll<Self::Output> {
297        let this = self.project();
298
299        loop {
300            match this.semaphore.try_acquire() {
301                Some(guard) => return Poll::Ready(guard),
302                None => {
303                    // Wait on the listener.
304                    if this.listener.is_none() {
305                        *this.listener = Some(this.semaphore.event.listen());
306                    } else {
307                        ready!(strategy.poll(this.listener, cx));
308                    }
309                }
310            }
311        }
312    }
313}
314
315easy_wrapper! {
316    /// The future returned by [`Semaphore::acquire_arc`].
317    pub struct AcquireArc(AcquireArcInner => SemaphoreGuardArc);
318    #[cfg(all(feature = "std", not(target_family = "wasm")))]
319    pub(crate) wait();
320}
321
322pin_project_lite::pin_project! {
323    struct AcquireArcInner {
324        // The semaphore being acquired.
325        semaphore: Arc<Semaphore>,
326
327        // The listener waiting on the semaphore.
328        listener: Option<EventListener>,
329
330        // Keeping this future `!Unpin` enables future optimizations.
331        #[pin]
332        _pin: PhantomPinned
333    }
334}
335
336impl fmt::Debug for AcquireArc {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        f.write_str("AcquireArc { .. }")
339    }
340}
341
342impl EventListenerFuture for AcquireArcInner {
343    type Output = SemaphoreGuardArc;
344
345    fn poll_with_strategy<'x, S: Strategy<'x>>(
346        self: Pin<&mut Self>,
347        strategy: &mut S,
348        cx: &mut S::Context,
349    ) -> Poll<Self::Output> {
350        let this = self.project();
351
352        loop {
353            match this.semaphore.try_acquire_arc() {
354                Some(guard) => return Poll::Ready(guard),
355                None => {
356                    // Wait on the listener.
357                    if this.listener.is_none() {
358                        *this.listener = Some(this.semaphore.event.listen());
359                    } else {
360                        ready!(strategy.poll(this.listener, cx));
361                    }
362                }
363            }
364        }
365    }
366}
367
368/// A guard that releases the acquired permit.
369#[clippy::has_significant_drop]
370#[derive(Debug)]
371pub struct SemaphoreGuard<'a>(&'a Semaphore);
372
373impl SemaphoreGuard<'_> {
374    /// Drops the guard _without_ releasing the acquired permit.
375    #[inline]
376    pub fn forget(self) {
377        mem::forget(self);
378    }
379}
380
381impl Drop for SemaphoreGuard<'_> {
382    fn drop(&mut self) {
383        self.0.count.fetch_add(1, Ordering::AcqRel);
384        self.0.event.notify(1);
385    }
386}
387
388/// An owned guard that releases the acquired permit.
389#[clippy::has_significant_drop]
390#[derive(Debug)]
391pub struct SemaphoreGuardArc(Option<Arc<Semaphore>>);
392
393impl SemaphoreGuardArc {
394    /// Drops the guard _without_ releasing the acquired permit.
395    /// (Will still decrement the `Arc` reference count.)
396    #[inline]
397    pub fn forget(mut self) {
398        // Drop the inner `Arc` in order to decrement the reference count.
399        // FIXME: get rid of the `Option` once RFC 3466 or equivalent becomes available.
400        drop(self.0.take());
401        mem::forget(self);
402    }
403}
404
405impl Drop for SemaphoreGuardArc {
406    fn drop(&mut self) {
407        let opt = self.0.take().unwrap();
408        opt.count.fetch_add(1, Ordering::AcqRel);
409        opt.event.notify(1);
410    }
411}