Skip to main content

ostd/sync/
wait.rs

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