1use core::task::{Poll, Waker};
35
36use super::internal::Ordering;
37use super::queue::{HANDOFF, Policy, stops_spin};
38use super::spin::Gaps;
39use super::waiter::{Cancel, Park, Wait};
40
41#[cfg(not(all(loom, test)))]
45pub const MAX_BYPASS: u32 = 16;
46#[cfg(all(loom, test))]
49pub const MAX_BYPASS: u32 = 2;
50
51pub(super) const CLOSED_GRANT: usize = usize::MAX;
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59pub(super) struct Closed;
60
61pub(super) trait Lock: Policy {
63 const SPINS: bool = true;
67
68 fn admit(&self, state: usize, need: usize) -> Option<usize>;
71
72 fn give_back(&self, need: usize);
74
75 fn closed(&self, _state: usize) -> bool {
79 false
80 }
81
82 fn try_take(&self, need: usize) -> Result<(), usize> {
86 take(self, need, false)
87 }
88
89 fn take_for_waiter(&self, need: usize) -> bool {
92 take(self, need, true).is_ok()
93 }
94
95 fn take_or_owe(&self, wait: &Wait, need: usize) -> Result<(), usize> {
99 self.owe_or_take(wait, |state| self.admit(state, need))
100 }
101}
102
103pub(super) fn take<L: Lock + ?Sized>(lock: &L, need: usize, for_waiter: bool) -> Result<(), usize> {
106 let failure = if for_waiter {
107 Ordering::Acquire
108 } else {
109 Ordering::Relaxed
110 };
111 take_from(lock, need, for_waiter, lock.queue().state.load(failure))
112}
113
114pub(super) fn take_from<L: Lock + ?Sized>(
118 lock: &L,
119 need: usize,
120 for_waiter: bool,
121 mut current: usize,
122) -> Result<(), usize> {
123 let (success, failure) = if for_waiter {
124 (Ordering::AcqRel, Ordering::Acquire)
125 } else {
126 (Ordering::Acquire, Ordering::Relaxed)
127 };
128 let state = &lock.queue().state;
129 loop {
130 let next = match lock.admit(current, need) {
131 Some(next) if for_waiter || current & HANDOFF == 0 => next,
132 _ => return Err(current),
133 };
134 match state.compare_exchange_weak(current, next, success, failure) {
135 Ok(_) => return Ok(()),
136 Err(actual) => current = actual,
137 }
138 }
139}
140
141fn spin_take<L: Lock>(lock: &L, need: usize, mut seen: usize) -> bool {
152 let queue = lock.queue();
153 let rounds = queue.spin.rounds();
154 if rounds == 0 {
155 return false;
156 }
157 let mut gaps = Gaps::new(rounds);
158 let mut freed = false;
159 while gaps.wait() {
160 if stops_spin(seen) {
161 return false;
162 }
163 seen = queue.state.load(Ordering::Relaxed);
164 if !stops_spin(seen)
165 && let Some(next) = lock.admit(seen, need)
166 {
167 freed = true;
168 match queue
169 .state
170 .compare_exchange(seen, next, Ordering::Acquire, Ordering::Relaxed)
171 {
172 Ok(_) => {
173 queue.spin.won(rounds);
174 return true;
175 }
176 Err(actual) => seen = actual,
177 }
178 }
179 }
180 if !freed {
181 queue.spin.ran_out(rounds);
182 }
183 false
184}
185
186#[inline]
191pub(super) fn taken_at_once<L: Lock + ?Sized>(
192 lock: &L,
193 need: usize,
194 waker: &Waker,
195) -> Poll<Result<(), Closed>> {
196 admitted(lock, need, crate::coop::spend(waker))
197}
198
199#[inline]
203pub(super) fn admitted<L: Lock + ?Sized>(
204 lock: &L,
205 need: usize,
206 admit: bool,
207) -> Poll<Result<(), Closed>> {
208 if admit {
209 Poll::Ready(Ok(()))
210 } else {
211 lock.give_back(need);
212 Poll::Pending
213 }
214}
215
216pub(super) struct Contend {
218 wait: Wait,
219 bypass: u32,
220}
221
222impl Contend {
223 pub(super) const fn new() -> Self {
224 Self {
225 wait: Wait::new(),
226 bypass: 0,
227 }
228 }
229
230 #[inline]
231 pub(super) fn is_queued(&self) -> bool {
232 self.wait.is_queued()
233 }
234
235 #[inline]
238 pub(super) fn poll<L: Lock>(&mut self, lock: &L, need: usize, waker: &Waker) -> Poll<()> {
239 self.poll_from(lock, need, waker, None)
240 }
241
242 #[inline]
246 pub(super) fn poll_from<L: Lock>(
247 &mut self,
248 lock: &L,
249 need: usize,
250 waker: &Waker,
251 seen: Option<usize>,
252 ) -> Poll<()> {
253 self.poll_closable_from(lock, need, waker, seen)
254 .map(|taken| debug_assert!(taken.is_ok(), "a lock that never closes was closed"))
255 }
256
257 pub(super) fn poll_closable<L: Lock>(
260 &mut self,
261 lock: &L,
262 need: usize,
263 waker: &Waker,
264 ) -> Poll<Result<(), Closed>> {
265 self.poll_closable_from(lock, need, waker, None)
266 }
267
268 fn poll_closable_from<L: Lock>(
269 &mut self,
270 lock: &L,
271 need: usize,
272 waker: &Waker,
273 seen: Option<usize>,
274 ) -> Poll<Result<(), Closed>> {
275 let queue = lock.queue();
276 let mut park = if self.wait.is_queued() {
277 self.wait.park(queue, Some(waker))
278 } else {
279 let first = match seen {
283 Some(seen) => take_from(lock, need, false, seen),
284 None => lock.try_take(need),
285 };
286 match first {
287 Ok(()) => return taken_at_once(lock, need, waker),
288 Err(seen) => {
289 if lock.closed(seen) {
290 return if crate::coop::spend(waker) {
291 Poll::Ready(Err(Closed))
292 } else {
293 Poll::Pending
294 };
295 }
296 if L::SPINS && spin_take(lock, need, seen) {
297 return taken_at_once(lock, need, waker);
298 }
299 }
300 }
301 lock.enqueue(&mut self.wait, need, waker);
302 self.wait.park(queue, None)
303 };
304 loop {
305 match park {
306 Park::Granted(payload) => {
307 queue.consume_nudge();
309 return Poll::Ready(if payload == CLOSED_GRANT {
310 Err(Closed)
311 } else {
312 Ok(())
313 });
314 }
315 Park::Pending => return Poll::Pending,
316 Park::Nudged => {
322 queue.consume_nudge();
323 let attempt = if self.bypass + 1 == MAX_BYPASS {
324 lock.take_or_owe(&self.wait, need)
325 } else {
326 lock.try_take(need)
327 };
328 match attempt {
329 Ok(()) => {
330 if L::SPINS && queue.spin.rounds() == 0 {
334 queue.spin.won(0);
335 }
336 self.cancel(lock, need);
337 return Poll::Ready(Ok(()));
338 }
339 Err(seen) if seen & HANDOFF == 0 => {
344 self.bypass = (self.bypass + 1).min(MAX_BYPASS);
345 }
346 Err(_) => {}
347 }
348 park = if L::SPINS {
352 self.wait.spin_park(queue, waker)
353 } else {
354 self.wait.park(queue, Some(waker))
355 };
356 }
357 }
358 }
359 }
360
361 pub(super) fn cancel<L: Lock>(&mut self, lock: &L, need: usize) {
364 match self.wait.cancel(lock.queue()) {
365 Cancel::Idle => {}
366 Cancel::Withdrawn(woken) => lock.withdrawn(woken),
367 Cancel::Granted(payload) => {
368 lock.queue().consume_nudge();
369 if payload != CLOSED_GRANT {
371 lock.give_back(need);
372 }
373 }
374 }
375 }
376}
377
378#[cfg(all(test, not(loom)))]
379mod tests {
380 use core::cell::Cell;
381
382 use super::super::queue::{Arrivals, WaitQueue};
383 use super::super::raw_semaphore::RawSemaphore;
384 use super::super::test_support::Polled;
385 use super::super::waiter::WakeList;
386 use super::*;
387
388 struct Gap<'a> {
392 sem: &'a RawSemaphore,
393 after_loss: Cell<Option<Box<dyn FnOnce() + 'a>>>,
394 }
395
396 impl Gap<'_> {
397 fn lost(&self, attempt: Result<(), usize>) -> Result<(), usize> {
398 if attempt.is_err()
399 && let Some(between) = self.after_loss.take()
400 {
401 between();
402 }
403 attempt
404 }
405 }
406
407 impl Policy for Gap<'_> {
408 fn queue(&self) -> &WaitQueue {
409 self.sem.queue()
410 }
411
412 fn pass(&self, arrivals: Arrivals, woken: &mut WakeList<'_>, wake_front: bool) -> usize {
413 self.sem.pass(arrivals, woken, wake_front)
414 }
415 }
416
417 impl Lock for Gap<'_> {
418 fn admit(&self, state: usize, need: usize) -> Option<usize> {
419 self.sem.admit(state, need)
420 }
421
422 fn give_back(&self, need: usize) {
423 self.sem.give_back(need);
424 }
425
426 fn try_take(&self, need: usize) -> Result<(), usize> {
427 self.lost(self.sem.try_take(need))
428 }
429
430 fn take_or_owe(&self, wait: &Wait, need: usize) -> Result<(), usize> {
431 self.lost(self.sem.take_or_owe(wait, need))
432 }
433 }
434
435 #[test]
436 fn nothing_barges_between_a_waiters_last_loss_and_its_handoff() {
437 let sem = RawSemaphore::new(1);
438 let barged = Cell::new(false);
439 let gap = Gap {
440 sem: &sem,
441 after_loss: Cell::new(None),
442 };
443 assert!(sem.try_acquire(1));
444 let mut contend = Contend::new();
445 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
446 contend.poll(&gap, 1, cx.waker())
447 }));
448 waiter.pending();
449 for round in 1..=MAX_BYPASS {
450 sem.release(1);
451 assert_eq!(waiter.wakes() as u32, round, "a release nudges it");
452 assert!(sem.try_acquire(1), "a free permit is taken past it");
453 if round < MAX_BYPASS {
454 waiter.pending();
455 }
456 }
457 gap.after_loss.set(Some(Box::new(|| {
460 sem.release(1);
461 barged.set(sem.try_acquire(1));
462 })));
463 let served = waiter.poll().is_ready();
464 assert!(
465 !barged.get(),
466 "a barger took the permit freed after the waiter's last counted loss"
467 );
468 assert!(served, "the freed permit is handed to the waiter");
469 assert!(!sem.try_acquire(1), "the waiter holds it");
470 sem.release(1);
471 assert!(sem.try_acquire(1), "barging resumes once it is served");
472 }
473
474 struct Raced<'a> {
479 sem: &'a RawSemaphore,
480 passes: Cell<u32>,
481 pass_begins: Box<dyn Fn(u32) + 'a>,
482 pass_ends: Box<dyn Fn(u32) + 'a>,
483 before_try: Cell<Option<Box<dyn FnOnce() + 'a>>>,
484 before_owe: Cell<Option<Box<dyn FnOnce() + 'a>>>,
485 }
486
487 impl<'a> Raced<'a> {
488 fn new(
489 sem: &'a RawSemaphore,
490 pass_begins: impl Fn(u32) + 'a,
491 pass_ends: impl Fn(u32) + 'a,
492 ) -> Self {
493 Self {
494 sem,
495 passes: Cell::new(0),
496 pass_begins: Box::new(pass_begins),
497 pass_ends: Box::new(pass_ends),
498 before_try: Cell::new(None),
499 before_owe: Cell::new(None),
500 }
501 }
502 }
503
504 impl Policy for Raced<'_> {
505 fn queue(&self) -> &WaitQueue {
506 self.sem.queue()
507 }
508
509 fn pass(&self, arrivals: Arrivals, woken: &mut WakeList<'_>, wake_front: bool) -> usize {
510 let pass = self.passes.get() + 1;
511 self.passes.set(pass);
512 (self.pass_begins)(pass);
513 let clear = self.sem.pass(arrivals, woken, wake_front);
514 (self.pass_ends)(pass);
515 clear
516 }
517 }
518
519 impl Lock for Raced<'_> {
520 fn admit(&self, state: usize, need: usize) -> Option<usize> {
521 self.sem.admit(state, need)
522 }
523
524 fn give_back(&self, need: usize) {
525 self.sem.give_back(need);
526 }
527
528 fn try_take(&self, need: usize) -> Result<(), usize> {
529 if let Some(first) = self.before_try.take() {
530 first();
531 }
532 self.sem.try_take(need)
533 }
534
535 fn take_or_owe(&self, wait: &Wait, need: usize) -> Result<(), usize> {
536 if let Some(first) = self.before_owe.take() {
537 first();
538 }
539 self.sem.take_or_owe(wait, need)
540 }
541 }
542
543 #[test]
549 fn a_waiter_serving_the_queue_is_not_passed_over_without_bound() {
550 let sem = RawSemaphore::new(1);
551 let barged = Cell::new(0u32);
552 let rounds = 4 * MAX_BYPASS;
553 let raced = Raced::new(
554 &sem,
555 |pass| {
556 if pass == 1 {
557 sem.release(1);
558 }
559 },
560 |pass| {
561 if pass <= rounds && sem.try_acquire(1) {
562 barged.set(barged.get() + 1);
563 sem.release(1);
564 }
565 },
566 );
567 assert!(sem.try_acquire(1));
568 let mut contend = Contend::new();
569 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
570 contend.poll(&raced, 1, cx.waker())
571 }));
572 let served = waiter.poll().is_ready();
573 assert!(
574 barged.get() <= MAX_BYPASS,
575 "{} callers took the permit past a queued waiter in {} passes of its own drain, \
576 and it never got to retry (the bound is {MAX_BYPASS})",
577 barged.get(),
578 raced.passes.get(),
579 );
580 assert!(
581 raced.passes.get() <= 3,
582 "the waiter's drain ran {} passes: releases that come while its nudge is out \
583 must not ask for more",
584 raced.passes.get(),
585 );
586 assert!(
587 served,
588 "the permit the last caller gave back is the waiter's"
589 );
590 assert!(!sem.try_acquire(1), "the waiter holds it");
591 }
592
593 #[test]
598 fn a_retry_lost_in_the_poll_that_queues_counts_as_a_bypass() {
599 let sem = RawSemaphore::new(1);
600 let raced = Raced::new(
601 &sem,
602 |pass| {
603 if pass == 1 {
604 sem.release(1);
605 }
606 },
607 |pass| {
608 if pass == 1 {
609 assert!(
610 sem.try_acquire(1),
611 "the freed permit is taken past the waiter"
612 );
613 }
614 },
615 );
616 assert!(sem.try_acquire(1));
617 let mut contend = Contend::new();
618 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
619 contend.poll(&raced, 1, cx.waker())
620 }));
621 waiter.pending();
622 drop(waiter);
623 assert_eq!(
624 contend.bypass, 1,
625 "the loss in the queueing poll is counted"
626 );
627 contend.cancel(&raced, 1);
628 sem.release(1);
629 assert!(sem.try_acquire(1), "nothing is left held or owed");
630 }
631
632 #[test]
637 fn a_loss_to_its_own_handoff_does_not_count_past_the_bound() {
638 let sem = RawSemaphore::new(1);
639 let raced = Raced::new(&sem, |_| {}, |_| {});
640 assert!(sem.try_acquire(1));
641 let mut contend = Contend::new();
642 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
643 contend.poll(&raced, 1, cx.waker())
644 }));
645 waiter.pending();
646 for _ in 1..MAX_BYPASS {
648 sem.release(1);
649 assert!(sem.try_acquire(1), "a free permit is taken past it");
650 waiter.pending();
651 }
652 sem.release(1);
653 assert!(sem.try_acquire(1), "a free permit is taken past it");
654 raced.before_owe.set(Some(Box::new(|| {
658 sem.release(1);
659 assert!(sem.try_acquire(1), "a free permit is taken past it");
660 })));
661 raced.before_try.set(Some(Box::new(|| sem.release(1))));
664 assert!(waiter.poll().is_ready(), "the handoff reached the waiter");
665 drop(waiter);
666 assert_eq!(
667 contend.bypass, MAX_BYPASS,
668 "a loss to the permit taken for the waiter itself was counted"
669 );
670 assert!(!sem.try_acquire(1), "the waiter holds it");
671 }
672
673 #[test]
678 fn a_drain_that_found_nobody_queued_does_not_pass_again_for_releases() {
679 let sem = RawSemaphore::new(2);
680 let hammering = Cell::new(false);
681 let rounds = 4 * MAX_BYPASS;
682 let raced = Raced::new(
683 &sem,
684 |_| {},
685 |pass| {
686 if hammering.get() && pass <= rounds {
687 sem.release(1);
690 assert!(sem.try_acquire(1), "the permit is free again");
691 }
692 },
693 );
694 assert!(sem.try_acquire(1), "the other holder's permit");
695 let mut contend = Contend::new();
696 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
697 contend.poll(&raced, 2, cx.waker())
698 }));
699 waiter.pending();
700 drop(waiter);
701 let before = raced.passes.get();
704 hammering.set(true);
705 contend.cancel(&raced, 2);
706 hammering.set(false);
707 assert_eq!(
708 raced.passes.get() - before,
709 1,
710 "the drain passed again for releases that had nobody to wake"
711 );
712 let mut next = Contend::new();
716 let mut waiter = Polled::new(core::future::poll_fn(|cx| next.poll(&raced, 2, cx.waker())));
717 waiter.pending();
718 sem.release(1);
719 assert_eq!(waiter.wakes(), 1, "the release nudges the waiter");
720 assert!(waiter.poll().is_ready(), "both permits are its");
721 drop(waiter);
722 assert_eq!(
723 next.bypass, 0,
724 "it retried on a request from before it queued"
725 );
726 sem.release(2);
727 assert_eq!(sem.available(), 2, "a permit was lost or made up");
728 }
729
730 #[test]
734 fn a_release_during_a_pass_that_found_a_waiter_reaches_it() {
735 let sem = RawSemaphore::new(1);
736 let raced = Raced::new(
737 &sem,
738 |_| {},
739 |pass| {
740 if pass == 1 {
741 sem.release(1);
742 }
743 },
744 );
745 assert!(sem.try_acquire(1));
746 let mut contend = Contend::new();
747 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
748 contend.poll(&raced, 1, cx.waker())
749 }));
750 assert!(
753 waiter.poll().is_ready(),
754 "a release that found the drain role taken was lost"
755 );
756 assert!(!sem.try_acquire(1), "the waiter holds it");
757 }
758
759 struct Watched<'a> {
764 sem: &'a RawSemaphore,
765 looks: Cell<u32>,
766 at_look: Box<dyn Fn(u32) + 'a>,
767 }
768
769 impl<'a> Watched<'a> {
770 fn new(sem: &'a RawSemaphore, at_look: impl Fn(u32) + 'a) -> Self {
771 Self {
772 sem,
773 looks: Cell::new(0),
774 at_look: Box::new(at_look),
775 }
776 }
777 }
778
779 impl Policy for Watched<'_> {
780 fn queue(&self) -> &WaitQueue {
781 self.sem.queue()
782 }
783
784 fn pass(&self, arrivals: Arrivals, woken: &mut WakeList<'_>, wake_front: bool) -> usize {
785 self.sem.pass(arrivals, woken, wake_front)
786 }
787 }
788
789 impl Lock for Watched<'_> {
790 fn admit(&self, state: usize, need: usize) -> Option<usize> {
791 let admitted = self.sem.admit(state, need);
792 let look = self.looks.get() + 1;
793 self.looks.set(look);
794 (self.at_look)(look);
795 admitted
796 }
797
798 fn give_back(&self, need: usize) {
799 self.sem.give_back(need);
800 }
801 }
802
803 #[test]
808 fn a_contender_takes_a_lock_let_go_during_its_spin_without_queueing() {
809 let sem = RawSemaphore::new(1);
810 assert!(sem.try_acquire(1), "the holder takes it");
811 let budget = sem.queue().spin.rounds();
812 assert!(budget > 0, "a fresh lock spins");
813 let watched = Watched::new(&sem, |look| {
814 if look == 2 {
815 sem.release(1);
816 }
817 });
818 let mut contend = Contend::new();
819 let mut contender = Polled::new(core::future::poll_fn(|cx| {
820 contend.poll(&watched, 1, cx.waker())
821 }));
822 assert!(contender.poll().is_ready(), "the spin took the freed lock");
823 assert_eq!(contender.wakes(), 0, "a spinner needs no wake");
824 drop(contender);
825 assert!(!contend.is_queued(), "the contender never queued");
826 assert!(
827 watched.looks.get() >= 3,
828 "it looked again after the release"
829 );
830 assert_eq!(
831 sem.queue().spin.rounds(),
832 (2 * budget).min(u32::from(super::super::spin::MAX)),
833 "a won spin doubles the budget"
834 );
835 assert!(!sem.try_acquire(1), "the contender holds it");
836 }
837
838 #[test]
841 fn a_spin_that_never_sees_room_halves_the_budget_and_queues() {
842 let sem = RawSemaphore::new(1);
843 assert!(sem.try_acquire(1));
844 let budget = sem.queue().spin.rounds();
845 let watched = Watched::new(&sem, |_| {});
846 let mut contend = Contend::new();
847 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
848 contend.poll(&watched, 1, cx.waker())
849 }));
850 waiter.pending();
851 assert!(watched.looks.get() > 1, "it spun before queueing");
852 assert_eq!(
853 sem.queue().spin.rounds(),
854 budget / 2,
855 "a spin that ran out halves"
856 );
857 sem.release(1);
858 assert_eq!(waiter.wakes(), 1, "the release reaches the queued waiter");
859 assert!(waiter.poll().is_ready());
860 }
861
862 #[test]
865 fn a_spin_that_saw_room_but_lost_keeps_the_budget() {
866 let sem = RawSemaphore::new(1);
867 assert!(sem.try_acquire(1));
868 let budget = sem.queue().spin.rounds();
869 let watched = Watched::new(&sem, |look| match look {
870 2 => sem.release(1),
872 3 => assert!(sem.try_acquire(1), "the barger takes it"),
875 _ => {}
876 });
877 let mut contend = Contend::new();
878 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
879 contend.poll(&watched, 1, cx.waker())
880 }));
881 waiter.pending();
882 assert_eq!(
883 sem.queue().spin.rounds(),
884 budget,
885 "a busy lock keeps its budget"
886 );
887 drop(waiter);
888 contend.cancel(&watched, 1);
889 sem.release(1);
890 }
891
892 #[test]
898 fn a_contender_queues_at_once_behind_a_settled_queue() {
899 let sem = RawSemaphore::new(1);
900 assert!(sem.try_acquire(1));
901 let budget = sem.queue().spin.rounds();
902 let plain = Watched::new(&sem, |_| {});
903 let mut first = Contend::new();
904 let mut first_waiter = Polled::new(core::future::poll_fn(|cx| {
905 first.poll(&plain, 1, cx.waker())
906 }));
907 first_waiter.pending();
908 let after_first = sem.queue().spin.rounds();
909 let watched = Watched::new(&sem, |_| {});
910 let mut second = Contend::new();
911 let mut second_waiter = Polled::new(core::future::poll_fn(|cx| {
912 second.poll(&watched, 1, cx.waker())
913 }));
914 second_waiter.pending();
915 assert_eq!(watched.looks.get(), 1, "it looked once and did not spin");
916 assert_eq!(
917 sem.queue().spin.rounds(),
918 after_first,
919 "the budget is untouched"
920 );
921 assert!(after_first <= budget);
922 drop((first_waiter, second_waiter));
923 first.cancel(&plain, 1);
924 second.cancel(&watched, 1);
925 sem.release(1);
926 assert!(sem.try_acquire(1), "nothing is left held or owed");
927 }
928
929 #[test]
938 fn the_spin_takes_only_what_a_fresh_caller_may() {
939 use super::super::raw_semaphore::SHIFT;
940 const DRAINING_BIT: usize = 1;
941 const QUEUED_BIT: usize = 1 << 2;
942 const CLOSED_BIT: usize = 1 << super::super::queue::FIRST_BIT;
943 for permits in 0..=2usize {
944 for flags in 0..(1usize << SHIFT) {
945 let state = (permits << SHIFT) | flags;
946 let sem = RawSemaphore::new(0);
947 sem.queue().state.store(state, Ordering::Relaxed);
948 let took = spin_take(&sem, 1, state);
949 let settled = flags & (QUEUED_BIT | DRAINING_BIT) == QUEUED_BIT;
950 let may = permits >= 1 && flags & (HANDOFF | CLOSED_BIT) == 0 && !settled;
951 assert_eq!(took, may, "state {state:#x}: the spin took the wrong thing");
952 let after = sem.queue().state.load(Ordering::Relaxed);
953 let expected = if took { state - (1 << SHIFT) } else { state };
954 assert_eq!(
955 after, expected,
956 "state {state:#x}: the spin left the wrong word"
957 );
958 }
959 }
960 }
961
962 #[test]
965 fn a_won_retry_revives_a_spent_budget() {
966 let sem = RawSemaphore::new(1);
967 let spin = &sem.queue().spin;
968 while spin.rounds() > 0 {
969 spin.ran_out(spin.rounds());
970 }
971 assert!(sem.try_acquire(1));
972 let mut contend = Contend::new();
973 let mut waiter = Polled::new(core::future::poll_fn(|cx| {
974 contend.poll(&sem, 1, cx.waker())
975 }));
976 waiter.pending();
977 assert_eq!(spin.rounds(), 0, "a spent budget queues at once");
978 sem.release(1);
979 assert!(waiter.poll().is_ready(), "the retry wins");
980 assert_eq!(spin.rounds(), 1, "the won retry revives the budget");
981 }
982}