Skip to content

Commit b9660de

Browse files
committed
std: solve priority issue for Parker
1 parent 3b6ae15 commit b9660de

File tree

1 file changed

+31
-24
lines changed

1 file changed

+31
-24
lines changed

library/std/src/sys_common/thread_parker/wait_flag.rs

+31-24
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@
2121
//! "wait flag").
2222
//!
2323
//! `unpark` changes the state to `NOTIFIED`. If the state was `PARKED`, the thread
24-
//! is or will be sleeping on the wait flag, so we raise it. Only the first thread
25-
//! to call `unpark` will raise the wait flag, so spurious wakeups are avoided
26-
//! (this is especially important for semaphores).
24+
//! is or will be sleeping on the wait flag, so we raise it.
2725
2826
use crate::pin::Pin;
2927
use crate::sync::atomic::AtomicI8;
30-
use crate::sync::atomic::Ordering::SeqCst;
28+
use crate::sync::atomic::Ordering::{Relaxed, SeqCst};
3129
use crate::sys::wait_flag::WaitFlag;
3230
use crate::time::Duration;
3331

@@ -49,39 +47,48 @@ impl Parker {
4947

5048
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
5149
pub unsafe fn park(self: Pin<&Self>) {
52-
// The state values are chosen so that this subtraction changes
53-
// `NOTIFIED` to `EMPTY` and `EMPTY` to `PARKED`.
54-
let state = self.state.fetch_sub(1, SeqCst);
55-
match state {
56-
EMPTY => (),
50+
match self.state.fetch_sub(1, SeqCst) {
51+
// NOTIFIED => EMPTY
5752
NOTIFIED => return,
53+
// EMPTY => PARKED
54+
EMPTY => (),
5855
_ => panic!("inconsistent park state"),
5956
}
6057

61-
self.wait_flag.wait();
58+
// Avoid waking up from spurious wakeups (these are quite likely, see below).
59+
loop {
60+
self.wait_flag.wait();
6261

63-
// We need to do a load here to use `Acquire` ordering.
64-
self.state.swap(EMPTY, SeqCst);
62+
match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, Relaxed) {
63+
Ok(_) => return,
64+
Err(PARKED) => (),
65+
Err(_) => panic!("inconsistent park state"),
66+
}
67+
}
6568
}
6669

6770
// This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
6871
pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
69-
let state = self.state.fetch_sub(1, SeqCst);
70-
match state {
71-
EMPTY => (),
72+
match self.state.fetch_sub(1, SeqCst) {
7273
NOTIFIED => return,
74+
EMPTY => (),
7375
_ => panic!("inconsistent park state"),
7476
}
7577

76-
let wakeup = self.wait_flag.wait_timeout(dur);
77-
let state = self.state.swap(EMPTY, SeqCst);
78-
if state == NOTIFIED && !wakeup {
79-
// The token was made available after the wait timed out, but before
80-
// we reset the state, so we need to reset the wait flag to avoid
81-
// spurious wakeups. This wait has no timeout, but we know it will
82-
// return quickly, as the unparking thread will definitely raise the
83-
// flag if it has not already done so.
84-
self.wait_flag.wait();
78+
self.wait_flag.wait_timeout(dur);
79+
80+
// Either a wakeup or a timeout occurred. Wakeups may be spurious, as there can be
81+
// a race condition when `unpark` is performed between receiving the timeout and
82+
// resetting the state, resulting in the eventflag being set unnecessarily. `park`
83+
// is protected against this by looping until the token is actually given, but
84+
// here we cannot easily tell.
85+
86+
// Use `swap` to provide acquire ordering (not strictly necessary, but all other
87+
// implementations do).
88+
match self.state.swap(EMPTY, SeqCst) {
89+
NOTIFIED => (),
90+
PARKED => (),
91+
_ => panic!("inconsistent park state"),
8592
}
8693
}
8794

0 commit comments

Comments
 (0)