#![cfg_attr(not(feature = "sync"), allow(unreachable_pub, dead_code))] //! # Implementation Details. //! //! The semaphore is implemented using an intrusive linked list of waiters. An //! atomic counter tracks the number of available permits. If the semaphore does //! not contain the required number of permits, the task attempting to acquire //! permits places its waker at the end of a queue. When new permits are made //! available (such as by releasing an initial acquisition), they are assigned //! to the task at the front of the queue, waking that task if its requested //! number of permits is met. //! //! Because waiters are enqueued at the back of the linked list and dequeued //! from the front, the semaphore is fair. Tasks trying to acquire large numbers //! of permits at a time will always be woken eventually, even if many other //! tasks are acquiring smaller numbers of permits. This means that in a //! use-case like tokio's read-write lock, writers will not be starved by //! readers. usecrate::loom::cell::UnsafeCell; usecrate::loom::sync::atomic::AtomicUsize; usecrate::loom::sync::{Mutex, MutexGuard}; usecrate::util::linked_list::{self, LinkedList}; #[cfg(all(tokio_unstable, feature = "tracing"))] usecrate::util::trace; usecrate::util::WakeList;
use std::future::Future; use std::marker::PhantomPinned; use std::pin::Pin; use std::ptr::NonNull; use std::sync::atomic::Ordering::*; use std::task::{Context, Poll, Waker}; use std::{cmp, fmt};
/// An asynchronous counting semaphore which permits waiting on multiple permits at once. pub(crate) struct Semaphore {
waiters: Mutex<Waitlist>, /// The current number of available permits in the semaphore.
permits: AtomicUsize, #[cfg(all(tokio_unstable, feature = "tracing"))]
resource_span: tracing::Span,
}
/// Error returned from the [`Semaphore::try_acquire`] function. /// /// [`Semaphore::try_acquire`]: crate::sync::Semaphore::try_acquire #[derive(Debug, PartialEq, Eq)] pubenum TryAcquireError { /// The semaphore has been [closed] and cannot issue new permits. /// /// [closed]: crate::sync::Semaphore::close
Closed,
/// The semaphore has no available permits.
NoPermits,
} /// Error returned from the [`Semaphore::acquire`] function. /// /// An `acquire` operation can only fail if the semaphore has been /// [closed]. /// /// [closed]: crate::sync::Semaphore::close /// [`Semaphore::acquire`]: crate::sync::Semaphore::acquire #[derive(Debug)] pubstruct AcquireError(());
/// An entry in the wait queue. struct Waiter { /// The current state of the waiter. /// /// This is either the number of remaining permits required by /// the waiter, or a flag indicating that the waiter is not yet queued.
state: AtomicUsize,
/// The waker to notify the task awaiting permits. /// /// # Safety /// /// This may only be accessed while the wait queue is locked.
waker: UnsafeCell<Option<Waker>>,
/// Intrusive linked-list pointers. /// /// # Safety /// /// This may only be accessed while the wait queue is locked. /// /// TODO: Ideally, we would be able to use loom to enforce that /// this isn't accessed concurrently. However, it is difficult to /// use a `UnsafeCell` here, since the `Link` trait requires _returning_ /// references to `Pointers`, and `UnsafeCell` requires that checked access /// take place inside a closure. We should consider changing `Pointers` to /// use `UnsafeCell` internally.
pointers: linked_list::Pointers<Waiter>,
impl Semaphore { /// The maximum number of permits which a semaphore can hold. /// /// Note that this reserves three bits of flags in the permit counter, but /// we only actually use one of them. However, the previous semaphore /// implementation used three bits, so we will continue to reserve them to /// avoid a breaking change if additional flags need to be added in the /// future. pub(crate) const MAX_PERMITS: usize = usize::MAX >> 3; const CLOSED: usize = 1; // The least-significant bit in the number of permits is reserved to use // as a flag indicating that the semaphore has been closed. Consequently // PERMIT_SHIFT is used to leave that bit for that purpose. const PERMIT_SHIFT: usize = 1;
/// Creates a new semaphore with the initial number of permits /// /// Maximum number of permits on 32-bit platforms is `1<<29`. pub(crate) fn new(permits: usize) -> Self {
assert!(
permits <= Self::MAX_PERMITS, "a semaphore may not have more than MAX_PERMITS permits ({})", Self::MAX_PERMITS
);
/// Creates a new semaphore with the initial number of permits. /// /// Maximum number of permits on 32-bit platforms is `1<<29`. #[cfg(not(all(loom, test)))] pub(crate) constfn const_new(permits: usize) -> Self {
assert!(permits <= Self::MAX_PERMITS);
/// Returns the current number of available permits. pub(crate) fn available_permits(&self) -> usize { self.permits.load(Acquire) >> Self::PERMIT_SHIFT
}
/// Adds `added` new permits to the semaphore. /// /// The maximum number of permits is `usize::MAX >> 3`, and this function will panic if the limit is exceeded. pub(crate) fn release(&self, added: usize) { if added == 0 { return;
}
// Assign permits to the wait queue self.add_permits_locked(added, self.waiters.lock());
}
/// Closes the semaphore. This prevents the semaphore from issuing new /// permits and notifies all pending waiters. pub(crate) fn close(&self) { letmut waiters = self.waiters.lock(); // If the semaphore's permits counter has enough permits for an // unqueued waiter to acquire all the permits it needs immediately, // it won't touch the wait list. Therefore, we have to set a bit on // the permit counter as well. However, we must do this while // holding the lock --- otherwise, if we set the bit and then wait // to acquire the lock we'll enter an inconsistent state where the // permit counter is closed, but the wait list is not. self.permits.fetch_or(Self::CLOSED, Release);
waiters.closed = true; whilelet Some(mut waiter) = waiters.queue.pop_back() { let waker = unsafe { waiter.as_mut().waker.with_mut(|waker| (*waker).take()) }; iflet Some(waker) = waker {
waker.wake();
}
}
}
/// Returns true if the semaphore is closed. pub(crate) fn is_closed(&self) -> bool { self.permits.load(Acquire) & Self::CLOSED == Self::CLOSED
}
pub(crate) fn try_acquire(&self, num_permits: usize) -> Result<(), TryAcquireError> {
assert!(
num_permits <= Self::MAX_PERMITS, "a semaphore may not have more than MAX_PERMITS permits ({})", Self::MAX_PERMITS
); let num_permits = num_permits << Self::PERMIT_SHIFT; letmut curr = self.permits.load(Acquire); loop { // Has the semaphore closed? if curr & Self::CLOSED == Self::CLOSED { return Err(TryAcquireError::Closed);
}
// Are there enough permits remaining? if curr < num_permits { return Err(TryAcquireError::NoPermits);
}
let next = curr - num_permits;
matchself.permits.compare_exchange(curr, next, AcqRel, Acquire) {
Ok(_) => { // TODO: Instrument once issue has been solved return Ok(());
}
Err(actual) => curr = actual,
}
}
}
/// Release `rem` permits to the semaphore's wait list, starting from the /// end of the queue. /// /// If `rem` exceeds the number of permits needed by the wait list, the /// remainder are assigned back to the semaphore. fn add_permits_locked(&self, mut rem: usize, waiters: MutexGuard<'_, Waitlist>) { letmut wakers = WakeList::new(); letmut lock = Some(waiters); letmut is_empty = false; while rem > 0 { letmut waiters = lock.take().unwrap_or_else(|| self.waiters.lock()); 'inner: while wakers.can_push() { // Was the waiter assigned enough permits to wake it? match waiters.queue.last() {
Some(waiter) => { if !waiter.assign_permits(&mut rem) { break'inner;
}
}
None => {
is_empty = true; // If we assigned permits to all the waiters in the queue, and there are // still permits left over, assign them back to the semaphore. break'inner;
}
}; letmut waiter = waiters.queue.pop_back().unwrap(); iflet Some(waker) = unsafe { waiter.as_mut().waker.with_mut(|waker| (*waker).take()) }
{
wakers.push(waker);
}
}
if rem > 0 && is_empty { let permits = rem;
assert!(
permits <= Self::MAX_PERMITS, "cannot add more than MAX_PERMITS permits ({})", Self::MAX_PERMITS
); let prev = self.permits.fetch_add(rem << Self::PERMIT_SHIFT, Release); let prev = prev >> Self::PERMIT_SHIFT;
assert!(
prev + permits <= Self::MAX_PERMITS, "number of added permits ({}) would overflow MAX_PERMITS ({})",
rem, Self::MAX_PERMITS
);
/// Decrease a semaphore's permits by a maximum of `n`. /// /// If there are insufficient permits and it's not possible to reduce by `n`, /// return the number of permits that were actually reduced. pub(crate) fn forget_permits(&self, n: usize) -> usize { if n == 0 { return0;
}
letmut curr_bits = self.permits.load(Acquire); loop { let curr = curr_bits >> Self::PERMIT_SHIFT; let new = curr.saturating_sub(n); matchself.permits.compare_exchange_weak(
curr_bits,
new << Self::PERMIT_SHIFT,
AcqRel,
Acquire,
) {
Ok(_) => return std::cmp::min(curr, n),
Err(actual) => curr_bits = actual,
};
}
}
let needed = if queued {
node.state.load(Acquire) << Self::PERMIT_SHIFT
} else {
num_permits << Self::PERMIT_SHIFT
};
letmut lock = None; // First, try to take the requested number of permits from the // semaphore. letmut curr = self.permits.load(Acquire); letmut waiters = loop { // Has the semaphore closed? if curr & Self::CLOSED > 0 { return Poll::Ready(Err(AcquireError::closed()));
}
letmut remaining = 0; let total = curr
.checked_add(acquired)
.expect("number of permits must not overflow"); let (next, acq) = if total >= needed { let next = curr - (needed - acquired);
(next, needed >> Self::PERMIT_SHIFT)
} else {
remaining = (needed - acquired) - curr;
(0, curr >> Self::PERMIT_SHIFT)
};
if remaining > 0 && lock.is_none() { // No permits were immediately available, so this permit will // (probably) need to wait. We'll need to acquire a lock on the // wait queue before continuing. We need to do this _before_ the // CAS that sets the new value of the semaphore's `permits` // counter. Otherwise, if we subtract the permits and then // acquire the lock, we might miss additional permits being // added while waiting for the lock.
lock = Some(self.waiters.lock());
}
if node.assign_permits(&mut acquired) { self.add_permits_locked(acquired, waiters); return Poll::Ready(Ok(()));
}
assert_eq!(acquired, 0); letmut old_waker = None;
// Otherwise, register the waker & enqueue the node.
node.waker.with_mut(|waker| { // Safety: the wait list is locked, so we may modify the waker. let waker = unsafe { &mut *waker }; // Do we need to register the new waker? if waker
.as_ref()
.map_or(true, |waker| !waker.will_wake(cx.waker()))
{
old_waker = std::mem::replace(waker, Some(cx.waker().clone()));
}
});
// If the waiter is not already in the wait queue, enqueue it. if !queued { let node = unsafe { let node = Pin::into_inner_unchecked(node) as *mut _;
NonNull::new_unchecked(node)
};
#[cfg(all(tokio_unstable, feature = "tracing"))] let _resource_span = self.node.ctx.resource_span.clone().entered(); #[cfg(all(tokio_unstable, feature = "tracing"))] let _async_op_span = self.node.ctx.async_op_span.clone().entered(); #[cfg(all(tokio_unstable, feature = "tracing"))] let _async_op_poll_span = self.node.ctx.async_op_poll_span.clone().entered();
let (node, semaphore, needed, queued) = self.project();
// First, ensure the current task has enough budget to proceed. #[cfg(all(tokio_unstable, feature = "tracing"))] let coop = ready!(trace_poll_op!( "poll_acquire", crate::runtime::coop::poll_proceed(cx),
));
#[cfg(not(all(tokio_unstable, feature = "tracing")))] let coop = ready!(crate::runtime::coop::poll_proceed(cx));
let result = match semaphore.poll_acquire(cx, needed, node, *queued) {
Poll::Pending => {
*queued = true;
Poll::Pending
}
Poll::Ready(r) => {
coop.made_progress();
r?;
*queued = false;
Poll::Ready(Ok(()))
}
};
let this = self.get_unchecked_mut();
(
Pin::new_unchecked(&mut this.node),
this.semaphore,
this.num_permits,
&mut this.queued,
)
}
}
}
impl Drop for Acquire<'_> { fn drop(&mutself) { // If the future is completed, there is no node in the wait list, so we // can skip acquiring the lock. if !self.queued { return;
}
// This is where we ensure safety. The future is being dropped, // which means we must ensure that the waiter entry is no longer stored // in the linked list. letmut waiters = self.semaphore.waiters.lock();
// remove the entry from the list let node = NonNull::from(&mutself.node); // Safety: we have locked the wait list. unsafe { waiters.queue.remove(node) };
let acquired_permits = self.num_permits - self.node.state.load(Acquire); if acquired_permits > 0 { self.semaphore.add_permits_locked(acquired_permits, waiters);
}
}
}
// Safety: the `Acquire` future is not `Sync` automatically because it contains // a `Waiter`, which, in turn, contains an `UnsafeCell`. However, the // `UnsafeCell` is only accessed when the future is borrowed mutably (either in // `poll` or in `drop`). Therefore, it is safe (although not particularly // _useful_) for the future to be borrowed immutably across threads. unsafeimpl Sync for Acquire<'_> {}
impl TryAcquireError { /// Returns `true` if the error was caused by a closed semaphore. #[allow(dead_code)] // may be used later! pub(crate) fn is_closed(&self) -> bool {
matches!(self, TryAcquireError::Closed)
}
/// Returns `true` if the error was caused by calling `try_acquire` on a /// semaphore with no available permits. #[allow(dead_code)] // may be used later! pub(crate) fn is_no_permits(&self) -> bool {
matches!(self, TryAcquireError::NoPermits)
}
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.22Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-23)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.