use crate::ffi::c_void; use crate::ptr; use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; use crate::sys::hermit::abi; use crate::sys::locks::Mutex; use crate::sys_common::lazy_box::{LazyBox, LazyInit}; use crate::time::Duration; // The implementation is inspired by Andrew D. Birrell's paper // "Implementing Condition Variables with Semaphores" pub struct Condvar { counter: AtomicUsize, sem1: *const c_void, sem2: *const c_void, } pub(crate) type MovableCondvar = LazyBox; impl LazyInit for Condvar { fn init() -> Box { Box::new(Self::new()) } } unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} impl Condvar { pub fn new() -> Self { let mut condvar = Self { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() }; unsafe { let _ = abi::sem_init(&mut condvar.sem1, 0); let _ = abi::sem_init(&mut condvar.sem2, 0); } condvar } pub unsafe fn notify_one(&self) { if self.counter.load(SeqCst) > 0 { self.counter.fetch_sub(1, SeqCst); abi::sem_post(self.sem1); abi::sem_timedwait(self.sem2, 0); } } pub unsafe fn notify_all(&self) { let counter = self.counter.swap(0, SeqCst); for _ in 0..counter { abi::sem_post(self.sem1); } for _ in 0..counter { abi::sem_timedwait(self.sem2, 0); } } pub unsafe fn wait(&self, mutex: &Mutex) { self.counter.fetch_add(1, SeqCst); mutex.unlock(); abi::sem_timedwait(self.sem1, 0); abi::sem_post(self.sem2); mutex.lock(); } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { self.counter.fetch_add(1, SeqCst); mutex.unlock(); let millis = dur.as_millis().min(u32::MAX as u128) as u32; let res = if millis > 0 { abi::sem_timedwait(self.sem1, millis) } else { abi::sem_trywait(self.sem1) }; abi::sem_post(self.sem2); mutex.lock(); res == 0 } } impl Drop for Condvar { fn drop(&mut self) { unsafe { let _ = abi::sem_destroy(self.sem1); let _ = abi::sem_destroy(self.sem2); } } }