1
Fork 0

Improve windows thread parker.

- Clarify memory ordering and spurious wakeups.
This commit is contained in:
Mara Bos 2020-10-06 12:40:55 +02:00
parent e9904342eb
commit f47480b8ac

View file

@ -49,19 +49,26 @@ impl Parker {
return; return;
} }
loop { if c::WaitOnAddress::is_available() {
// Wait for something to happen. loop {
if c::WaitOnAddress::is_available() { // Wait for something to happen, assuming it's still set to PARKED.
c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE); c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
} else { // Change NOTIFIED=>EMPTY but leave PARKED alone.
c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut()); if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED {
} // Actually woken up by unpark().
// Change NOTIFIED=>EMPTY and return in that case. return;
if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED { } else {
return; // Spurious wake up. We loop to try again.
} else { }
// Spurious wake up. We loop to try again.
} }
} else {
// Wait for unpark() to produce this event.
c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
// Set the state back to EMPTY (from either PARKED or NOTIFIED).
// Note that we don't just write EMPTY, but use swap() to also
// include a acquire-ordered read to synchronize with unpark()'s
// release-ordered write.
self.state.swap(EMPTY, Acquire);
} }
} }
@ -77,9 +84,12 @@ impl Parker {
if c::WaitOnAddress::is_available() { if c::WaitOnAddress::is_available() {
// Wait for something to happen, assuming it's still set to PARKED. // Wait for something to happen, assuming it's still set to PARKED.
c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout)); c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
// Change NOTIFIED=>EMPTY and return in that case. // Set the state back to EMPTY (from either PARKED or NOTIFIED).
// Note that we don't just write EMPTY, but use swap() to also
// include a acquire-ordered read to synchronize with unpark()'s
// release-ordered write.
if self.state.swap(EMPTY, Acquire) == NOTIFIED { if self.state.swap(EMPTY, Acquire) == NOTIFIED {
return; // Actually woken up by unpark().
} else { } else {
// Timeout or spurious wake up. // Timeout or spurious wake up.
// We return either way, because we can't easily tell if it was the // We return either way, because we can't easily tell if it was the
@ -97,17 +107,17 @@ impl Parker {
}; };
// Wait for unpark() to produce this event. // Wait for unpark() to produce this event.
if c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS { let unparked = c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;
// Awoken by another thread.
self.state.swap(EMPTY, Acquire); // Set the state back to EMPTY (from either PARKED or NOTIFIED).
} else { let prev_state = self.state.swap(EMPTY, Acquire);
// Not awoken by another thread (spurious or timeout).
if self.state.swap(EMPTY, Acquire) == NOTIFIED { if !unparked && prev_state == NOTIFIED {
// If the state is NOTIFIED, we *just* missed an unpark(), // We were awoken by a timeout, not by unpark(), but the state
// which is now waiting for us to wait for it. // was set to NOTIFIED, which means we *just* missed an
// Wait for it to consume the event and unblock it. // unpark(), which is now blocked on us to wait for it.
c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut()); // Wait for it to consume the event and unblock that thread.
} c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
} }
} }
} }