use std::sync::atomic::Ordering::Relaxed;
use std::sync::atomic::{AtomicU32, AtomicUsize};
use atomic_wait::{wait, wake_all, wake_one};
use crate::concurrent::sync::mutex::MutexGuard;
pub struct Condvar {
counter: AtomicU32,
num_waiters: AtomicUsize,
}
impl Condvar {
pub const fn new() -> Self {
Self {
counter: AtomicU32::new(0),
num_waiters: AtomicUsize::new(0),
}
}
pub fn wait<'a, T>(&self, mutex_guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
self.num_waiters.fetch_add(1, Relaxed);
let counter_value = self.counter.load(Relaxed);
let mutex = mutex_guard.mutex;
drop(mutex_guard);
wait(&self.counter, counter_value);
self.num_waiters.fetch_sub(1, Relaxed);
mutex.lock()
}
pub fn notify_one(&self) {
if self.num_waiters.load(Relaxed) > 0 {
self.counter.fetch_add(1, Relaxed);
wake_one(&self.counter)
}
}
pub fn notify_all(&self) {
if self.num_waiters.load(Relaxed) > 0 {
self.counter.fetch_add(1, Relaxed);
wake_all(&self.counter)
}
}
}
#[cfg(test)]
mod tests {
use std::thread;
use std::time::Duration;
use quickcheck_macros::quickcheck;
use crate::concurrent::sync::Mutex;
use super::*;
#[quickcheck]
fn test_condvar() {
let mutex = Mutex::new(0);
let condvar = Condvar::new();
let mut wakeups = 0;
thread::scope(|s| {
s.spawn(|| {
thread::sleep(Duration::from_nanos(10));
*mutex.lock() = 123;
condvar.notify_one();
});
let mut m = mutex.lock();
while *m < 100 {
m = condvar.wait(m);
wakeups += 1;
}
assert_eq!(*m, 123);
});
assert!(wakeups < 10);
}
}