Skip to content

Commit f47480b

Browse files
committed
Improve windows thread parker.
- Clarify memory ordering and spurious wakeups.
1 parent e990434 commit f47480b

File tree

1 file changed

+34
-24
lines changed

1 file changed

+34
-24
lines changed

library/std/src/sys/windows/thread_parker.rs

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,26 @@ impl Parker {
4949
return;
5050
}
5151

52-
loop {
53-
// Wait for something to happen.
54-
if c::WaitOnAddress::is_available() {
52+
if c::WaitOnAddress::is_available() {
53+
loop {
54+
// Wait for something to happen, assuming it's still set to PARKED.
5555
c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
56-
} else {
57-
c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
58-
}
59-
// Change NOTIFIED=>EMPTY and return in that case.
60-
if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED {
61-
return;
62-
} else {
63-
// Spurious wake up. We loop to try again.
56+
// Change NOTIFIED=>EMPTY but leave PARKED alone.
57+
if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED {
58+
// Actually woken up by unpark().
59+
return;
60+
} else {
61+
// Spurious wake up. We loop to try again.
62+
}
6463
}
64+
} else {
65+
// Wait for unpark() to produce this event.
66+
c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
67+
// Set the state back to EMPTY (from either PARKED or NOTIFIED).
68+
// Note that we don't just write EMPTY, but use swap() to also
69+
// include a acquire-ordered read to synchronize with unpark()'s
70+
// release-ordered write.
71+
self.state.swap(EMPTY, Acquire);
6572
}
6673
}
6774

@@ -77,9 +84,12 @@ impl Parker {
7784
if c::WaitOnAddress::is_available() {
7885
// Wait for something to happen, assuming it's still set to PARKED.
7986
c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
80-
// Change NOTIFIED=>EMPTY and return in that case.
87+
// Set the state back to EMPTY (from either PARKED or NOTIFIED).
88+
// Note that we don't just write EMPTY, but use swap() to also
89+
// include a acquire-ordered read to synchronize with unpark()'s
90+
// release-ordered write.
8191
if self.state.swap(EMPTY, Acquire) == NOTIFIED {
82-
return;
92+
// Actually woken up by unpark().
8393
} else {
8494
// Timeout or spurious wake up.
8595
// We return either way, because we can't easily tell if it was the
@@ -97,17 +107,17 @@ impl Parker {
97107
};
98108

99109
// Wait for unpark() to produce this event.
100-
if c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS {
101-
// Awoken by another thread.
102-
self.state.swap(EMPTY, Acquire);
103-
} else {
104-
// Not awoken by another thread (spurious or timeout).
105-
if self.state.swap(EMPTY, Acquire) == NOTIFIED {
106-
// If the state is NOTIFIED, we *just* missed an unpark(),
107-
// which is now waiting for us to wait for it.
108-
// Wait for it to consume the event and unblock it.
109-
c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
110-
}
110+
let unparked = c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;
111+
112+
// Set the state back to EMPTY (from either PARKED or NOTIFIED).
113+
let prev_state = self.state.swap(EMPTY, Acquire);
114+
115+
if !unparked && prev_state == NOTIFIED {
116+
// We were awoken by a timeout, not by unpark(), but the state
117+
// was set to NOTIFIED, which means we *just* missed an
118+
// unpark(), which is now blocked on us to wait for it.
119+
// Wait for it to consume the event and unblock that thread.
120+
c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
111121
}
112122
}
113123
}

0 commit comments

Comments
 (0)