2 use crate::sync::atomic::AtomicU32;
3 use crate::sync::atomic::Ordering::{Acquire, Release};
4 use crate::sys::futex::{futex_wait, futex_wake};
5 use crate::time::Duration;
7 const PARKED: u32 = u32::MAX;
9 const NOTIFIED: u32 = 1;
15 // Notes about memory ordering:
17 // Memory ordering is only relevant for the relative ordering of operations
18 // between different variables. Even Ordering::Relaxed guarantees a
19 // monotonic/consistent order when looking at just a single atomic variable.
21 // So, since this parker is just a single atomic variable, we only need to look
22 // at the ordering guarantees we need to provide to the 'outside world'.
24 // The only memory ordering guarantee that parking and unparking provide, is
25 // that things which happened before unpark() are visible on the thread
26 // returning from park() afterwards. Otherwise, it was effectively unparked
27 // before unpark() was called while still consuming the 'token'.
29 // In other words, unpark() needs to synchronize with the part of park() that
30 // consumes the token and returns.
32 // This is done with a release-acquire synchronization, by using
33 // Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
34 // Ordering::Acquire when checking for this state in park().
36 /// Construct the futex parker. The UNIX parker implementation
37 /// requires this to happen in-place.
38 pub unsafe fn new(parker: *mut Parker) {
39 parker.write(Self { state: AtomicU32::new(EMPTY) });
42 // Assumes this is only called by the thread that owns the Parker,
43 // which means that `self.state != PARKED`.
44 pub unsafe fn park(self: Pin<&Self>) {
45 // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
47 if self.state.fetch_sub(1, Acquire) == NOTIFIED {
51 // Wait for something to happen, assuming it's still set to PARKED.
52 futex_wait(&self.state, PARKED, None);
53 // Change NOTIFIED=>EMPTY and return in that case.
54 if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() {
57 // Spurious wake up. We loop to try again.
62 // Assumes this is only called by the thread that owns the Parker,
63 // which means that `self.state != PARKED`. This implementation doesn't
64 // require `Pin`, but other implementations do.
65 pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
66 // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
68 if self.state.fetch_sub(1, Acquire) == NOTIFIED {
71 // Wait for something to happen, assuming it's still set to PARKED.
72 futex_wait(&self.state, PARKED, Some(timeout));
73 // This is not just a store, because we need to establish a
74 // release-acquire ordering with unpark().
75 if self.state.swap(EMPTY, Acquire) == NOTIFIED {
76 // Woke up because of unpark().
78 // Timeout or spurious wake up.
79 // We return either way, because we can't easily tell if it was the
84 // This implementation doesn't require `Pin`, but other implementations do.
86 pub fn unpark(self: Pin<&Self>) {
87 // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
88 // wake the thread in the first case.
90 // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
91 // purpose, to make sure every unpark() has a release-acquire ordering
93 if self.state.swap(NOTIFIED, Release) == PARKED {
94 futex_wake(&self.state);