| #![forbid(unsafe_op_in_unsafe_fn)] |
| |
| use crate::pin::Pin; |
| use crate::ptr; |
| use crate::sync::atomic::Ordering::Relaxed; |
| use crate::sync::atomic::{Atomic, AtomicUsize}; |
| use crate::sys::pal::sync as pal; |
| use crate::sys::sync::{Mutex, OnceBox}; |
| use crate::time::{Duration, Instant}; |
| |
| pub struct Condvar { |
| cvar: OnceBox<pal::Condvar>, |
| mutex: Atomic<usize>, |
| } |
| |
| impl Condvar { |
| pub const fn new() -> Condvar { |
| Condvar { cvar: OnceBox::new(), mutex: AtomicUsize::new(0) } |
| } |
| |
| #[inline] |
| fn get(&self) -> Pin<&pal::Condvar> { |
| self.cvar.get_or_init(|| { |
| let mut cvar = Box::pin(pal::Condvar::new()); |
| // SAFETY: we only call `init` once per `pal::Condvar`, namely here. |
| unsafe { cvar.as_mut().init() }; |
| cvar |
| }) |
| } |
| |
| #[inline] |
| fn verify(&self, mutex: Pin<&pal::Mutex>) { |
| let addr = ptr::from_ref::<pal::Mutex>(&mutex).addr(); |
| // Relaxed is okay here because we never read through `self.mutex`, and only use it to |
| // compare addresses. |
| match self.mutex.compare_exchange(0, addr, Relaxed, Relaxed) { |
| Ok(_) => {} // Stored the address |
| Err(n) if n == addr => {} // Lost a race to store the same address |
| _ => panic!("attempted to use a condition variable with two mutexes"), |
| } |
| } |
| |
| #[inline] |
| pub fn notify_one(&self) { |
| // SAFETY: we called `init` above. |
| unsafe { self.get().notify_one() } |
| } |
| |
| #[inline] |
| pub fn notify_all(&self) { |
| // SAFETY: we called `init` above. |
| unsafe { self.get().notify_all() } |
| } |
| |
| #[inline] |
| pub unsafe fn wait(&self, mutex: &Mutex) { |
| // SAFETY: the caller guarantees that the lock is owned, thus the mutex |
| // must have been initialized already. |
| let mutex = unsafe { mutex.pal.get_unchecked() }; |
| self.verify(mutex); |
| // SAFETY: we called `init` above, we verified that this condition |
| // variable is only used with `mutex` and the caller guarantees that |
| // `mutex` is locked by the current thread. |
| unsafe { self.get().wait(mutex) } |
| } |
| |
| pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { |
| // SAFETY: the caller guarantees that the lock is owned, thus the mutex |
| // must have been initialized already. |
| let mutex = unsafe { mutex.pal.get_unchecked() }; |
| self.verify(mutex); |
| |
| if pal::Condvar::PRECISE_TIMEOUT { |
| // SAFETY: we called `init` above, we verified that this condition |
| // variable is only used with `mutex` and the caller guarantees that |
| // `mutex` is locked by the current thread. |
| unsafe { self.get().wait_timeout(mutex, dur) } |
| } else { |
| // Timeout reports are not reliable, so do the check ourselves. |
| let now = Instant::now(); |
| // SAFETY: we called `init` above, we verified that this condition |
| // variable is only used with `mutex` and the caller guarantees that |
| // `mutex` is locked by the current thread. |
| let woken = unsafe { self.get().wait_timeout(mutex, dur) }; |
| woken || now.elapsed() < dur |
| } |
| } |
| } |