use crate::cell::UnsafeCell; use crate::mem; use crate::sync::atomic::{AtomicU32, Ordering}; use crate::sys::cloudabi::abi; use crate::sys::mutex::{self, Mutex}; use crate::sys::time::checked_dur2intervals; use crate::time::Duration; extern "C" { #[thread_local] static __pthread_thread_id: abi::tid; } pub struct Condvar { condvar: UnsafeCell, } unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} const NEW: Condvar = Condvar { condvar: UnsafeCell::new(AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0)) }; impl Condvar { pub const fn new() -> Condvar { NEW } pub unsafe fn init(&mut self) {} pub unsafe fn notify_one(&self) { let condvar = self.condvar.get(); if (*condvar).load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 { let ret = abi::condvar_signal(condvar as *mut abi::condvar, abi::scope::PRIVATE, 1); assert_eq!(ret, abi::errno::SUCCESS, "Failed to signal on condition variable"); } } pub unsafe fn notify_all(&self) { let condvar = self.condvar.get(); if (*condvar).load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 { let ret = abi::condvar_signal( condvar as *mut abi::condvar, abi::scope::PRIVATE, abi::nthreads::MAX, ); assert_eq!(ret, abi::errno::SUCCESS, "Failed to broadcast on condition variable"); } } pub unsafe fn wait(&self, mutex: &Mutex) { let mutex = mutex::raw(mutex); assert_eq!( (*mutex).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, "This lock is not write-locked by this thread" ); // Call into the kernel to wait on the condition variable. let condvar = self.condvar.get(); let subscription = abi::subscription { type_: abi::eventtype::CONDVAR, union: abi::subscription_union { condvar: abi::subscription_condvar { condvar: condvar as *mut abi::condvar, condvar_scope: abi::scope::PRIVATE, lock: mutex as *mut abi::lock, lock_scope: abi::scope::PRIVATE, }, }, ..mem::zeroed() }; let mut event: mem::MaybeUninit = mem::MaybeUninit::uninit(); let mut nevents: mem::MaybeUninit = mem::MaybeUninit::uninit(); let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr()); assert_eq!(ret, abi::errno::SUCCESS, "Failed to wait on condition variable"); assert_eq!( event.assume_init().error, abi::errno::SUCCESS, "Failed to wait on condition variable" ); } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { let mutex = mutex::raw(mutex); assert_eq!( (*mutex).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, "This lock is not write-locked by this thread" ); // Call into the kernel to wait on the condition variable. let condvar = self.condvar.get(); let timeout = checked_dur2intervals(&dur).expect("overflow converting duration to nanoseconds"); let subscriptions = [ abi::subscription { type_: abi::eventtype::CONDVAR, union: abi::subscription_union { condvar: abi::subscription_condvar { condvar: condvar as *mut abi::condvar, condvar_scope: abi::scope::PRIVATE, lock: mutex as *mut abi::lock, lock_scope: abi::scope::PRIVATE, }, }, ..mem::zeroed() }, abi::subscription { type_: abi::eventtype::CLOCK, union: abi::subscription_union { clock: abi::subscription_clock { clock_id: abi::clockid::MONOTONIC, timeout, ..mem::zeroed() }, }, ..mem::zeroed() }, ]; let mut events: [mem::MaybeUninit; 2] = [mem::MaybeUninit::uninit(); 2]; let mut nevents: mem::MaybeUninit = mem::MaybeUninit::uninit(); let ret = abi::poll( subscriptions.as_ptr(), mem::MaybeUninit::first_ptr_mut(&mut events), 2, nevents.as_mut_ptr(), ); assert_eq!(ret, abi::errno::SUCCESS, "Failed to wait on condition variable"); let nevents = nevents.assume_init(); for i in 0..nevents { assert_eq!( events[i].assume_init().error, abi::errno::SUCCESS, "Failed to wait on condition variable" ); if events[i].assume_init().type_ == abi::eventtype::CONDVAR { return true; } } false } pub unsafe fn destroy(&self) { let condvar = self.condvar.get(); assert_eq!( (*condvar).load(Ordering::Relaxed), abi::CONDVAR_HAS_NO_WAITERS.0, "Attempted to destroy a condition variable with blocked threads" ); } }