use crate::cell::UnsafeCell; use crate::intrinsics::{atomic_cxchg, atomic_load, atomic_xadd, atomic_xchg}; use crate::ptr; use crate::time::Duration; use crate::sys::mutex::{mutex_unlock, Mutex}; use crate::sys::syscall::{futex, TimeSpec, FUTEX_WAIT, FUTEX_WAKE, FUTEX_REQUEUE}; pub struct Condvar { lock: UnsafeCell<*mut i32>, seq: UnsafeCell } impl Condvar { pub const fn new() -> Condvar { Condvar { lock: UnsafeCell::new(ptr::null_mut()), seq: UnsafeCell::new(0) } } #[inline] pub unsafe fn init(&self) { *self.lock.get() = ptr::null_mut(); *self.seq.get() = 0; } #[inline] pub fn notify_one(&self) { unsafe { let seq = self.seq.get(); atomic_xadd(seq, 1); let _ = futex(seq, FUTEX_WAKE, 1, 0, ptr::null_mut()); } } #[inline] pub fn notify_all(&self) { unsafe { let lock = self.lock.get(); let seq = self.seq.get(); if *lock == ptr::null_mut() { return; } atomic_xadd(seq, 1); let _ = futex(seq, FUTEX_REQUEUE, 1, crate::usize::MAX, *lock); } } #[inline] unsafe fn wait_inner(&self, mutex: &Mutex, timeout_ptr: *const TimeSpec) -> bool { let lock = self.lock.get(); let seq = self.seq.get(); if *lock != mutex.lock.get() { if *lock != ptr::null_mut() { panic!("Condvar used with more than one Mutex"); } atomic_cxchg(lock as *mut usize, 0, mutex.lock.get() as usize); } mutex_unlock(*lock); let seq_before = atomic_load(seq); let _ = futex(seq, FUTEX_WAIT, seq_before, timeout_ptr as usize, ptr::null_mut()); let seq_after = atomic_load(seq); while atomic_xchg(*lock, 2) != 0 { let _ = futex(*lock, FUTEX_WAIT, 2, 0, ptr::null_mut()); } seq_before != seq_after } #[inline] pub fn wait(&self, mutex: &Mutex) { unsafe { assert!(self.wait_inner(mutex, ptr::null())); } } #[inline] pub fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { unsafe { let timeout = TimeSpec { tv_sec: dur.as_secs() as i64, tv_nsec: dur.subsec_nanos() as i32 }; self.wait_inner(mutex, &timeout as *const TimeSpec) } } #[inline] pub unsafe fn destroy(&self) { *self.lock.get() = ptr::null_mut(); *self.seq.get() = 0; } } unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {}