Skip to main content

ostd/sync/
wait.rs

1// SPDX-License-Identifier: MPL-2.0
2use vstd::atomic_ghost::*;
3use vstd::prelude::*;
4use vstd::resource::{
5    Loc,
6    ghost_var::{GhostVar, GhostVarAuth},
7};
8use vstd_extra::resource_invariant::ResourceInvariant;
9
10use alloc::{collections::VecDeque, sync::Arc};
11use core::intrinsics::atomic_cxchg;
12use core::sync::atomic::{/*AtomicBool,*/ Ordering};
13
14use super::{LocalIrqDisabled, SpinLock};
15use crate::task::{Task, scheduler};
16
17// # Explanation on the memory orders
18//
19// ```
20// [CPU 1 (the waker)]     [CPU 2 (the waiter)]
21// cond = true;
22// wake_up();
23//                         wait();
24//                         if cond { /* .. */ }
25// ```
26//
27// As soon as the waiter is woken up by the waker, it must see the true condition. This is
28// trivially satisfied if `wake_up()` and `wait()` synchronize with a lock. But if they synchronize
29// with an atomic variable, `wake_up()` must access the variable with `Ordering::Release` and
30// `wait()` must access the variable with `Ordering::Acquire`.
31//
32// Examples of `wake_up()`:
33//  - `WaitQueue::wake_one()`
34//  - `WaitQueue::wake_all()`
35//  - `Waker::wake_up()`
36//
37// Examples of `wait()`:
38//  - `WaitQueue::wait_until()`
39//  - `Waiter::wait()`
40//  - `Waiter::drop()`
41//
42// Note that dropping a waiter must be treated as a `wait()` with zero timeout, because we need to
43// make sure that the wake event isn't lost in this case.
44
45verus! {
46
47struct WakersInvariant;
48
49impl ResourceInvariant<VecDeque<Arc<Waker>>> for WakersInvariant {
50    type Constant = Loc;
51
52    type Resource = GhostVar<int>;
53
54    /// While the spin lock is unlocked, its mirror records the exact queue
55    /// length. Lock acquisition transfers the mirror to the guard, allowing
56    /// the relation to be updated together with `num_wakers` before unlock.
57    closed spec fn inv(ghost_id: Loc, wakers: VecDeque<Arc<Waker>>, mirror: GhostVar<int>) -> bool {
58        &&& mirror.id() == ghost_id
59        &&& mirror@ == wakers@.len()
60    }
61}
62
63struct_with_invariants! {
64
65/// A wait queue.
66///
67/// One may wait on a wait queue to put its executing thread to sleep.
68/// Multiple threads may be the waiters of a wait queue.
69/// Other threads may invoke the `wake`-family methods of a wait queue to
70/// wake up one or many waiting threads.
71pub struct WaitQueue {
72    // A copy of `wakers.len()`, used for the lock-free fast path in `wake_one` and `wake_all`.
73    num_wakers: AtomicU32<_, GhostVarAuth<int>, _>,
74    wakers: SpinLock<VecDeque<Arc<Waker>>, LocalIrqDisabled, WakersInvariant>,
75}
76
77closed spec fn wf(self) -> bool {
78    // The authoritative half agrees with the executable atomic counter. Its
79    // ID links it to the mirror protected by `wakers`.
80    invariant on num_wakers with (wakers) is (v: u32, g: GhostVarAuth<int>) {
81        &&& g.id() == wakers.constant()
82        &&& g@ == v as int
83    }
84}
85}
86
87impl WaitQueue {
88    #[verifier::type_invariant]
89    pub closed spec fn type_inv(self) -> bool {
90        self.wf()
91    }
92}
93
94impl WaitQueue {
95    /// Creates a new, empty wait queue.
96    pub const fn new() -> Self {
97        proof_decl! {
98            let tracked (count_auth, count_mirror) = GhostVarAuth::<int>::new(0int);
99            let ghost ghost_id = count_auth.id();
100        }
101        let wakers = SpinLock::new(VecDeque::new(), Ghost(ghost_id), Tracked(count_mirror));
102        WaitQueue { num_wakers: AtomicU32::new(Ghost(wakers), 0, Tracked(count_auth)), wakers }
103    }
104
105    /// Waits until some condition is met.
106    ///
107    /// This method takes a closure that tests a user-given condition.
108    /// The method only returns if the condition returns `Some(_)`.
109    /// A waker thread should first make the condition `Some(_)`, then invoke the
110    /// `wake`-family method. This ordering is important to ensure that waiter
111    /// threads do not lose any wakeup notifications.
112    ///
113    /// By taking a condition closure, this wait-wakeup mechanism becomes
114    /// more efficient and robust.
115    #[track_caller]
116    #[verus_spec(ret =>
117        requires
118            cond.requires(()),
119        ensures
120            cond.ensures((), Some(ret)),
121    )]
122    #[verifier::exec_allows_no_decreases_clause]
123    pub fn wait_until<F, R>(&self, mut cond: F) -> R where F: FnMut() -> Option<R> {
124        if let Some(res) = cond() {
125            return res;
126        }
127        let (waiter, _) = Waiter::new_pair();
128        #[verus_spec(invariant
129            cond.requires(()),
130        )]
131        loop {
132            self.enqueue(waiter.waker());
133            if let Some(res) = cond() {
134                assert(cond.ensures((), Some(res)));
135                proof! { admit(); }  // FIXME: https://github.com/verus-lang/verus/issues/2295
136                return res;
137            }
138            waiter.wait();
139        }
140    }
141
142    /// Wakes up one waiting thread, if there is one at the point of time when this method is
143    /// called, returning whether such a thread was woken up.
144    #[verifier::exec_allows_no_decreases_clause]
145    pub fn wake_one(&self) -> (r: bool) {
146        proof!{
147            use_type_invariant(self);
148        }
149
150        // Fast path
151        if self.is_empty() {
152            return false;
153        }
154        loop
155            invariant
156                self.wf(),
157        {
158            let mut wakers = self.wakers.lock();
159            let Some(waker) = wakers.pop_front() else {
160                wakers.drop();
161                return false;
162            };
163            proof_decl!{
164                let tracked count_mirror = wakers.tracked_borrow_mut_resource();
165            }
166            atomic_with_ghost! {
167                self.num_wakers => fetch_sub(1);
168                update prev -> next;
169                ghost count_auth => {
170                    count_auth.agree(&*count_mirror);
171                    assert(prev == count_mirror@);
172                    assert(prev > 0);
173                    count_auth.update(count_mirror, next);
174                }
175            };
176            // Avoid holding lock when calling `wake_up`
177            //drop(wakers);
178            wakers.drop();
179
180            if waker.wake_up() {
181                return true;
182            }
183        }
184    }
185
186    /// Wakes up all waiting threads, returning the number of threads that were woken up.
187    #[verifier::exec_allows_no_decreases_clause]
188    pub fn wake_all(&self) -> (r: usize) {
189        proof!{
190            use_type_invariant(self);
191        }
192
193        // Fast path
194        if self.is_empty() {
195            return 0;
196        }
197        let mut num_woken = 0;
198
199        loop
200            invariant
201                self.wf(),
202        {
203            let mut wakers = self.wakers.lock();
204            let Some(waker) = wakers.pop_front() else {
205                wakers.drop();
206                break;
207            };
208            proof_decl!{
209                let tracked count_mirror = wakers.tracked_borrow_mut_resource();
210            }
211            atomic_with_ghost! {
212                self.num_wakers => fetch_sub(1);
213                update prev -> next;
214                ghost count_auth => {
215                    count_auth.agree(&*count_mirror);
216                    assert(prev == count_mirror@);
217                    assert(prev > 0);
218                    count_auth.update(count_mirror, next);
219                }
220            };
221            // Avoid holding lock when calling `wake_up`
222            //drop(wakers);
223            wakers.drop();
224
225            if waker.wake_up() {
226                assume(num_woken < usize::MAX);
227                num_woken += 1;
228            }
229        }
230
231        num_woken
232    }
233
234    fn is_empty(&self) -> bool {
235        proof! {
236            use_type_invariant(self);
237        }
238        self.num_wakers.load() == 0
239    }
240
241    /// Enqueues the input [`Waker`] to the wait queue.
242    #[doc(hidden)]
243    pub fn enqueue(&self, waker: Arc<Waker>) {
244        proof!{
245            use_type_invariant(self);
246        }
247        let mut wakers = self.wakers.lock();
248        wakers.push_back(waker);
249        proof_decl!{
250            let tracked count_mirror = wakers.tracked_borrow_mut_resource();
251        }
252        atomic_with_ghost! {
253            self.num_wakers => fetch_add(1);
254            update prev -> next;
255            ghost count_auth => {
256                count_auth.agree(&*count_mirror);
257                assert(prev == count_mirror@);
258                assume(prev < u32::MAX);
259                count_auth.update(count_mirror, next);
260            }
261        };
262        wakers.drop();
263    }
264}
265
266impl Default for WaitQueue {
267    fn default() -> Self {
268        Self::new()
269    }
270}
271
272/// A waiter that can put the current thread to sleep until it is woken up by the associated
273/// [`Waker`].
274///
275/// By definition, a waiter belongs to the current thread, so it cannot be sent to another thread
276/// and its reference cannot be shared between threads.
277pub struct Waiter {
278    waker: Arc<Waker>,
279}
280
281impl !Send for Waiter {
282
283}
284
285impl !Sync for Waiter {
286
287}
288
289impl Waiter {
290    /// Checks if the input waker is the associated waker of the current waiter.
291    pub closed spec fn rel_waker(self, waker: Arc<Waker>) -> bool {
292        self.waker == waker
293    }
294}
295
296struct_with_invariants! {
297/// A waker that can wake up the associated [`Waiter`].
298///
299/// A waker can be created by calling [`Waiter::new_pair`]. This method creates an `Arc<Waker>` that can
300/// be used across different threads.
301pub struct Waker {
302    has_woken: AtomicBool<_, (), _>, // It should attach a task-related token once we start to verify the scheduler, using () as a placeholder for now.
303    task: Arc<Task>,
304}
305
306closed spec fn wf(self) -> bool {
307    invariant on has_woken is (v: bool, g: ()) {
308        true
309    }
310}
311}
312
313impl Waker {
314    #[verifier::type_invariant]
315    pub closed spec fn type_inv(self) -> bool {
316        self.wf()
317    }
318}
319
320#[verus_verify]
321impl Waiter {
322    /// Creates a waiter and its associated [`Waker`].
323    #[verus_spec(ret =>
324        ensures
325            ret.0.rel_waker(ret.1),
326    )]
327    pub fn new_pair() -> (Self, Arc<Waker>) {
328        proof_decl! {
329            let ghost waker_id: int = arbitrary();
330        }
331        let waker = Arc::new(
332            Waker {
333                has_woken: AtomicBool::new(Ghost(()), false, Tracked(())),
334                // task: Task::current().unwrap().cloned(),
335                task: Arc::new(Task {  }),
336            },
337        );
338        let waiter = Self { waker: waker.clone() };
339        (waiter, waker)
340    }
341
342    /// Waits until the waiter is woken up by calling [`Waker::wake_up`] on the associated
343    /// [`Waker`].
344    ///
345    /// This method returns immediately if the waiter has been woken since the end of the last call
346    /// to this method (or since the waiter was created, if this method has not been called
347    /// before). Otherwise, it puts the current thread to sleep until the waiter is woken up.
348    #[track_caller]
349    pub fn wait(&self) {
350        self.waker.do_wait();
351    }
352
353    /// Waits until some condition is met or the cancel condition becomes true.
354    ///
355    /// This method will return `Ok(_)` if the condition returns `Some(_)`, and will stop waiting
356    /// if the cancel condition returns `Err(_)`. In this situation, this method will return the `Err(_)`
357    /// generated by the cancel condition.
358    #[verus_spec(ret =>
359        requires
360            cond.requires(()),
361            cancel_cond.requires(()),
362        ensures
363            match ret {
364                Ok(res) => cond.ensures((),Some(res)),
365                Err(e) => cancel_cond.ensures((), Err(e)),
366            },
367    )]
368    #[track_caller]
369    #[verifier::exec_allows_no_decreases_clause]
370    pub fn wait_until_or_cancelled<F, R, FCancel, E>(
371        &self,
372        mut cond: F,
373        cancel_cond: FCancel,
374    ) -> core::result::Result<R, E> where
375        F: FnMut() -> Option<R>,
376        FCancel: Fn() -> core::result::Result<(), E>,
377     {
378        let mut cond = cond;
379        #[verus_spec(invariant
380            cond.requires(()),
381            cancel_cond.requires(()),
382        )]
383        loop {
384            if let Some(res) = cond() {
385                assert(cond.ensures((), Some(res))) by {
386                    admit();
387                };  // FIXME:
388                proof! { admit(); }  // FIXME: https://github.com/verus-lang/verus/issues/2295
389                return Ok(res);
390            };
391            if let Err(e) = cancel_cond() {
392                // Close the waker and check again to avoid missing a wake event.
393                self.waker.close();
394                proof! { admit(); }  // FIXME: https://github.com/verus-lang/verus/issues/2295
395                return cond().ok_or(e);
396            }
397            self.wait();
398        }
399    }
400
401    /// Gets the associated [`Waker`] of the current waiter.
402    #[verus_spec(ret =>
403        ensures
404            self.rel_waker(ret),
405    )]
406    pub fn waker(&self) -> Arc<Waker> {
407        self.waker.clone()
408    }
409
410    /// Returns the task that the associated waker will attempt to wake up.
411    pub fn task(&self) -> &Arc<Task> {
412        &self.waker.task
413    }
414}
415
416/*impl Drop for Waiter {
417    #[verifier::external_body]
418    fn drop(&mut self)
419        opens_invariants none
420        no_unwind
421    {
422        // When dropping the waiter, we need to close the waker to ensure that if someone wants to
423        // wake up the waiter afterwards, they will perform a no-op.
424        self.waker.close();
425    }
426}*/
427
428impl Waiter {
429    /// VERUS LIMITATION: We implement `drop` and call it manually because Verus's support for `Drop` is incomplete for now.
430    pub fn drop(self) {
431        // When dropping the waiter, we need to close the waker to ensure that if someone wants to
432        // wake up the waiter afterwards, they will perform a no-op.
433        self.waker.close();
434    }
435}
436
437impl Waker {
438    /// Wakes up the associated [`Waiter`].
439    ///
440    /// This method returns `true` if the waiter is woken by this call. It returns `false` if the
441    /// waiter has already been woken by a previous call to the method, or if the waiter has been
442    /// dropped.
443    ///
444    /// Note that if this method returns `true`, it implies that the wake event will be properly
445    /// delivered, _or_ that the waiter will be dropped after being woken. It's up to the caller to
446    /// handle the latter case properly to avoid missing the wake event.
447    #[verifier::external_body]
448    pub fn wake_up(&self) -> bool {
449        /*if self.has_woken.swap(true, Ordering::Release) {
450            return false;
451        }
452        scheduler::unpark_target(self.task.clone());
453
454        true*/
455        unimplemented!()
456    }
457
458    #[track_caller]
459    #[verifier::external_body]
460    fn do_wait(&self) {
461        /*while !self.has_woken.swap(false, Ordering::Acquire) {
462            scheduler::park_current(|| self.has_woken.load(Ordering::Acquire));
463        }*/
464        unimplemented!()
465    }
466
467    fn close(&self) {
468        // This must use `Ordering::Acquire`, although we do not care about the return value. See
469        // the memory order explanation at the top of the file for details.
470        //let _ = self.has_woken.swap(true, Ordering::Acquire);
471        proof!{ use_type_invariant(self);}
472        let _ =
473            atomic_with_ghost!{
474            self.has_woken => swap(true);
475            update prev -> next;
476            ghost g => {}
477        };
478    }
479}
480
481} // verus!
482#[cfg(ktest)]
483mod test {
484    use super::*;
485    use crate::{prelude::*, task::TaskOptions};
486
487    fn queue_wake<F>(wake: F)
488    where
489        F: Fn(&WaitQueue) + Sync + Send + 'static,
490    {
491        let queue = Arc::new(WaitQueue::new());
492        let queue_cloned = queue.clone();
493
494        let cond = Arc::new(AtomicBool::new(false));
495        let cond_cloned = cond.clone();
496
497        TaskOptions::new(move || {
498            Task::yield_now();
499
500            cond_cloned.store(true, Ordering::Relaxed);
501            wake(&queue_cloned);
502        })
503        .data(())
504        .spawn()
505        .unwrap();
506
507        queue.wait_until(|| cond.load(Ordering::Relaxed).then_some(()));
508
509        assert!(cond.load(Ordering::Relaxed));
510    }
511
512    #[ktest]
513    fn queue_wake_one() {
514        queue_wake(|queue| {
515            queue.wake_one();
516        });
517    }
518
519    #[ktest]
520    fn queue_wake_all() {
521        queue_wake(|queue| {
522            queue.wake_all();
523        });
524    }
525
526    #[ktest]
527    fn waiter_wake_twice() {
528        let (_waiter, waker) = Waiter::new_pair();
529
530        assert!(waker.wake_up());
531        assert!(!waker.wake_up());
532    }
533
534    #[ktest]
535    fn waiter_wake_drop() {
536        let (waiter, waker) = Waiter::new_pair();
537
538        drop(waiter);
539        assert!(!waker.wake_up());
540    }
541
542    #[ktest]
543    fn waiter_wake_async() {
544        let (waiter, waker) = Waiter::new_pair();
545
546        let cond = Arc::new(AtomicBool::new(false));
547        let cond_cloned = cond.clone();
548
549        TaskOptions::new(move || {
550            Task::yield_now();
551
552            cond_cloned.store(true, Ordering::Relaxed);
553            assert!(waker.wake_up());
554        })
555        .data(())
556        .spawn()
557        .unwrap();
558
559        waiter.wait();
560
561        assert!(cond.load(Ordering::Relaxed));
562    }
563
564    #[ktest]
565    fn waiter_wake_reorder() {
566        let (waiter, waker) = Waiter::new_pair();
567
568        let cond = Arc::new(AtomicBool::new(false));
569        let cond_cloned = cond.clone();
570
571        let (waiter2, waker2) = Waiter::new_pair();
572
573        let cond2 = Arc::new(AtomicBool::new(false));
574        let cond2_cloned = cond2.clone();
575
576        TaskOptions::new(move || {
577            Task::yield_now();
578
579            cond2_cloned.store(true, Ordering::Relaxed);
580            assert!(waker2.wake_up());
581
582            Task::yield_now();
583
584            cond_cloned.store(true, Ordering::Relaxed);
585            assert!(waker.wake_up());
586        })
587        .data(())
588        .spawn()
589        .unwrap();
590
591        waiter.wait();
592        assert!(cond.load(Ordering::Relaxed));
593
594        waiter2.wait();
595        assert!(cond2.load(Ordering::Relaxed));
596    }
597}