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