1use alloc::{collections::VecDeque, sync::Arc};
4use core::{
5 sync::atomic::{AtomicU64, AtomicUsize, Ordering, fence},
6 time::Duration,
7};
8
9use crate::{
10 runtime::{
11 lock::{PreemptScope, PreemptTicketLock},
12 task_runtime,
13 },
14 thread::{
15 TaskError, ThreadId, ThreadWakeHandle, WaitWakeClaim, WaitWakeClaimState, WaitWakeDelivery,
16 WakeIntent,
17 current::{
18 CurrentParkStart, acquire_blocking_permit, park::begin_current_park_with_permit,
19 },
20 },
21 time::MonotonicDeadline,
22};
23
24#[track_caller]
26pub fn sleep(duration: Duration) {
27 sleep_until(task_runtime::monotonic_now().deadline_after(duration));
28}
29
30#[track_caller]
32pub fn sleep_until(deadline: MonotonicDeadline) {
33 let queue = WaitQueue::new();
34 while !task_runtime::monotonic_now().reached(deadline) {
35 queue
36 .wait_once(Some(deadline))
37 .expect("timed sleep must satisfy scheduler invariants");
38 }
39}
40
41#[derive(Debug)]
47pub struct WaitQueue {
48 waiters: PreemptTicketLock<VecDeque<Waiter>>,
49 notification_generation: AtomicU64,
50 active_wait_attempts: AtomicUsize,
51}
52
53#[derive(Clone, Debug)]
59pub struct WaitQueueWakeToken {
60 waiter: Arc<WaiterWake>,
61}
62
63#[derive(Clone, Copy, Debug, Eq, PartialEq)]
65pub enum WaitQueueWakeOutcome {
66 Delivered,
68 Retry,
70 Stale,
72}
73
74pub enum WaitQueueRegistration<G> {
76 Armed(G),
78 Retry(G),
80}
81
82impl WaitQueueWakeToken {
83 pub fn notify(&self) -> WaitQueueWakeOutcome {
85 self.notify_with_intent(WakeIntent::Normal)
86 }
87
88 pub fn notify_sync(&self) -> WaitQueueWakeOutcome {
90 self.notify_with_intent(WakeIntent::Sync)
91 }
92
93 pub fn is_active(&self) -> bool {
95 self.waiter.claim.is_active()
96 }
97
98 fn notify_with_intent(&self, intent: WakeIntent) -> WaitQueueWakeOutcome {
99 assert_task_context_notification();
100 let claim_owner = match self.waiter.try_select() {
101 WaiterSelection::Selected(claim_owner) => claim_owner,
102 WaiterSelection::Retry => return WaitQueueWakeOutcome::Retry,
103 WaiterSelection::Stale => return WaitQueueWakeOutcome::Stale,
104 };
105 let delivery = claim_owner
109 .wake
110 .deliver_wait_claim_from_task(&claim_owner.claim, intent);
111 match delivery {
112 WaitWakeDelivery::Delivered => {
113 debug_assert_eq!(self.waiter.deactivate(), WaiterRemoval::Delivered);
114 WaitQueueWakeOutcome::Delivered
115 }
116 WaitWakeDelivery::Cancelled | WaitWakeDelivery::Exited => {
117 self.waiter.deactivate();
118 WaitQueueWakeOutcome::Stale
119 }
120 WaitWakeDelivery::Unavailable => {
121 if claim_owner.requeue_after_unavailable() {
122 WaitQueueWakeOutcome::Retry
123 } else {
124 self.waiter.deactivate();
125 WaitQueueWakeOutcome::Stale
126 }
127 }
128 }
129 }
130}
131
132impl WaitQueue {
133 pub const fn new() -> Self {
135 Self {
136 waiters: PreemptTicketLock::new(VecDeque::new()),
137 notification_generation: AtomicU64::new(0),
138 active_wait_attempts: AtomicUsize::new(0),
139 }
140 }
141
142 #[track_caller]
144 pub fn wait(&self) {
145 self.wait_once(None)
146 .expect("wait queue park must satisfy scheduler invariants");
147 }
148
149 #[track_caller]
157 pub fn wait_until<F>(&self, condition: F)
158 where
159 F: Fn() -> bool,
160 {
161 self.try_wait_until(condition)
162 .expect("conditional wait must satisfy scheduler invariants");
163 }
164
165 pub fn try_wait_until<F>(&self, condition: F) -> Result<(), TaskError>
175 where
176 F: Fn() -> bool,
177 {
178 loop {
179 if self.wait_once_if(None, &condition)? {
180 return Ok(());
181 }
182 }
183 }
184
185 #[track_caller]
190 pub fn wait_timeout(&self, timeout: Duration) -> bool {
191 let deadline = task_runtime::monotonic_now().deadline_after(timeout);
192 loop {
193 if task_runtime::monotonic_now().reached(deadline) {
194 return true;
195 }
196 let outcome = self
197 .wait_once(Some(deadline))
198 .expect("timed wait must satisfy scheduler invariants");
199 if outcome == WaitOutcome::Notified {
200 return false;
201 }
202 if task_runtime::monotonic_now().reached(deadline) {
203 return true;
204 }
205 }
206 }
207
208 #[track_caller]
212 pub fn wait_timeout_until<F>(&self, timeout: Duration, condition: F) -> bool
213 where
214 F: Fn() -> bool,
215 {
216 self.wait_until_deadline(
217 task_runtime::monotonic_now().deadline_after(timeout),
218 condition,
219 )
220 }
221
222 #[track_caller]
229 pub fn wait_until_deadline<F>(&self, deadline: MonotonicDeadline, condition: F) -> bool
230 where
231 F: Fn() -> bool,
232 {
233 loop {
234 if task_runtime::monotonic_now().reached(deadline) {
235 return !condition();
236 }
237 let condition_met = self
238 .wait_once_if(Some(deadline), &condition)
239 .unwrap_or_else(|error| {
240 panic!("timed conditional wait must satisfy scheduler invariants: {error:?}")
241 });
242 if condition_met {
243 return false;
244 }
245 }
246 }
247
248 pub fn notify_one(&self) -> bool {
255 self.notify_one_with_intent(WakeIntent::Normal)
256 }
257
258 pub fn notify_one_sync(&self) -> bool {
264 self.notify_one_with_intent(WakeIntent::Sync)
265 }
266
267 fn notify_one_with_intent(&self, intent: WakeIntent) -> bool {
268 assert_task_context_notification();
269 if !self.may_have_active_wait_attempts() {
270 return false;
271 }
272 let _preempt = PreemptScope::enter();
273 self.notify_one_preempt_disabled(intent)
274 }
275
276 fn notify_one_preempt_disabled(&self, intent: WakeIntent) -> bool {
277 let (notification_generation, mut selected) = {
278 let mut waiters = self.waiters.lock();
279 let previous_generation = self
280 .notification_generation
281 .try_update(Ordering::Release, Ordering::Relaxed, |generation| {
282 generation.checked_add(1)
283 })
284 .unwrap_or_else(|_| panic!("wait-queue notification generation exhausted"));
285 let notification_generation = previous_generation + 1;
286 let selected = select_waiter(&mut waiters, notification_generation);
287 (notification_generation, selected)
288 };
289 loop {
290 let Some(claim_owner) = selected else {
291 return false;
292 };
293
294 let delivery = claim_owner
295 .wake
296 .deliver_wait_claim_from_task(&claim_owner.claim, intent);
297 let mut waiters = self.waiters.lock();
298 let index = waiters
299 .iter()
300 .position(|waiter| waiter.owns_claim(&claim_owner));
301 match delivery {
302 WaitWakeDelivery::Delivered => {
303 assert_eq!(
304 claim_owner.claim.state(),
305 WaitWakeClaimState::Delivered,
306 "scheduler delivery must publish the claim before returning"
307 );
308 if let Some(index) = index {
309 let waiter = waiters
310 .remove(index)
311 .expect("located delivered waiter must remain present");
312 assert_eq!(waiter.wake.deactivate(), WaiterRemoval::Delivered);
313 }
314 return true;
315 }
316 WaitWakeDelivery::Cancelled | WaitWakeDelivery::Exited => {
317 if let Some(index) = index {
318 let waiter = waiters
319 .remove(index)
320 .expect("located stale waiter must remain present");
321 let _ = waiter.wake.deactivate();
322 }
323 }
324 WaitWakeDelivery::Unavailable => {
325 if let Some(index) = index {
326 waiters[index].requeue_after_unavailable();
327 }
328 }
329 }
330 selected = select_waiter(&mut waiters, notification_generation);
331 }
332 }
333
334 pub fn notify_all(&self) {
340 assert_task_context_notification();
341 if !self.may_have_active_wait_attempts() {
342 return;
343 }
344 let _preempt = PreemptScope::enter();
345 while self.notify_one_preempt_disabled(WakeIntent::Normal) {}
346 }
347
348 fn wait_once(&self, deadline: Option<MonotonicDeadline>) -> Result<WaitOutcome, TaskError> {
349 self.wait_once_inner(deadline, None)
350 }
351
352 fn wait_once_if(
353 &self,
354 deadline: Option<MonotonicDeadline>,
355 condition: &dyn Fn() -> bool,
356 ) -> Result<bool, TaskError> {
357 match self.wait_once_inner(deadline, Some(condition))? {
358 WaitOutcome::Condition => Ok(true),
359 WaitOutcome::Notified | WaitOutcome::OtherWake => Ok(false),
360 }
361 }
362
363 fn wait_once_inner(
364 &self,
365 deadline: Option<MonotonicDeadline>,
366 condition: Option<&dyn Fn() -> bool>,
367 ) -> Result<WaitOutcome, TaskError> {
368 let permit = acquire_blocking_permit()?;
371 let _active_attempt = ActiveWaitAttempt::begin(&self.active_wait_attempts);
372 let observed_generation = if let Some(condition) = condition {
373 let generation = self.notification_generation.load(Ordering::Acquire);
374 if condition() {
375 return Ok(WaitOutcome::Condition);
376 }
377 Some(generation)
378 } else {
379 None
380 };
381 let park = {
382 let mut waiters = self.waiters.lock();
383 if observed_generation.is_some_and(|generation| {
384 self.notification_generation.load(Ordering::Acquire) != generation
385 }) {
386 return Ok(WaitOutcome::OtherWake);
387 }
388 let mut park = match begin_current_park_with_permit(&permit)? {
389 CurrentParkStart::Notified => return Ok(WaitOutcome::OtherWake),
390 CurrentParkStart::Prepared(park) => park,
391 };
392 let thread = park.thread_id();
393 waiters.push_back(Waiter::new(thread, park.generation(), park.wake_handle()));
394 if let Some(deadline) = deadline
395 && let Err(error) = park.arm_deadline(deadline)
396 {
397 remove_waiter(&mut waiters, thread);
398 park.cancel()?;
399 return Err(error);
400 }
401 park
402 };
403 let thread = park.thread_id();
404
405 if let Err(error) = park.commit() {
406 remove_waiter(&mut self.waiters.lock(), thread);
407 return Err(error);
408 }
409 Ok(match remove_waiter(&mut self.waiters.lock(), thread) {
410 WaiterRemoval::OtherWake => WaitOutcome::OtherWake,
411 WaiterRemoval::Missing | WaiterRemoval::Delivered => WaitOutcome::Notified,
412 })
413 }
414
415 fn may_have_active_wait_attempts(&self) -> bool {
416 fence(Ordering::SeqCst);
423 self.active_wait_attempts.load(Ordering::SeqCst) != 0
424 }
425}
426
427#[track_caller]
445pub fn wait_until_registered<F, L, R, G, H>(condition: F, mut acquire: L, mut register: R) -> bool
446where
447 F: Fn() -> bool,
448 L: FnMut() -> G,
449 R: FnMut(&mut G, WaitQueueWakeToken) -> WaitQueueRegistration<H>,
450{
451 let mut selected = false;
452 loop {
453 match wait_once_registered(&condition, &mut acquire, &mut register)
454 .expect("registered conditional wait must satisfy scheduler invariants")
455 {
456 WaitOutcome::Condition => return selected,
457 WaitOutcome::Notified => selected = true,
458 WaitOutcome::OtherWake => {}
459 }
460 }
461}
462
463fn wait_once_registered<F, L, R, G, H>(
464 condition: &F,
465 acquire: &mut L,
466 register: &mut R,
467) -> Result<WaitOutcome, TaskError>
468where
469 F: Fn() -> bool,
470 L: FnMut() -> G,
471 R: FnMut(&mut G, WaitQueueWakeToken) -> WaitQueueRegistration<H>,
472{
473 let permit = acquire_blocking_permit()?;
474 let mut queue_guard = acquire();
475 if condition() {
476 drop(queue_guard);
477 return Ok(WaitOutcome::Condition);
478 }
479 let park = match begin_current_park_with_permit(&permit)? {
480 CurrentParkStart::Notified => {
481 drop(queue_guard);
482 return Ok(WaitOutcome::OtherWake);
483 }
484 CurrentParkStart::Prepared(park) => park,
485 };
486 let token = WaitQueueWakeToken {
487 waiter: Arc::new(WaiterWake::new(
488 park.thread_id(),
489 park.generation(),
490 park.wake_handle(),
491 )),
492 };
493 let registration = register(&mut queue_guard, token.clone());
494 drop(queue_guard);
495 let (registration, retry) = match registration {
496 WaitQueueRegistration::Armed(registration) => (registration, false),
497 WaitQueueRegistration::Retry(registration) => (registration, true),
498 };
499
500 if retry {
501 let removal = token.waiter.deactivate();
502 drop(registration);
503 park.cancel()?;
504 return Ok(if removal == WaiterRemoval::Delivered {
505 WaitOutcome::Notified
506 } else {
507 WaitOutcome::OtherWake
508 });
509 }
510
511 if let Err(error) = park.commit() {
512 token.waiter.deactivate();
513 drop(registration);
514 return Err(error);
515 }
516 let removal = token.waiter.deactivate();
517 drop(registration);
518 Ok(match removal {
519 WaiterRemoval::Delivered => WaitOutcome::Notified,
520 WaiterRemoval::Missing | WaiterRemoval::OtherWake => WaitOutcome::OtherWake,
521 })
522}
523
524struct ActiveWaitAttempt<'a> {
525 active_wait_attempts: &'a AtomicUsize,
526}
527
528impl<'a> ActiveWaitAttempt<'a> {
529 fn begin(active_wait_attempts: &'a AtomicUsize) -> Self {
530 active_wait_attempts
531 .try_update(Ordering::SeqCst, Ordering::SeqCst, |attempts| {
532 attempts.checked_add(1)
533 })
534 .unwrap_or_else(|_| panic!("wait-queue active-attempt count exhausted"));
535 Self {
536 active_wait_attempts,
537 }
538 }
539}
540
541impl Drop for ActiveWaitAttempt<'_> {
542 fn drop(&mut self) {
543 self.active_wait_attempts
544 .try_update(Ordering::SeqCst, Ordering::SeqCst, |attempts| {
545 attempts.checked_sub(1)
546 })
547 .unwrap_or_else(|_| panic!("wait-queue active-attempt count underflow"));
548 }
549}
550
551fn assert_task_context_notification() {
552 assert!(
553 !task_runtime::in_hard_irq(),
554 "WaitQueue notification is task-context-only; use IrqWaitCell from hard IRQ"
555 );
556}
557
558impl Default for WaitQueue {
559 fn default() -> Self {
560 Self::new()
561 }
562}
563
564#[derive(Debug)]
565struct Waiter {
566 wake: Arc<WaiterWake>,
567 last_attempted_by: u64,
568}
569
570impl Waiter {
571 fn new(thread: ThreadId, park_generation: u64, wake: ThreadWakeHandle) -> Self {
572 Self {
573 wake: Arc::new(WaiterWake::new(thread, park_generation, wake)),
574 last_attempted_by: 0,
575 }
576 }
577
578 fn select(&mut self, notification_generation: u64) -> Option<Arc<WaiterWake>> {
579 if self.last_attempted_by == notification_generation {
580 return None;
581 }
582 self.last_attempted_by = notification_generation;
583 let WaiterSelection::Selected(selected_waiter) = self.wake.try_select() else {
584 return None;
585 };
586 Some(selected_waiter)
587 }
588
589 fn owns_claim(&self, claim_owner: &Arc<WaiterWake>) -> bool {
590 Arc::ptr_eq(&self.wake, claim_owner)
591 }
592
593 fn requeue_after_unavailable(&self) {
594 assert!(self.wake.requeue_after_unavailable());
595 }
596}
597
598#[derive(Debug)]
599struct WaiterWake {
600 wake: ThreadWakeHandle,
601 claim: WaitWakeClaim,
602}
603
604impl WaiterWake {
605 fn new(thread: ThreadId, park_generation: u64, wake: ThreadWakeHandle) -> Self {
606 Self {
607 wake,
608 claim: WaitWakeClaim::new(thread, park_generation),
609 }
610 }
611
612 fn try_select(self: &Arc<Self>) -> WaiterSelection {
613 loop {
614 match self.claim.state() {
615 WaitWakeClaimState::Queued => {
616 if self.claim.select() {
617 return WaiterSelection::Selected(Arc::clone(self));
618 }
619 }
620 WaitWakeClaimState::Selected => return WaiterSelection::Retry,
621 WaitWakeClaimState::Delivered
622 | WaitWakeClaimState::Cancelled
623 | WaitWakeClaimState::Inactive => return WaiterSelection::Stale,
624 }
625 }
626 }
627
628 fn requeue_after_unavailable(&self) -> bool {
629 self.claim.requeue_cancelled()
630 }
631
632 fn deactivate(&self) -> WaiterRemoval {
633 if self.claim.deactivate() {
634 WaiterRemoval::Delivered
635 } else {
636 WaiterRemoval::OtherWake
637 }
638 }
639}
640
641enum WaiterSelection {
642 Selected(Arc<WaiterWake>),
643 Retry,
644 Stale,
645}
646
647fn select_waiter(
648 waiters: &mut VecDeque<Waiter>,
649 notification_generation: u64,
650) -> Option<Arc<WaiterWake>> {
651 waiters
652 .iter_mut()
653 .find_map(|waiter| waiter.select(notification_generation))
654}
655
656#[derive(Clone, Copy, Debug, Eq, PartialEq)]
657enum WaitOutcome {
658 Condition,
659 Notified,
660 OtherWake,
661}
662
663#[derive(Clone, Copy, Debug, Eq, PartialEq)]
664enum WaiterRemoval {
665 Missing,
666 OtherWake,
667 Delivered,
668}
669
670fn remove_waiter(waiters: &mut VecDeque<Waiter>, thread: ThreadId) -> WaiterRemoval {
671 let Some(index) = waiters
672 .iter()
673 .position(|waiter| waiter.wake.claim.thread() == thread)
674 else {
675 return WaiterRemoval::Missing;
676 };
677 let waiter = waiters
678 .remove(index)
679 .expect("located wait-queue entry must remain present under its lock");
680 waiter.wake.deactivate()
681}
682
683#[cfg(all(test, not(miri)))]
684mod loom_tests {
685 use loom::{
686 sync::{
687 Arc, Mutex,
688 atomic::{AtomicBool, AtomicUsize, Ordering},
689 },
690 thread,
691 };
692
693 #[test]
694 fn notification_generation_closes_the_predicate_enqueue_window() {
695 loom::model(|| {
696 const READY: usize = 1;
697 const RETRY: usize = 2;
698 const QUEUED: usize = 3;
699
700 let notification_generation = Arc::new(AtomicUsize::new(0));
701 let condition = Arc::new(AtomicBool::new(false));
702 let waiter_queued = Arc::new(Mutex::new(false));
703 let waiter_outcome = Arc::new(AtomicUsize::new(0));
704 let waiter_woken = Arc::new(AtomicBool::new(false));
705
706 let waiter = {
707 let notification_generation = Arc::clone(¬ification_generation);
708 let condition = Arc::clone(&condition);
709 let waiter_queued = Arc::clone(&waiter_queued);
710 let waiter_outcome = Arc::clone(&waiter_outcome);
711 thread::spawn(move || {
712 let observed = notification_generation.load(Ordering::Acquire);
713 if condition.load(Ordering::Acquire) {
714 waiter_outcome.store(READY, Ordering::Release);
715 return;
716 }
717
718 let mut queued = waiter_queued.lock().unwrap();
719 if notification_generation.load(Ordering::Acquire) != observed {
720 waiter_outcome.store(RETRY, Ordering::Release);
721 } else {
722 *queued = true;
723 waiter_outcome.store(QUEUED, Ordering::Release);
724 }
725 })
726 };
727 let notifier = {
728 let notification_generation = Arc::clone(¬ification_generation);
729 let condition = Arc::clone(&condition);
730 let waiter_queued = Arc::clone(&waiter_queued);
731 let waiter_woken = Arc::clone(&waiter_woken);
732 thread::spawn(move || {
733 condition.store(true, Ordering::Release);
734 let mut queued = waiter_queued.lock().unwrap();
735 notification_generation.fetch_add(1, Ordering::Release);
736 if *queued {
737 *queued = false;
738 waiter_woken.store(true, Ordering::Release);
739 }
740 })
741 };
742
743 waiter.join().unwrap();
744 notifier.join().unwrap();
745 assert!(condition.load(Ordering::Acquire));
746 if waiter_outcome.load(Ordering::Acquire) == QUEUED {
747 assert!(
748 waiter_woken.load(Ordering::Acquire),
749 "a waiter committed before notification must be selected"
750 );
751 }
752 });
753 }
754}