Semaphore

Struct Semaphore 

Source
pub struct Semaphore { /* private fields */ }
Expand description

A counting semaphore.

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 N tasks can make it into a critical section simultaneously, you’d create a Semaphore with N permits. Each task would acquire a permit to gain entry, and then release it at the end. In this case, a “permit object” might be fine, because acquire and release are both being called in the same context.

  • To represent data being generated or received (perhaps over a network), you’d create a Semaphore with 0 permits. Tasks interested in consuming resources would attempt to acquire and block; as data becomes available, the generating task would release permits, potentially in batches, to unblock the consumers. In this case, release and acquire are 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

Source

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.

Source

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.

Source

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.

Source

pub fn release(self: Pin<&Self>)

Stuffs one permit back into the semaphore.

Source

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.

Source

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!.

Trait Implementations§

Source§

impl Debug for Semaphore

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'pin> Unpin for Semaphore
where PinnedFieldsOf<__Semaphore<'pin>>: Unpin,

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<U, T> Captures<T> for U
where U: ?Sized,

Source§

impl<U, T> Captures<T> for U
where U: ?Sized,