1 | use crate::sync::atomic::{AtomicU32, Ordering::Relaxed}; |
2 | use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; |
3 | use crate::sys::sync::Mutex; |
4 | use crate::time::Duration; |
5 | |
6 | pub struct Condvar { |
7 | // The value of this atomic is simply incremented on every notification. |
8 | // This is used by `.wait()` to not miss any notifications after |
9 | // unlocking the mutex and before waiting for notifications. |
10 | futex: AtomicU32, |
11 | } |
12 | |
13 | impl Condvar { |
14 | #[inline ] |
15 | pub const fn new() -> Self { |
16 | Self { futex: AtomicU32::new(0) } |
17 | } |
18 | |
19 | // All the memory orderings here are `Relaxed`, |
20 | // because synchronization is done by unlocking and locking the mutex. |
21 | |
22 | pub fn notify_one(&self) { |
23 | self.futex.fetch_add(1, Relaxed); |
24 | futex_wake(&self.futex); |
25 | } |
26 | |
27 | pub fn notify_all(&self) { |
28 | self.futex.fetch_add(1, Relaxed); |
29 | futex_wake_all(&self.futex); |
30 | } |
31 | |
32 | pub unsafe fn wait(&self, mutex: &Mutex) { |
33 | self.wait_optional_timeout(mutex, None); |
34 | } |
35 | |
36 | pub unsafe fn wait_timeout(&self, mutex: &Mutex, timeout: Duration) -> bool { |
37 | self.wait_optional_timeout(mutex, Some(timeout)) |
38 | } |
39 | |
40 | unsafe fn wait_optional_timeout(&self, mutex: &Mutex, timeout: Option<Duration>) -> bool { |
41 | // Examine the notification counter _before_ we unlock the mutex. |
42 | let futex_value = self.futex.load(Relaxed); |
43 | |
44 | // Unlock the mutex before going to sleep. |
45 | mutex.unlock(); |
46 | |
47 | // Wait, but only if there hasn't been any |
48 | // notification since we unlocked the mutex. |
49 | let r = futex_wait(&self.futex, futex_value, timeout); |
50 | |
51 | // Lock the mutex again. |
52 | mutex.lock(); |
53 | |
54 | r |
55 | } |
56 | } |
57 | |