std/sys/sync/condvar/
pthread.rs

1#![forbid(unsafe_op_in_unsafe_fn)]
2
3use crate::pin::Pin;
4use crate::ptr;
5use crate::sync::atomic::Ordering::Relaxed;
6use crate::sync::atomic::{Atomic, AtomicUsize};
7use crate::sys::pal::sync as pal;
8use crate::sys::sync::{Mutex, OnceBox};
9use crate::time::{Duration, Instant};
10
11pub struct Condvar {
12    cvar: OnceBox<pal::Condvar>,
13    mutex: Atomic<usize>,
14}
15
16impl Condvar {
17    pub const fn new() -> Condvar {
18        Condvar { cvar: OnceBox::new(), mutex: AtomicUsize::new(0) }
19    }
20
21    #[inline]
22    fn get(&self) -> Pin<&pal::Condvar> {
23        self.cvar.get_or_init(|| {
24            let mut cvar = Box::pin(pal::Condvar::new());
25            // SAFETY: we only call `init` once per `pal::Condvar`, namely here.
26            unsafe { cvar.as_mut().init() };
27            cvar
28        })
29    }
30
31    #[inline]
32    fn verify(&self, mutex: Pin<&pal::Mutex>) {
33        let addr = ptr::from_ref::<pal::Mutex>(&mutex).addr();
34        // Relaxed is okay here because we never read through `self.mutex`, and only use it to
35        // compare addresses.
36        match self.mutex.compare_exchange(0, addr, Relaxed, Relaxed) {
37            Ok(_) => {}               // Stored the address
38            Err(n) if n == addr => {} // Lost a race to store the same address
39            _ => panic!("attempted to use a condition variable with two mutexes"),
40        }
41    }
42
43    #[inline]
44    pub fn notify_one(&self) {
45        // SAFETY: we called `init` above.
46        unsafe { self.get().notify_one() }
47    }
48
49    #[inline]
50    pub fn notify_all(&self) {
51        // SAFETY: we called `init` above.
52        unsafe { self.get().notify_all() }
53    }
54
55    #[inline]
56    pub unsafe fn wait(&self, mutex: &Mutex) {
57        // SAFETY: the caller guarantees that the lock is owned, thus the mutex
58        // must have been initialized already.
59        let mutex = unsafe { mutex.pal.get_unchecked() };
60        self.verify(mutex);
61        // SAFETY: we called `init` above, we verified that this condition
62        // variable is only used with `mutex` and the caller guarantees that
63        // `mutex` is locked by the current thread.
64        unsafe { self.get().wait(mutex) }
65    }
66
67    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
68        // SAFETY: the caller guarantees that the lock is owned, thus the mutex
69        // must have been initialized already.
70        let mutex = unsafe { mutex.pal.get_unchecked() };
71        self.verify(mutex);
72
73        if pal::Condvar::PRECISE_TIMEOUT {
74            // SAFETY: we called `init` above, we verified that this condition
75            // variable is only used with `mutex` and the caller guarantees that
76            // `mutex` is locked by the current thread.
77            unsafe { self.get().wait_timeout(mutex, dur) }
78        } else {
79            // Timeout reports are not reliable, so do the check ourselves.
80            let now = Instant::now();
81            // SAFETY: we called `init` above, we verified that this condition
82            // variable is only used with `mutex` and the caller guarantees that
83            // `mutex` is locked by the current thread.
84            let woken = unsafe { self.get().wait_timeout(mutex, dur) };
85            woken || now.elapsed() < dur
86        }
87    }
88}