Skip to main content

shuttle_engine/future/
batch_semaphore.rs

1//! A counting semaphore supporting both async and sync operations.
2use crate::current;
3use crate::runtime::execution::ExecutionState;
4use crate::runtime::task::{clock::VectorClock, TaskId};
5use crate::runtime::thread;
6use crate::sync_types::{ResourceSignature, ResourceType};
7use std::cell::RefCell;
8use std::collections::VecDeque;
9use std::fmt;
10use std::future::Future;
11use std::pin::Pin;
12use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
13use std::sync::Arc;
14use std::sync::Mutex;
15use std::task::{Context, Poll, Waker};
16use tracing::trace;
17
18struct Waiter {
19    /// The task waiting on this waiter's `Acquire`.
20    ///
21    /// Refreshed on every poll (like `waker`) rather than frozen at creation
22    /// time. An `Acquire` future is not necessarily owned by the task that
23    /// created it: it can be cached inside a longer-lived object and later
24    /// polled by a different task (tokio's `poll_recv(&mut self, cx)` is the
25    /// motivating example — the in-flight acquire lives in the `Receiver`, and
26    /// a `Receiver` may be moved between tasks). The semaphore must unblock
27    /// whoever is actually waiting now, so this follows the poller. This
28    /// mirrors tokio's own `batch_semaphore`, which refreshes its waiter's
29    /// `Waker` under a `will_wake` check.
30    ///
31    /// Stored as an atomic rather than a `Cell` to keep `Waiter` (and hence
32    /// `Acquire`) `Sync`.
33    task_id: AtomicUsize,
34    num_permits: usize,
35    is_queued: AtomicBool,
36    has_permits: AtomicBool,
37    /// Clock of the task that created this waiter. Note this is *not* refreshed
38    /// when `task_id` is: it is only used to seed the causality of the acquired
39    /// permits, and keeping the original enqueue clock is conservative (it can
40    /// only add happens-before edges, never remove them).
41    clock: VectorClock,
42    waker: Mutex<Option<Waker>>,
43}
44
45// Implement debug in order to not output the `VectorClock`
46impl fmt::Debug for Waiter {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.debug_struct("Waiter")
49            .field("task_id", &self.task_id())
50            .field("num_permits", &self.num_permits)
51            .field("is_queued", &self.is_queued)
52            .field("has_permits", &self.has_permits)
53            .field("waker", &self.waker)
54            .finish()
55    }
56}
57
58impl Waiter {
59    fn new(num_permits: usize) -> Self {
60        Self {
61            task_id: AtomicUsize::new(ExecutionState::me().into()),
62            num_permits,
63            is_queued: AtomicBool::new(false),
64            has_permits: AtomicBool::new(false),
65            clock: current::clock(),
66            waker: Mutex::new(None),
67        }
68    }
69
70    /// The task currently waiting on this waiter. See [`Waiter::task_id`].
71    fn task_id(&self) -> TaskId {
72        TaskId::from(self.task_id.load(Ordering::SeqCst))
73    }
74
75    /// Point this waiter at the task that is polling it now, so that a later
76    /// `release` unblocks the current poller rather than whoever polled first.
77    fn set_task_id(&self, task_id: TaskId) {
78        self.task_id.store(task_id.into(), Ordering::SeqCst);
79    }
80}
81
82/// Number of permits (`num_available`) available to be acquired. The permits
83/// are grouped into batches in the `permit_clocks` deque, such that batches
84/// farther back correspond to later `release` calls. Each batch is a tuple
85/// of the permits remaining in that batch and the clock of the event whence
86/// the permits originate.
87struct PermitsAvailable {
88    // Invariant: the number of permits available is equal to the sum of the
89    // batch sizes in the queue.
90    num_available: usize,
91
92    /// Batches of permits with associated clocks (corresponding to the
93    /// `release` events that created them). This is an `Option` because the
94    /// deque is lazily initialized; see `const_new`.
95    permit_clocks: Option<VecDeque<(usize, VectorClock)>>,
96
97    /// The clock of the last successful acquire event. Used for causal
98    /// dependence in `try_acquire` failures.
99    last_acquire: VectorClock,
100}
101
102// Implement debug in order to not output the `VectorClock`s
103impl fmt::Debug for PermitsAvailable {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.debug_struct("PermitsAvailable")
106            .field("num_available", &self.num_available)
107            .finish()
108    }
109}
110
111impl PermitsAvailable {
112    fn new(num_permits: usize) -> Self {
113        let mut permit_clocks = VecDeque::new();
114        if num_permits > 0 {
115            permit_clocks.push_back((num_permits, current::clock()));
116        }
117        Self {
118            num_available: num_permits,
119            permit_clocks: Some(permit_clocks),
120            last_acquire: VectorClock::new(),
121        }
122    }
123
124    const fn const_new(num_permits: usize) -> Self {
125        // A `VecDeque` cannot be populated in a const fn, due to allocation.
126        // Instead, we set `permit_clocks` to `None`, and initialize it lazily
127        // when it is needed for the first time, to contain one batch of size
128        // `num_permits`.
129        Self {
130            num_available: num_permits,
131            permit_clocks: None,
132            last_acquire: VectorClock::new(),
133        }
134    }
135
136    fn available(&self) -> usize {
137        self.num_available
138    }
139
140    fn init_permit_clocks(&mut self) {
141        if self.permit_clocks.is_none() {
142            let mut permit_clocks = VecDeque::new();
143            if self.num_available > 0 {
144                permit_clocks.push_back((self.num_available, VectorClock::new()));
145            }
146            self.permit_clocks = Some(permit_clocks);
147        }
148    }
149
150    fn acquire(&mut self, mut num_permits: usize, acquire_clock: VectorClock) -> Result<VectorClock, TryAcquireError> {
151        // Acquiring zero permits is always possible, and is not causally
152        // dependent on any event.
153        if num_permits == 0 {
154            return Ok(VectorClock::new());
155        }
156
157        if num_permits <= self.num_available {
158            self.init_permit_clocks();
159            self.last_acquire.update(&acquire_clock);
160            self.num_available -= num_permits;
161
162            // Acquire `num_permits` from the available batches. This may
163            // consume one or more batches from the queue. The resulting clock
164            // is the join of all the batches used (fully or partially), since
165            // the acquiry causally depends on the releases that created those
166            // batches.
167            let mut clock = VectorClock::new();
168            let permit_clocks = self.permit_clocks.as_mut().unwrap();
169            while let Some((batch_size, batch_clock)) = permit_clocks.front_mut() {
170                clock.update(batch_clock);
171
172                if num_permits < *batch_size {
173                    // The current batch is larger than the number of permits
174                    // requested: diminish batch, finish loop.
175                    *batch_size -= num_permits;
176                    num_permits = 0;
177                } else {
178                    // The current batch is fully consumed by the request.
179                    // Remove it from the queue.
180                    num_permits -= *batch_size;
181                    permit_clocks.pop_front();
182                }
183
184                // Break early to avoid causally depending on the next batch.
185                if num_permits == 0 {
186                    break;
187                }
188            }
189
190            assert_eq!(num_permits, 0);
191            Ok(clock)
192        } else {
193            // There are not enough permits to fulfill the request.
194            Err(TryAcquireError::NoPermits)
195        }
196    }
197
198    fn release(&mut self, num_permits: usize, clock: VectorClock) {
199        self.init_permit_clocks();
200        self.num_available += num_permits;
201        self.permit_clocks.as_mut().unwrap().push_back((num_permits, clock));
202    }
203}
204
205/// Fairness mode for the semaphore. Determines which threads are woken when
206/// permits are released.
207#[derive(Clone, Copy, Debug, PartialEq, Eq)]
208pub enum Fairness {
209    /// The semaphore is strictly fair, so earlier requesters always get
210    /// priority over later ones.
211    StrictlyFair,
212
213    /// The semaphore makes no guarantees about fairness. In particular,
214    /// a waiter can be starved by other threads.
215    Unfair,
216}
217
218/// A counting semaphore which permits waiting on multiple permits at once,
219/// and supports both asychronous and synchronous blocking operations.
220#[derive(Debug)]
221struct BatchSemaphoreState {
222    id: Option<crate::annotations::ObjectId>,
223
224    // Key invariants:
225    //
226    // (1) if `waiters` is nonempty and the head waiter is `H`,
227    // then `H.num_permits > permits_available.available()`.  (In other words,
228    // we are never in a state where there are enough permits available for the
229    // first waiter.  This invariant is ensured by the `drop` handler below.)
230    //
231    // (2) W is in waiters iff W.is_queued
232    //
233    // (3) W.is_queued ==> !W.has_permits
234    // Note: the converse is not true.  We can have !W.has_permits && !W.is_queued
235    // when the Acquire is created but not yet polled.
236    //
237    // (4) closed ==> waiters.is_empty()
238    waiters: VecDeque<Arc<Waiter>>,
239    permits_available: PermitsAvailable,
240    // TODO: should there be a clock for the close event?
241    closed: bool,
242}
243
244impl BatchSemaphoreState {
245    fn acquire_permits(&mut self, num_permits: usize, fairness: Fairness) -> Result<(), TryAcquireError> {
246        assert!(num_permits > 0);
247        if self.closed {
248            Err(TryAcquireError::Closed)
249        } else if self.waiters.is_empty() || matches!(fairness, Fairness::Unfair) {
250            // Permits here can be acquired in one of two scenarios:
251            // - The waiter queue is empty; nobody else is waiting for permits,
252            //   so if there are enough available, immediately succeed.
253            // - The semaphore is operating in an unfair mode; the current
254            //   thread is either requesting permits for the first time, or it
255            //   was woken and selected by the scheduler. In either case, the
256            //   thread may succeed, as long as there are enough permits.
257
258            let clock = self.permits_available.acquire(num_permits, current::clock())?;
259
260            // If successful, the acquiry is causally dependent on the event
261            // which released the acquired permits.
262            ExecutionState::with(|s| {
263                s.update_clock(&clock);
264            });
265
266            Ok(())
267        } else {
268            Err(TryAcquireError::NoPermits)
269        }
270    }
271
272    fn unblock_waiters_from_front(&mut self) {
273        while let Some(front) = self.waiters.front() {
274            // A waiter whose task has already finished is stale: its `Acquire`
275            // future was cancelled (e.g. a `select!` branch lost, or a
276            // `poll_recv`-style API cached the `Acquire` inside a longer-lived
277            // object) and the registering task then exited. There is nobody to
278            // unblock, so discard the waiter without consuming permits; if the
279            // `Acquire` is still alive and some other task polls it, it will
280            // re-acquire from the (still available) permits.
281            //
282            // `remove_waiter` can reach this during execution cleanup, when the
283            // task list is gone, so probe defensively and treat "can't tell" as
284            // not stale (i.e. preserve the old behaviour).
285            let front_is_stale = ExecutionState::try_with(|s| {
286                !s.in_cleanup() && s.try_get(front.task_id()).is_some_and(|t| t.finished())
287            })
288            .unwrap_or(false);
289            if front_is_stale {
290                let waiter = self.waiters.pop_front().unwrap();
291                waiter.is_queued.store(false, Ordering::SeqCst);
292                // Preserve the "queued <=> waker registered" invariant asserted
293                // in `Acquire::poll`; waking a finished task's waker is a no-op.
294                waiter.waker.lock().unwrap().take();
295                trace!("dropping stale waiter {:?} for finished task", waiter);
296                continue;
297            }
298            if front.num_permits <= self.permits_available.available() {
299                let waiter = self.waiters.pop_front().unwrap();
300
301                crate::annotations::record_semaphore_acquire_unblocked(
302                    self.id.unwrap(),
303                    waiter.task_id(),
304                    waiter.num_permits,
305                );
306
307                // The clock we pass into the semaphore is the clock of the
308                // waiter, corresponding to the point at which the waiter was
309                // enqueued. The clock we get in return corresponds to the
310                // join of the clocks of the acquired permits, used to update
311                // the waiter's clock to causally depend on the release events.
312                let clock = self
313                    .permits_available
314                    .acquire(waiter.num_permits, waiter.clock.clone())
315                    .unwrap();
316                trace!("granted {:?} permits to waiter {:?}", waiter.num_permits, waiter);
317
318                // Update waiter state as it is no longer in the queue
319                assert!(waiter.is_queued.swap(false, Ordering::SeqCst));
320                assert!(!waiter.has_permits.swap(true, Ordering::SeqCst));
321                ExecutionState::with(|s| {
322                    let task = s.get_mut(waiter.task_id());
323                    assert!(!task.finished());
324                    // The acquiry is causally dependent on the event
325                    // which released the acquired permits.
326                    task.clock.update(&clock);
327                    task.unblock();
328                });
329                let mut maybe_waker = waiter.waker.lock().unwrap();
330                if let Some(waker) = maybe_waker.take() {
331                    waker.wake();
332                }
333            } else {
334                return;
335            }
336        }
337    }
338}
339
340/// Counting semaphore
341#[derive(Debug)]
342pub struct BatchSemaphore {
343    state: RefCell<BatchSemaphoreState>,
344    fairness: Fairness,
345    #[allow(unused)]
346    signature: ResourceSignature,
347}
348
349/// Error returned from the [`BatchSemaphore::try_acquire`] function.
350#[derive(Debug, PartialEq, Eq)]
351pub enum TryAcquireError {
352    /// The semaphore has been closed and cannot issue new permits.
353    Closed,
354
355    /// The semaphore has no available permits.
356    NoPermits,
357}
358
359/// Error returned from the [`BatchSemaphore::acquire`] function.
360///
361/// An `acquire*` operation can only fail if the semaphore has been
362/// closed.
363#[derive(Debug)]
364pub struct AcquireError(());
365
366impl AcquireError {
367    fn closed() -> AcquireError {
368        AcquireError(())
369    }
370}
371
372impl fmt::Display for AcquireError {
373    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
374        write!(fmt, "semaphore closed")
375    }
376}
377
378impl std::error::Error for AcquireError {}
379
380impl BatchSemaphore {
381    /// Creates a new semaphore with the initial number of permits.
382    #[track_caller]
383    pub fn new(num_permits: usize, fairness: Fairness) -> Self {
384        Self::new_with_signature(
385            num_permits,
386            fairness,
387            ExecutionState::new_resource_signature(ResourceType::BatchSemaphore),
388        )
389    }
390
391    pub fn new_with_signature(num_permits: usize, fairness: Fairness, signature: ResourceSignature) -> Self {
392        let state = RefCell::new(BatchSemaphoreState {
393            id: Some(crate::annotations::record_semaphore_created()),
394            waiters: VecDeque::new(),
395            permits_available: PermitsAvailable::new(num_permits),
396            closed: false,
397        });
398        Self {
399            state,
400            fairness,
401            signature,
402        }
403    }
404
405    /// Creates a new semaphore with the initial number of permits.
406    #[track_caller]
407    pub const fn const_new(num_permits: usize, fairness: Fairness) -> Self {
408        Self::const_new_with_signature(
409            num_permits,
410            fairness,
411            ResourceSignature::new_const(ResourceType::BatchSemaphore),
412        )
413    }
414
415    pub const fn const_new_with_signature(
416        num_permits: usize,
417        fairness: Fairness,
418        signature: ResourceSignature,
419    ) -> Self {
420        let state = RefCell::new(BatchSemaphoreState {
421            id: None,
422            waiters: VecDeque::new(),
423            permits_available: PermitsAvailable::const_new(num_permits),
424            closed: false,
425        });
426        Self {
427            state,
428            fairness,
429            signature,
430        }
431    }
432
433    /// Returns the current number of available permits.
434    pub fn available_permits(&self) -> usize {
435        let state = self.state.borrow();
436        state.permits_available.available()
437    }
438
439    fn init_object_id(&self) {
440        let mut state = self.state.borrow_mut();
441        if state.id.is_none() {
442            state.id = Some(crate::annotations::record_semaphore_created());
443        }
444    }
445
446    /// Closes the semaphore. This prevents the semaphore from issuing new
447    /// permits and notifies all pending waiters.
448    pub fn close(&self) {
449        thread::switch();
450        self.close_no_scheduling_point();
451    }
452
453    /// Closes the semaphore without invoking `thread::switch`
454    pub fn close_no_scheduling_point(&self) {
455        self.init_object_id();
456        let mut state = self.state.borrow_mut();
457        if state.closed {
458            return;
459        }
460        crate::annotations::record_semaphore_closed(state.id.unwrap());
461        state.closed = true;
462
463        // Wake up all the waiters.  Since we've marked the state as closed, they
464        // will all return `AcquireError::closed` from their acquire calls.
465        let ptr = &*state as *const BatchSemaphoreState;
466        for waiter in state.waiters.drain(..) {
467            trace!(
468                "semaphore {:p} removing and waking up waiter {:?} on close",
469                ptr,
470                waiter,
471            );
472            assert!(waiter.is_queued.swap(false, Ordering::SeqCst));
473            assert!(!waiter.has_permits.load(Ordering::SeqCst)); // sanity check
474            ExecutionState::with(|exec_state| {
475                // A waiter whose task has finished is stale (its `Acquire` was
476                // cancelled and the task exited); there is nothing to unblock.
477                if !exec_state.in_cleanup() && !exec_state.get(waiter.task_id()).finished() {
478                    exec_state.get_mut(waiter.task_id()).unblock();
479                }
480            });
481            let mut maybe_waker = waiter.waker.lock().unwrap();
482            if let Some(waker) = maybe_waker.take() {
483                waker.wake();
484            }
485        }
486    }
487
488    /// Returns true iff the semaphore is closed.
489    pub fn is_closed(&self) -> bool {
490        let state = self.state.borrow();
491        state.closed
492    }
493
494    /// Try to acquire the specified number of permits from the Semaphore.
495    /// If the permits are available, returns Ok(())
496    /// If the semaphore is closed, returns `Err(TryAcquireError::Closed)`
497    /// If there aren't enough permits, returns `Err(TryAcquireError::NoPermits)`
498    pub fn try_acquire(&self, num_permits: usize) -> Result<(), TryAcquireError> {
499        thread::switch();
500
501        self.init_object_id();
502        let mut state = self.state.borrow_mut();
503        let id = state.id.unwrap();
504        let res = state.acquire_permits(num_permits, self.fairness).inspect_err(|_err| {
505            // Conservatively, the requester causally depends on the
506            // last successful acquire.
507            // TODO: This is not precise, but `try_acquire` causal dependency
508            // TODO: is both hard to define, and is most likely not worth the
509            // TODO: effort. The cases where causality would be tracked
510            // TODO: "imprecisely" do not correspond to commonly used sync.
511            // TODO: primitives, such as mutexes, mutexes, or condvars.
512            // TODO: An example would be a counting semaphore used to guard
513            // TODO: access to N homogenous resources (as opposed to FIFO,
514            // TODO: heterogenous resources).
515            // TODO: More precision could be gained by tracking clocks for all
516            // TODO: current permit holders, with a data structure similar to
517            // TODO: `permits_available`.
518            ExecutionState::with(|s| {
519                s.update_clock(&state.permits_available.last_acquire);
520            });
521        });
522        drop(state);
523
524        // If we won the race for permits of an unfair semaphore, re-block
525        // other waiting threads that can no longer succeed.
526        if res.is_ok() {
527            self.reblock_if_unfair();
528        }
529
530        crate::annotations::record_semaphore_try_acquire(id, num_permits, res.is_ok());
531
532        res
533    }
534
535    /// Clean-up method used when a thread succeeds in acquiring permits. If
536    /// the semaphore is unfair, a preceding `release` may have unblocked a
537    /// number of threads, some of which may no longer be able to succeed with
538    /// the permits remaining in the semaphore.
539    fn reblock_if_unfair(&self) {
540        if self.fairness == Fairness::Unfair {
541            let state = self.state.borrow_mut();
542            ExecutionState::with(|s| {
543                for waiter in &state.waiters {
544                    let available = state.permits_available.available();
545                    // Skip stale waiters: the task that registered the waiter
546                    // has finished, so there is nothing to block.
547                    if available < waiter.num_permits && s.try_get(waiter.task_id()).is_some_and(|t| !t.finished()) {
548                        // Block this waiter: it cannot succeed (there are not
549                        // enough permits available); its `poll` would return
550                        // without resolving.
551                        s.get_mut(waiter.task_id()).block(false);
552                    }
553                }
554            });
555        }
556    }
557
558    fn enqueue_waiter(&self, waiter: &Arc<Waiter>) {
559        let mut state = self.state.borrow_mut();
560
561        trace!("enqueuing waiter {:?} for semaphore {:p}", waiter, &self.state);
562        state.waiters.push_back(waiter.clone());
563
564        assert!(!waiter.has_permits.load(Ordering::SeqCst));
565        assert!(!waiter.is_queued.swap(true, Ordering::SeqCst));
566    }
567
568    fn remove_waiter(&self, waiter: &Arc<Waiter>) {
569        let mut state = self.state.borrow_mut();
570
571        trace!(waiters = ?state.waiters, "removing waiter {:?} from semaphore {:p}", waiter, &self.state);
572
573        // sanity checks
574        assert!(!state.closed);
575        assert!(!waiter.has_permits.load(Ordering::SeqCst));
576
577        let index = state
578            .waiters
579            .iter()
580            .position(|x| Arc::ptr_eq(x, waiter))
581            .expect("did not find waiter");
582
583        state.waiters.remove(index).unwrap();
584        assert!(waiter.is_queued.swap(false, Ordering::SeqCst));
585
586        match self.fairness {
587            Fairness::StrictlyFair => {
588                if index == 0 {
589                    // If the semaphore is strictly fair, and we removed the first waiter, check if its
590                    // removal unblocks remaining waiters.  This can happen in the following situation:
591                    // - the semahore has 1 permit available
592                    // - there are 2 waiters W1 and W2 where W1 wants 2 permits, and W2 wants 1 permit
593                    // - if W1 gives up and drops out, we want to ensure W2 is granted the semaphore
594                    state.unblock_waiters_from_front();
595                }
596            }
597            Fairness::Unfair => {}
598        }
599    }
600
601    /// Acquire the specified number of permits (async API)
602    pub fn acquire(&self, num_permits: usize) -> Acquire<'_> {
603        // No switch here; switch should be triggered on polling future
604        self.init_object_id();
605        Acquire::new(self, num_permits)
606    }
607
608    /// Acquire the specified number of permits (blocking API)
609    pub fn acquire_blocking(&self, num_permits: usize) -> Result<(), AcquireError> {
610        crate::future::block_on(self.acquire(num_permits))
611    }
612
613    /// Release `num_permits` back to the Semaphore
614    pub fn release(&self, num_permits: usize) {
615        thread::switch();
616
617        self.init_object_id();
618        if num_permits == 0 {
619            return;
620        }
621
622        let mut state = self.state.borrow_mut();
623
624        crate::annotations::record_semaphore_release(state.id.unwrap(), num_permits);
625
626        if ExecutionState::should_stop() {
627            // In case we are panicking, we release permits, but also clear
628            // the waiters queue: we should not unblock the threads at this
629            // point. However, the permits are released such that future
630            // acquires may succeed, as long as the requesters were not
631            // blocking on the semaphore at the time of the panic. This is
632            // used to correctly model lock poisoning.
633            state.permits_available.release(num_permits, VectorClock::new());
634            for waiter in &state.waiters {
635                waiter.is_queued.swap(false, Ordering::SeqCst);
636            }
637            state.waiters.clear();
638            state.closed = true;
639            return;
640        }
641
642        // Permits released into the semaphore reflect the releasing thread's
643        // clock; future acquires of those permits are causally dependent on
644        // this event.
645        ExecutionState::with(|s| {
646            let clock = s.increment_clock();
647            state.permits_available.release(num_permits, clock.clone());
648        });
649
650        let me = ExecutionState::me();
651        trace!(task = ?me, avail = ?state.permits_available, waiters = ?state.waiters, "released {} permits for semaphore {:p}", num_permits, &self.state);
652
653        match self.fairness {
654            Fairness::StrictlyFair => {
655                // in a strictly fair mode we will grant permits to waiters from the front
656                // of the queue, as long as there are enough permits available
657                state.unblock_waiters_from_front();
658            }
659            Fairness::Unfair => {
660                // in an unfair mode, we will unblock all the waiters for which
661                // there are enough permits available, then let them race
662                let num_available = state.permits_available.available();
663                for waiter in &mut state.waiters {
664                    if waiter.num_permits <= num_available {
665                        // A waiter whose task has already finished is stale: its
666                        // `Acquire` was cancelled (and possibly cached in a
667                        // longer-lived object) and the task then exited. Unlike
668                        // the strictly fair case there is nothing to clean up —
669                        // an unfair waiter holds no permits, so it blocks
670                        // nobody — but there is also nobody to unblock.
671                        let stale = ExecutionState::with(|s| {
672                            let task = s.get_mut(waiter.task_id());
673                            if task.finished() {
674                                true
675                            } else {
676                                task.unblock();
677                                false
678                            }
679                        });
680                        if stale {
681                            continue;
682                        }
683                        let maybe_waker = waiter.waker.lock().unwrap();
684                        if let Some(waker) = maybe_waker.as_ref() {
685                            waker.wake_by_ref();
686                        }
687                    }
688                }
689            }
690        }
691        drop(state);
692    }
693
694    /// Atomically `upgrade`s from holding `permits_currently_held` to holding `permits_to_be_held`.
695    /// The motivating use case for this is `parking_lot`s `RwLockUpgradableReadGuard::ugrade`, where we want to be able to
696    /// go from having a read guard to a write guard while honoring the order of `acquire`s.
697    ///
698    /// This is implemented by first trying to `acquire` `permits_to_be_held` (which for the `RwLock::upgrade` case would never
699    /// succeed, as the task is holding one permit, and wants to acquire all of them, meaning even with no other tasks it will
700    /// block on itself), then `release`ing `permits_currently_held`.
701    ///
702    /// This ensures the order of `acquire`s is honored, and prevents the potential deadlock situation which could occur in the
703    /// naive implementation where `permits_to_be_held - permits_currently_held` is `acquire`d, and two tasks try to `upgrade`
704    /// concurrently (or one `upgrade` in the presence of a `write`).
705    pub fn upgrade(&self, permits_currently_held: usize, permits_to_be_held: usize) -> Acquire<'_> {
706        assert!(permits_currently_held > 0);
707        assert!(permits_to_be_held > permits_currently_held);
708
709        let mut acquire = Box::pin(self.acquire(permits_to_be_held));
710        let waker = ExecutionState::with(|state| state.current_mut().waker());
711        let cx = &mut Context::from_waker(&waker);
712        let _poll = acquire.as_mut().poll(cx);
713
714        self.release(permits_currently_held);
715
716        *Pin::into_inner(acquire)
717    }
718}
719
720// Safety: Semaphore is never actually passed across true threads, only across continuations. The
721// RefCell<_> type therefore can't be preempted mid-bookkeeping-operation.
722// TODO we shouldn't need to do this, but RefCell is not Send, and anything we put within a Semaphore
723// TODO needs to be Send.
724unsafe impl Send for BatchSemaphore {}
725unsafe impl Sync for BatchSemaphore {}
726
727impl Default for BatchSemaphore {
728    #[track_caller]
729    fn default() -> Self {
730        Self::new(Default::default(), Fairness::StrictlyFair)
731    }
732}
733
734/// The future that results from async calls to `acquire*`.
735/// Callers must `await` on this future to obtain the necessary permits.
736#[derive(Debug)]
737pub struct Acquire<'a> {
738    waiter: Arc<Waiter>,
739    semaphore: &'a BatchSemaphore,
740    completed: bool, // Has the future completed yet?
741    never_polled: bool,
742}
743
744impl<'a> Acquire<'a> {
745    fn new(semaphore: &'a BatchSemaphore, num_permits: usize) -> Self {
746        let waiter = Arc::new(Waiter::new(num_permits));
747        Self {
748            waiter,
749            semaphore,
750            completed: false,
751            never_polled: true,
752        }
753    }
754}
755
756impl Future for Acquire<'_> {
757    type Output = Result<(), AcquireError>;
758
759    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
760        assert!(!self.completed);
761
762        let will_succeed = self.waiter.has_permits.load(Ordering::SeqCst)
763            || self.semaphore.is_closed()
764            || self.semaphore.available_permits() >= self.waiter.num_permits;
765
766        // If the acquire will succeed on the first try, we need to context switch once to allow the previous
767        // event to become visible. If we won't succeed, then we still need to context switch if the act of
768        // blocking does not commute with other operations on `batch_semaphore` (double-yield optimization,
769        // reasoning below).
770        //
771        // Fair Semaphores: blocking adds the current task to an *ordered* waiter queue. Two blocking acquires
772        // *do not commute* because in one ordering the queue will be [T1 T2] and in the other ordering [T2 T1].
773        // Thus we cannot apply the double-yield optimization for fair semaphores.
774        //
775        // Unfair Semaphores: blocking adds the current task to an *unordered set* of waiters. To check if the
776        // double-yield is valid we check if each operation (Z) on the semaphore commutes with a blocking acquire (Y1):
777        //
778        //     - Blocking Acquire: in both orderings `Z Y1` and `Y1 Z`, the waiter set has the same members, thus
779        //       the operations commute.
780        //     - Try Acquire: the try-acquire will fail in both orderings without changing the state of the semaphore
781        //     - Release: if the release unblocks Y1, then the optimization is not applicable. Otherwise, it must
782        //       unblock another task in the waiter set. As waiter-set insertion and removal for disjoint elements
783        //       commutes, release operations also commute in this case.
784        //
785        // Thus we apply the double-yield optimization for *unfair* semaphores only
786        let blocking_is_not_commutative = self.semaphore.fairness == Fairness::StrictlyFair;
787
788        if self.never_polled && (will_succeed || blocking_is_not_commutative) {
789            thread::switch();
790        }
791        self.never_polled = false;
792
793        if self.waiter.has_permits.load(Ordering::SeqCst) {
794            assert!(!self.waiter.is_queued.load(Ordering::SeqCst));
795            self.completed = true;
796            trace!("Acquire::poll for waiter {:?} with permits", self.waiter);
797            Poll::Ready(Ok(()))
798        } else if self.semaphore.is_closed() {
799            assert!(!self.waiter.is_queued.load(Ordering::SeqCst));
800            self.completed = true;
801            trace!("Acquire::poll for waiter {:?} with closed", self.waiter);
802            Poll::Ready(Err(AcquireError::closed()))
803        } else {
804            let is_queued = self.waiter.is_queued.load(Ordering::SeqCst);
805            trace!("Acquire::poll for waiter {:?}; is queued: {is_queued:?}", self.waiter);
806
807            // Sanity check: there should be a waker if the waiter is in
808            // the queue. Also true for unfair semaphores, which wake by ref.
809            assert_eq!(is_queued, self.waiter.waker.lock().unwrap().is_some());
810
811            // Should the waiter try to acquire permits here? Four cases:
812            // 1. unfair semaphore, waiter not yet enqueued;
813            // 2. fair semaphore, waiter not yet enqueued;
814            // 3. unfair semaphore, waiter already enqueued.
815            // 4. fair semaphore, waiter already enqueued;
816            //
817            // 1. and 2. are similar: the future was polled for the first time,
818            // so the waiter will try to acquire some permits. If successful,
819            // the waiter need not be enqueued, and the future is resolved.
820            // Otherwise, the waiter is added to the queue.
821            //
822            // 3. is slightly different: the future was polled, even though the
823            // waiter was already in the queue. This can happen either because
824            // the semaphore just received some permits and woke the waiter up,
825            // or because the future itself was polled manually. Either way,
826            // the semaphore is queried.
827            //
828            // 4. is a case where we do not try to acquire permits. The request
829            // would always fail, and the waiter should remain suspended until
830            // the semaphore has explicitly unblocked it and given it permits
831            // during a `release` call.
832            let try_to_acquire = match (self.semaphore.fairness, is_queued) {
833                // written this way to mirror the cases described above
834                (Fairness::Unfair, false) | (Fairness::StrictlyFair, false) | (Fairness::Unfair, true) => true,
835                (Fairness::StrictlyFair, true) => false,
836            };
837
838            if try_to_acquire {
839                // Access the semaphore state directly instead of `try_acquire`,
840                // because in case of `NoPermits`, we do not want to update the
841                // clock, as this thread will be blocked below.
842                let mut state = self.semaphore.state.borrow_mut();
843                let id = state.id.unwrap();
844                let acquire_result = state.acquire_permits(self.waiter.num_permits, self.semaphore.fairness);
845                drop(state);
846
847                match acquire_result {
848                    Ok(()) => {
849                        if is_queued {
850                            crate::annotations::record_semaphore_acquire_unblocked(
851                                id,
852                                self.waiter.task_id(),
853                                self.waiter.num_permits,
854                            );
855                            self.semaphore.remove_waiter(&self.waiter);
856                        } else {
857                            crate::annotations::record_semaphore_acquire_fast(id, self.waiter.num_permits);
858                        }
859                        self.waiter.has_permits.store(true, Ordering::SeqCst);
860                        self.completed = true;
861                        trace!("Acquire::poll for waiter {:?} that got permits", self.waiter);
862
863                        // If the semaphore is unfair, re-block other waiting
864                        // threads that can no longer succeed.
865                        self.semaphore.reblock_if_unfair();
866
867                        Poll::Ready(Ok(()))
868                    }
869                    Err(TryAcquireError::NoPermits) => {
870                        let mut maybe_waker = self.waiter.waker.lock().unwrap();
871                        *maybe_waker = Some(cx.waker().clone());
872                        // Point the waiter at whoever is polling now: this future
873                        // may have been created by a different task.
874                        self.waiter.set_task_id(ExecutionState::me());
875                        if !is_queued {
876                            crate::annotations::record_semaphore_acquire_blocked(id, self.waiter.num_permits);
877                            self.semaphore.enqueue_waiter(&self.waiter);
878                            self.waiter.is_queued.store(true, Ordering::SeqCst);
879                        }
880                        trace!("Acquire::poll for waiter {:?} that is enqueued", self.waiter);
881                        Poll::Pending
882                    }
883                    Err(TryAcquireError::Closed) => unreachable!(),
884                }
885            } else {
886                // No progress made, future is still pending. The waiter stays in
887                // the queue, but re-point it at the current poller and refresh
888                // its waker: this future may have been created by (or last
889                // polled by) another task, and `release` must wake whoever is
890                // waiting now. Without this, a permit granted to this waiter
891                // would unblock a task that is no longer interested, and the
892                // actual poller would never be woken.
893                *self.waiter.waker.lock().unwrap() = Some(cx.waker().clone());
894                self.waiter.set_task_id(ExecutionState::me());
895                Poll::Pending
896            }
897        }
898    }
899}
900
901impl Drop for Acquire<'_> {
902    fn drop(&mut self) {
903        trace!("Acquire::drop for Acquire {:p} with waiter {:?}", self, self.waiter);
904        if self.waiter.is_queued.load(Ordering::SeqCst) {
905            // If the associated waiter is in the wait list, remove it
906            self.semaphore.remove_waiter(&self.waiter);
907        } else if self.waiter.has_permits.load(Ordering::SeqCst) && !self.completed {
908            // If the waiter was granted permits, release them
909            self.semaphore.release(self.waiter.num_permits);
910        }
911    }
912}
913
914impl crate::annotations::WithName for &BatchSemaphore {
915    fn with_name_and_kind(self, name: Option<&str>, kind: Option<&str>) -> Self {
916        self.init_object_id();
917        crate::annotations::record_name_for_object(self.state.borrow().id.unwrap(), name, kind);
918        self
919    }
920}
921
922impl crate::annotations::WithName for BatchSemaphore {
923    fn with_name_and_kind(self, name: Option<&str>, kind: Option<&str>) -> Self {
924        (&self).with_name_and_kind(name, kind);
925        self
926    }
927}
928
929impl BatchSemaphore {
930    /// Returns a reference to this semaphore's resource signature.
931    pub fn signature(&self) -> &ResourceSignature {
932        &self.signature
933    }
934}