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}