102 lines
2.9 KiB
Rust
102 lines
2.9 KiB
Rust
// Used design from https://www.remlab.net/op/futex-condvar.shtml
|
|
|
|
use crate::{
|
|
error::Errno,
|
|
header::{pthread::*, time::timespec},
|
|
};
|
|
|
|
use core::sync::atomic::{AtomicU32 as AtomicUint, Ordering};
|
|
|
|
pub struct Cond {
|
|
cur: AtomicUint,
|
|
prev: AtomicUint,
|
|
}
|
|
|
|
type Result<T, E = Errno> = core::result::Result<T, E>;
|
|
|
|
impl Cond {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
cur: AtomicUint::new(0),
|
|
prev: AtomicUint::new(0),
|
|
}
|
|
}
|
|
fn wake(&self, count: i32) -> Result<(), Errno> {
|
|
// This is formally correct as long as we don't have more than u32::MAX threads.
|
|
let prev = self.prev.load(Ordering::Relaxed);
|
|
self.cur.store(prev.wrapping_add(1), Ordering::Relaxed);
|
|
|
|
crate::sync::futex_wake(&self.cur, count);
|
|
Ok(())
|
|
}
|
|
pub fn broadcast(&self) -> Result<(), Errno> {
|
|
self.wake(i32::MAX)
|
|
}
|
|
pub fn signal(&self) -> Result<(), Errno> {
|
|
self.broadcast()
|
|
//self.wake(1)
|
|
}
|
|
pub fn timedwait(&self, mutex: &RlctMutex, timeout: ×pec) -> Result<(), Errno> {
|
|
self.wait_inner(mutex, Some(timeout))
|
|
}
|
|
fn wait_inner(&self, mutex: &RlctMutex, timeout: Option<×pec>) -> Result<(), Errno> {
|
|
self.wait_inner_generic(
|
|
|| mutex.unlock(),
|
|
|| mutex.lock(),
|
|
|timeout| mutex.lock_with_timeout(timeout),
|
|
timeout,
|
|
)
|
|
}
|
|
pub fn wait_inner_typedmutex<'lock, T>(
|
|
&self,
|
|
guard: crate::sync::MutexGuard<'lock, T>,
|
|
) -> crate::sync::MutexGuard<'lock, T> {
|
|
let mut newguard = None;
|
|
let lock = guard.mutex;
|
|
self.wait_inner_generic(
|
|
move || {
|
|
drop(guard);
|
|
Ok(())
|
|
},
|
|
|| {
|
|
newguard = Some(lock.lock());
|
|
Ok(())
|
|
},
|
|
|_| unreachable!(),
|
|
None,
|
|
)
|
|
.unwrap();
|
|
newguard.unwrap()
|
|
}
|
|
// TODO: FUTEX_REQUEUE
|
|
fn wait_inner_generic(
|
|
&self,
|
|
unlock: impl FnOnce() -> Result<()>,
|
|
lock: impl FnOnce() -> Result<()>,
|
|
lock_with_timeout: impl FnOnce(×pec) -> Result<()>,
|
|
deadline: Option<×pec>,
|
|
) -> Result<(), Errno> {
|
|
// TODO: Error checking for certain types (i.e. robust and errorcheck) of mutexes, e.g. if the
|
|
// mutex is not locked.
|
|
let current = self.cur.load(Ordering::Relaxed);
|
|
self.prev.store(current, Ordering::Relaxed);
|
|
|
|
unlock();
|
|
|
|
match deadline {
|
|
Some(deadline) => {
|
|
crate::sync::futex_wait(&self.cur, current, Some(&deadline));
|
|
lock_with_timeout(deadline);
|
|
}
|
|
None => {
|
|
crate::sync::futex_wait(&self.cur, current, None);
|
|
lock();
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
pub fn wait(&self, mutex: &RlctMutex) -> Result<(), Errno> {
|
|
self.wait_inner(mutex, None)
|
|
}
|
|
}
|