-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcondvar.rs
More file actions
124 lines (100 loc) · 3.12 KB
/
condvar.rs
File metadata and controls
124 lines (100 loc) · 3.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
use atomic_wait::{wait, wake_all, wake_one};
use crate::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 notify_one(&self) {
if self.num_waiters.load(Ordering::Relaxed) > 0 {
self.counter.fetch_add(1, Ordering::Relaxed);
wake_one(&self.counter);
}
}
pub fn notify_all(&self) {
if self.num_waiters.load(Ordering::Relaxed) > 0 {
self.counter.fetch_add(1, Ordering::Relaxed);
wake_all(&self.counter);
}
}
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
self.num_waiters.fetch_add(1, Ordering::Relaxed);
let counter_value = self.counter.load(Ordering::Relaxed);
// Unlock the mutex by dropping the guard,
// but remember the mutex so we can lock it again later.
let mutex = guard.mutex;
drop(guard);
// Wait, but only if the counter hasn't changed since unlocking.
wait(&self.counter, counter_value);
self.num_waiters.fetch_sub(1, Ordering::Relaxed);
// If the condition matches, lock the mutex and do biz logic.
mutex.lock()
}
}
impl Default for Condvar {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use std::{collections::VecDeque, thread, time::Duration};
use crate::Mutex;
use super::*;
#[test]
fn condvar_should_work() {
let mutex = Mutex::new(0);
let condvar = Condvar::new();
let mut wakeups = 0;
thread::scope(|s| {
s.spawn(|| {
thread::sleep(Duration::from_secs(1));
*mutex.lock() = 123;
condvar.notify_one();
});
let mut m = mutex.lock();
while *m < 100 {
m = condvar.wait(m);
wakeups += 1;
}
assert_eq!(*m, 123);
});
// Check that the main thread actually did wait(not busy-loop),
// while still allowing for a few spurious wake ups.
assert!(0 < wakeups && wakeups < 10);
}
#[test]
fn condvar_usage() {
let queue = Mutex::new(VecDeque::new());
let not_empty = Condvar::new();
thread::scope(|s| {
s.spawn(|| loop {
let mut q = queue.lock();
let item = loop {
if let Some(item) = q.pop_front() {
break item;
} else {
q = not_empty.wait(q);
}
};
drop(q);
dbg!(item);
if item == 9 {
return;
}
});
for i in 0..10 {
queue.lock().push_back(i);
not_empty.notify_one();
thread::sleep(Duration::from_millis(10));
}
});
}
}