pub struct Semaphore { /* private fields */ }Expand description
A Semaphore gets initialized with a certain number of permits.
Callers can take one permit from the semaphore using the
Semaphore::acquire operation, which will block if there are none
available, and wake when one becomes available.
Permits can be added back to the semaphore one at a time using the
Semaphore::release operation, or in batches using
Semaphore::release_multiple.
Semaphores are useful for restricting concurrent access to something,
particularly in cases where you don’t want to restrict it to exactly one
observer (like with a Mutex). Two common use cases are:
-
To ensure that no more than
Ntasks can make it into a critical section simultaneously, you’d create aSemaphorewithNpermits. Each task wouldacquirea permit to gain entry, and thenreleaseit at the end. In this case, a “permit object” might be fine, becauseacquireandreleaseare both being called in the same context. -
To represent data being generated or received (perhaps over a network), you’d create a
Semaphorewith 0 permits. Tasks interested in consuming resources would attempt toacquireand block; as data becomes available, the generating task wouldreleasepermits, potentially in batches, to unblock the consumers. In this case,releaseandacquireare happening in totally different contexts.
To support both these uses, the Semaphore API is different from a lot of
Rust concurrency APIs, and does not return a “permit object” that represents
a permit until dropped. If your use case is closer to the first example, and
you would like the convenience of a permit object managing calls to
release for you, have a look at ScopedSemaphore, a thin wrapper that
provides a Permit.
§Getting a semaphore
Like lilos’s Mutex type, Semaphore must be pinned to be useful. So
generally you’ll wind up writing something like this:
let scooters = pin!(Semaphore::new(5));
// Drop &mut:
let scooters = scooters.into_ref();
// Check out one scooter from the pool.
scooters.acquire().await;This crate includes a convenience macro, create_semaphore!, that
basically just wraps up the first two lines:
lilos_semaphore::create_semaphore!(scooters, 5);
// Check out one scooter from the pool.
scooters.acquire().await;§Fairness
This semaphore implementation is fair, which in this context means that permits are handed out in the order they’re requested. If the semaphore runs out of permits, tasks requesting permits are queued in order and will be issued permits in order as they are returned to the semaphore.
Implementations§
Source§impl Semaphore
impl Semaphore
Sourcepub async fn acquire(self: Pin<&Self>)
pub async fn acquire(self: Pin<&Self>)
Creates a future that will resolve when it can take a single permit from the semaphore. Until then, the future will remain pending (i.e. block).
§Cancellation
Cancel-safe but affects your position in line, to maintain fairness.
If you drop the returned future before it resolves…
- If it had not successfully acquired a permit, nothing happens.
- If it had, the permit is released.
Dropping the future and re-calling acquire bumps the caller to the
back of the priority list, to maintain fairness. Otherwise, the result
is indistinguishable.
Sourcepub fn try_acquire(&self) -> Result<(), NoPermits>
pub fn try_acquire(&self) -> Result<(), NoPermits>
Attempts to take a single permit from the semaphore, returning Ok if
one is available immediately, or Err if they are all taken.
Sourcepub fn permits_available(&self) -> usize
pub fn permits_available(&self) -> usize
Returns the number of permits available in the semaphore.
Note that this is a snapshot. If this returns 4, for instance, it
doesn’t mean you can successfully call acquire 4 times without
blocking, because another acquirer may be racing you.
Sourcepub fn release_multiple(self: Pin<&Self>, n: usize)
pub fn release_multiple(self: Pin<&Self>, n: usize)
Stuffs one permit back into the semaphore.
Use this if you have called core::mem::forget on a Permit, when
you want to restore that permit to the semaphore. Note that this is an
unusual use case and should only be done with good reason.
It is, however, safe, in the Rust sense.
It’s possible to use this operation to increase the total number of
permits available in the Semaphore. That’s an even weirder use case,
so be careful.
Sourcepub const fn new(permits: usize) -> Self
pub const fn new(permits: usize) -> Self
Returns an Semaphore initialized with permits permits.
The result needs to be pinned to be useful, so you’ll usually write:
let semaphore = pin!(Semaphore::new(permit_count));
let semaphore = semaphore.into_ref();See also the convenience macro create_semaphore!.