rustix_futex_sync/
futex_condvar.rs

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