Skip to main content

asyncband/semaphore/
mod.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18// Portions of this file originated from Tokio 1.42.0's Semaphore and permit APIs.
19// Copyright (c) Tokio Contributors
20// The Tokio-derived portions remain licensed under the MIT License.
21// Asyncband substantially changed the contract: it has no closed state or close errors, accepts
22// usize permit counts without reserved flag bits, and adds direct drain and exact-reduction
23// operations backed by Asyncband's own waiter queue.
24// Upstream source:
25// https://github.com/tokio-rs/tokio/blob/bb9d57017e100985f86d8ca41ac105ee9140423e/tokio/src/sync/semaphore.rs
26
27//! Limit concurrent access with a set of permits.
28//!
29//! [`Semaphore::acquire`] waits for the requested number of permits and returns a guard that puts
30//! them back when dropped. [`Semaphore::try_acquire`] performs the same operation without waiting,
31//! while [`Semaphore::release`] adds permits that were not represented by a guard.
32//!
33//! # Examples
34//!
35//! ```
36//! # #[tokio::main]
37//! # async fn main() {
38//! use asyncband::semaphore::Semaphore;
39//!
40//! let semaphore = Semaphore::new(3);
41//! let a_permit = semaphore.acquire(1).await;
42//! let two_permits = semaphore.acquire(2).await;
43//!
44//! assert_eq!(semaphore.available_permits(), 0);
45//!
46//! let permit_attempt = semaphore.try_acquire(1);
47//! assert!(permit_attempt.is_none());
48//!
49//! drop(a_permit);
50//! assert_eq!(semaphore.available_permits(), 1);
51//! # }
52//! ```
53
54use std::sync::Arc;
55
56use crate::internal::semaphore;
57
58#[cfg(test)]
59mod tests;
60
61/// An async counting semaphore for controlling access to a set of resources.
62///
63/// See the [module level documentation](self) for more.
64#[derive(Debug)]
65pub struct Semaphore {
66    s: semaphore::Semaphore,
67}
68
69impl Semaphore {
70    /// Creates a new semaphore with the given number of permits.
71    ///
72    /// # Examples
73    ///
74    /// ```
75    /// use asyncband::semaphore::Semaphore;
76    ///
77    /// let sem = Semaphore::new(5); // Creates a semaphore with 5 permits
78    /// ```
79    pub const fn new(permits: usize) -> Self {
80        Self {
81            s: semaphore::Semaphore::new(permits),
82        }
83    }
84
85    /// Returns the current number of permits available.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use asyncband::semaphore::Semaphore;
91    ///
92    /// let sem = Semaphore::new(2);
93    /// assert_eq!(sem.available_permits(), 2);
94    ///
95    /// let permit = sem.try_acquire(1).unwrap();
96    /// assert_eq!(sem.available_permits(), 1);
97    /// ```
98    pub fn available_permits(&self) -> usize {
99        self.s.available_permits()
100    }
101
102    /// Atomically drains up to `up_to` permits that are currently available.
103    ///
104    /// Returns the number of permits actually drained, which may be less than `up_to`.
105    /// This operation neither creates nor cancels a deficit against future releases and does not
106    /// affect permits that have already been acquired.
107    ///
108    /// # Examples
109    ///
110    /// ```
111    /// use asyncband::semaphore::Semaphore;
112    ///
113    /// let sem = Semaphore::new(4);
114    /// let held = sem.try_acquire(2).unwrap();
115    ///
116    /// assert_eq!(sem.drain_permits(1), 1);
117    /// assert_eq!(sem.available_permits(), 1);
118    /// assert_eq!(sem.drain_permits(3), 1);
119    /// assert_eq!(sem.available_permits(), 0);
120    ///
121    /// drop(held);
122    /// assert_eq!(sem.available_permits(), 2);
123    /// ```
124    #[must_use = "`drain_permits` may drain fewer permits than requested"]
125    pub fn drain_permits(&self, up_to: usize) -> usize {
126        self.s.drain_permits(up_to)
127    }
128
129    /// Reduces the semaphore's logical permit balance by exactly `n`.
130    ///
131    /// This method returns immediately. If fewer than `n` permits are currently available, future
132    /// releases repay the resulting deficit before ordinary queued acquisitions may proceed.
133    /// Permits that have already been acquired remain valid and are not revoked.
134    ///
135    /// # Examples
136    ///
137    /// ```
138    /// use asyncband::semaphore::Semaphore;
139    ///
140    /// let sem = Semaphore::new(1);
141    /// sem.reduce_permits(3); // Consumes 1 available permit and records a deficit of 2.
142    /// assert_eq!(sem.available_permits(), 0);
143    ///
144    /// sem.release(2); // Repays the deficit, so neither permit becomes available.
145    /// assert_eq!(sem.available_permits(), 0);
146    ///
147    /// sem.release(1); // With the deficit repaid, this permit becomes available.
148    /// assert_eq!(sem.available_permits(), 1);
149    /// ```
150    pub fn reduce_permits(&self, n: usize) {
151        self.s.reduce_permits(n);
152    }
153
154    /// Adds `n` new permits to the semaphore.
155    ///
156    /// # Panics
157    ///
158    /// Panics if adding the permits would overflow the total permit count, or if notifying a waiter
159    /// panics. Added permits remain available, and notification is still attempted for every other
160    /// eligible waiter before the panic resumes.
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use asyncband::semaphore::Semaphore;
166    ///
167    /// let sem = Semaphore::new(0);
168    /// sem.release(2); // Adds 2 permits
169    /// assert_eq!(sem.available_permits(), 2);
170    /// ```
171    pub fn release(&self, permits: usize) {
172        self.s.release(permits);
173    }
174
175    /// Attempts to acquire `n` permits from the semaphore without blocking.
176    ///
177    /// If the permits are successfully acquired, a [`SemaphorePermit`] is returned.
178    /// The permits will be automatically returned to the semaphore when the permit
179    /// is dropped, unless [`forget`] is called.
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// use asyncband::semaphore::Semaphore;
185    ///
186    /// let sem = Semaphore::new(2);
187    ///
188    /// // First acquisition succeeds
189    /// let permit1 = sem.try_acquire(1).unwrap();
190    /// assert_eq!(sem.available_permits(), 1);
191    ///
192    /// // Second acquisition succeeds
193    /// let permit2 = sem.try_acquire(1).unwrap();
194    /// assert_eq!(sem.available_permits(), 0);
195    ///
196    /// // Third acquisition fails
197    /// assert!(sem.try_acquire(1).is_none());
198    /// ```
199    ///
200    /// [`forget`]: SemaphorePermit::forget
201    pub fn try_acquire(&self, permits: usize) -> Option<SemaphorePermit<'_>> {
202        if self.s.try_acquire(permits) {
203            Some(SemaphorePermit { sem: self, permits })
204        } else {
205            None
206        }
207    }
208
209    /// Acquires `n` permits from the semaphore.
210    ///
211    /// If the permits are not immediately available, this method will wait until they become
212    /// available. Returns a [`SemaphorePermit`] that will release the permits when dropped.
213    ///
214    /// # Cancel safety
215    ///
216    /// Pending acquisitions complete in order. Cancelling this call loses its place among them.
217    ///
218    /// # Examples
219    ///
220    /// ```
221    /// # #[tokio::main]
222    /// # async fn main() {
223    /// use std::sync::Arc;
224    ///
225    /// use asyncband::semaphore::Semaphore;
226    ///
227    /// let sem = Arc::new(Semaphore::new(2));
228    /// let sem2 = sem.clone();
229    ///
230    /// let handle = tokio::spawn(async move {
231    ///     let permit = sem2.acquire(1).await;
232    ///     // Do some work with the permit.
233    ///     // Permit is automatically released when dropped.
234    /// });
235    ///
236    /// let permit = sem.acquire(1).await;
237    /// // Do some work with the permit
238    /// drop(permit); // Explicitly release the permit
239    ///
240    /// handle.await.unwrap();
241    /// # }
242    /// ```
243    pub async fn acquire(&self, permits: usize) -> SemaphorePermit<'_> {
244        self.s.acquire(permits).await;
245        SemaphorePermit { sem: self, permits }
246    }
247
248    /// Attempts to acquire `n` permits from the semaphore without blocking.
249    ///
250    /// The semaphore must be wrapped in an [`Arc`] to call this method.
251    ///
252    /// If the permits are successfully acquired, a [`OwnedSemaphorePermit`] is returned.
253    /// The permits will be automatically returned to the semaphore when the permit
254    /// is dropped, unless [`forget`] is called.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// use std::sync::Arc;
260    ///
261    /// use asyncband::semaphore::Semaphore;
262    ///
263    /// let sem = Arc::new(Semaphore::new(2));
264    ///
265    /// let p1 = sem.clone().try_acquire_owned(1).unwrap();
266    /// assert_eq!(sem.available_permits(), 1);
267    ///
268    /// let p2 = sem.clone().try_acquire_owned(1).unwrap();
269    /// assert_eq!(sem.available_permits(), 0);
270    ///
271    /// let p3 = sem.try_acquire_owned(1);
272    /// assert!(p3.is_none());
273    /// ```
274    ///
275    /// [`forget`]: OwnedSemaphorePermit::forget
276    pub fn try_acquire_owned(self: Arc<Self>, permits: usize) -> Option<OwnedSemaphorePermit> {
277        if self.s.try_acquire(permits) {
278            Some(OwnedSemaphorePermit { sem: self, permits })
279        } else {
280            None
281        }
282    }
283
284    /// Acquires `n` permits from the semaphore.
285    ///
286    /// The semaphore must be wrapped in an [`Arc`] to call this method.
287    ///
288    /// If the permits are not immediately available, this method will wait until they become
289    /// available. Returns a [`OwnedSemaphorePermit`] that will release the permits when dropped.
290    ///
291    /// # Cancel safety
292    ///
293    /// Pending acquisitions complete in order. Cancelling this call loses its place among them.
294    ///
295    /// # Examples
296    ///
297    /// ```
298    /// # #[tokio::main]
299    /// # async fn main() {
300    /// use std::sync::Arc;
301    ///
302    /// use asyncband::semaphore::Semaphore;
303    ///
304    /// let sem = Arc::new(Semaphore::new(3));
305    /// let mut join_handles = vec![];
306    ///
307    /// for _ in 0..5 {
308    ///     let permit = sem.clone().acquire_owned(1).await;
309    ///     join_handles.push(tokio::spawn(async move {
310    ///         // perform task...
311    ///         // explicitly own `permit` in the task
312    ///         drop(permit);
313    ///     }));
314    /// }
315    ///
316    /// for handle in join_handles {
317    ///     handle.await.unwrap();
318    /// }
319    /// # }
320    /// ```
321    pub async fn acquire_owned(self: Arc<Self>, permits: usize) -> OwnedSemaphorePermit {
322        self.s.acquire(permits).await;
323        OwnedSemaphorePermit { sem: self, permits }
324    }
325}
326
327/// A permit from the semaphore.
328///
329/// This type is created by the [`acquire`] and [`try_acquire`] methods on [`Semaphore`].
330/// When the permit is dropped, the permits will be returned to the semaphore unless
331/// [`forget`] is called.
332///
333/// [`acquire`]: Semaphore::acquire
334/// [`try_acquire`]: Semaphore::try_acquire
335/// [`forget`]: SemaphorePermit::forget
336#[must_use = "permits are released immediately when dropped"]
337#[derive(Debug)]
338pub struct SemaphorePermit<'a> {
339    sem: &'a Semaphore,
340    permits: usize,
341}
342
343impl SemaphorePermit<'_> {
344    /// Forgets the permit **without** releasing it back to the semaphore.
345    ///
346    /// This can be used to permanently reduce the number of permits available
347    /// from a semaphore.
348    ///
349    /// # Examples
350    ///
351    /// ```
352    /// use std::sync::Arc;
353    ///
354    /// use asyncband::semaphore::Semaphore;
355    ///
356    /// let sem = Arc::new(Semaphore::new(10));
357    /// {
358    ///     let permit = sem.try_acquire(5).unwrap();
359    ///     assert_eq!(sem.available_permits(), 5);
360    ///     permit.forget();
361    /// }
362    ///
363    /// // Since we forgot the permit, available permits won't go back to
364    /// // its initial value even after the permit is dropped
365    /// assert_eq!(sem.available_permits(), 5);
366    /// ```
367    pub fn forget(mut self) {
368        self.permits = 0;
369    }
370
371    /// Merge two [`SemaphorePermit`] instances together, consuming `other`
372    /// without releasing the permits it holds.
373    ///
374    /// Permits held by both `self` and `other` are released when `self` drops.
375    ///
376    /// # Panics
377    ///
378    /// This function panics if permits from different [`Semaphore`] instances are merged or if
379    /// their combined permit count exceeds `usize::MAX`.
380    ///
381    /// # Examples
382    ///
383    /// ```
384    /// use std::sync::Arc;
385    ///
386    /// use asyncband::semaphore::Semaphore;
387    ///
388    /// let sem = Arc::new(Semaphore::new(10));
389    /// let mut permit = sem.try_acquire(1).unwrap();
390    ///
391    /// for _ in 0..9 {
392    ///     let new_permit = sem.try_acquire(1).unwrap();
393    ///     // Merge individual permits into a single one.
394    ///     permit.merge(new_permit)
395    /// }
396    ///
397    /// assert_eq!(sem.available_permits(), 0);
398    ///
399    /// // Release all permits in a single batch.
400    /// drop(permit);
401    ///
402    /// assert_eq!(sem.available_permits(), 10);
403    /// ```
404    #[track_caller]
405    pub fn merge(&mut self, mut other: Self) {
406        assert!(
407            std::ptr::eq(self.sem, other.sem),
408            "merging permits from different semaphore instances"
409        );
410        self.permits = self
411            .permits
412            .checked_add(other.permits)
413            .expect("merged permit count would overflow usize::MAX");
414        other.permits = 0;
415    }
416
417    /// Splits `n` permits from `self` and returns a new [`SemaphorePermit`] instance that holds `n`
418    /// permits.
419    ///
420    /// If there are insufficient permits, and it is impossible to reduce by `n`, returns `None`.
421    ///
422    /// # Examples
423    ///
424    /// ```
425    /// use std::sync::Arc;
426    ///
427    /// use asyncband::semaphore::Semaphore;
428    ///
429    /// let sem = Arc::new(Semaphore::new(3));
430    ///
431    /// let mut p1 = sem.try_acquire(3).unwrap();
432    /// let p2 = p1.split(1).unwrap();
433    ///
434    /// assert_eq!(p1.permits(), 2);
435    /// assert_eq!(p2.permits(), 1);
436    /// ```
437    pub fn split(&mut self, n: usize) -> Option<Self> {
438        if n > self.permits {
439            return None;
440        }
441
442        self.permits -= n;
443
444        Some(Self {
445            sem: self.sem,
446            permits: n,
447        })
448    }
449
450    /// Returns the number of permits this permit holds.
451    ///
452    /// # Examples
453    ///
454    /// ```
455    /// use asyncband::semaphore::Semaphore;
456    ///
457    /// let sem = Semaphore::new(5);
458    /// let permit = sem.try_acquire(3).unwrap();
459    /// assert_eq!(permit.permits(), 3);
460    /// ```
461    pub fn permits(&self) -> usize {
462        self.permits
463    }
464}
465
466impl Drop for SemaphorePermit<'_> {
467    fn drop(&mut self) {
468        self.sem.release(self.permits);
469    }
470}
471
472/// An owned permit from the semaphore.
473///
474/// This type is created by the [`acquire_owned`] method.
475///
476/// [`acquire_owned`]: Semaphore::acquire_owned
477#[must_use = "permits are released immediately when dropped"]
478#[derive(Debug)]
479pub struct OwnedSemaphorePermit {
480    sem: Arc<Semaphore>,
481    permits: usize,
482}
483
484impl OwnedSemaphorePermit {
485    /// Forgets the permit **without** releasing it back to the semaphore.
486    ///
487    /// This can be used to permanently reduce the number of permits available
488    /// from a semaphore.
489    ///
490    /// # Examples
491    ///
492    /// ```
493    /// use std::sync::Arc;
494    ///
495    /// use asyncband::semaphore::Semaphore;
496    ///
497    /// let sem = Arc::new(Semaphore::new(10));
498    /// {
499    ///     let permit = sem.clone().try_acquire_owned(5).unwrap();
500    ///     assert_eq!(sem.available_permits(), 5);
501    ///     permit.forget();
502    /// }
503    ///
504    /// // Since we forgot the permit, available permits won't go back to
505    /// // its initial value even after the permit is dropped
506    /// assert_eq!(sem.available_permits(), 5);
507    /// ```
508    pub fn forget(mut self) {
509        self.permits = 0;
510    }
511
512    /// Merge two [`OwnedSemaphorePermit`] instances together, consuming `other`
513    /// without releasing the permits it holds.
514    ///
515    /// Permits held by both `self` and `other` are released when `self` drops.
516    ///
517    /// # Panics
518    ///
519    /// This function panics if permits from different [`Semaphore`] instances are merged or if
520    /// their combined permit count exceeds `usize::MAX`.
521    ///
522    /// # Examples
523    ///
524    /// ```
525    /// use std::sync::Arc;
526    ///
527    /// use asyncband::semaphore::Semaphore;
528    ///
529    /// let sem = Arc::new(Semaphore::new(10));
530    /// let mut permit = sem.clone().try_acquire_owned(1).unwrap();
531    ///
532    /// for _ in 0..9 {
533    ///     let new_permit = sem.clone().try_acquire_owned(1).unwrap();
534    ///     // Merge individual permits into a single one.
535    ///     permit.merge(new_permit)
536    /// }
537    ///
538    /// assert_eq!(sem.available_permits(), 0);
539    ///
540    /// // Release all permits in a single batch.
541    /// drop(permit);
542    ///
543    /// assert_eq!(sem.available_permits(), 10);
544    /// ```
545    #[track_caller]
546    pub fn merge(&mut self, mut other: Self) {
547        assert!(
548            Arc::ptr_eq(&self.sem, &other.sem),
549            "merging permits from different semaphore instances"
550        );
551        self.permits = self
552            .permits
553            .checked_add(other.permits)
554            .expect("merged permit count would overflow usize::MAX");
555        other.permits = 0;
556    }
557
558    /// Splits `n` permits from `self` and returns a new [`OwnedSemaphorePermit`] instance that
559    /// holds `n` permits.
560    ///
561    /// If there are insufficient permits, and it is impossible to reduce by `n`, returns `None`.
562    ///
563    /// # Note
564    ///
565    /// It will clone the owned `Arc<Semaphore>` to construct the new instance.
566    ///
567    /// # Examples
568    ///
569    /// ```
570    /// use std::sync::Arc;
571    ///
572    /// use asyncband::semaphore::Semaphore;
573    ///
574    /// let sem = Arc::new(Semaphore::new(3));
575    ///
576    /// let mut p1 = sem.try_acquire_owned(3).unwrap();
577    /// let p2 = p1.split(1).unwrap();
578    ///
579    /// assert_eq!(p1.permits(), 2);
580    /// assert_eq!(p2.permits(), 1);
581    /// ```
582    pub fn split(&mut self, n: usize) -> Option<Self> {
583        if n > self.permits {
584            return None;
585        }
586
587        self.permits -= n;
588
589        Some(Self {
590            sem: self.sem.clone(),
591            permits: n,
592        })
593    }
594
595    /// Returns the number of permits this permit holds.
596    ///
597    /// # Examples
598    ///
599    /// ```
600    /// use std::sync::Arc;
601    ///
602    /// use asyncband::semaphore::Semaphore;
603    ///
604    /// let sem = Arc::new(Semaphore::new(5));
605    /// let permit = sem.try_acquire_owned(3).unwrap();
606    /// assert_eq!(permit.permits(), 3);
607    /// ```
608    pub fn permits(&self) -> usize {
609        self.permits
610    }
611}
612
613impl Drop for OwnedSemaphorePermit {
614    fn drop(&mut self) {
615        self.sem.release(self.permits);
616    }
617}