]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/windows/thread_parking.rs
Port pgo.sh to Python
[rust.git] / library / std / src / sys / windows / thread_parking.rs
1 // Thread parker implementation for Windows.
2 //
3 // This uses WaitOnAddress and WakeByAddressSingle if available (Windows 8+).
4 // This modern API is exactly the same as the futex syscalls the Linux thread
5 // parker uses. When These APIs are available, the implementation of this
6 // thread parker matches the Linux thread parker exactly.
7 //
8 // However, when the modern API is not available, this implementation falls
9 // back to NT Keyed Events, which are similar, but have some important
10 // differences. These are available since Windows XP.
11 //
12 // WaitOnAddress first checks the state of the thread parker to make sure it no
13 // WakeByAddressSingle calls can be missed between updating the parker state
14 // and calling the function.
15 //
16 // NtWaitForKeyedEvent does not have this option, and unconditionally blocks
17 // without checking the parker state first. Instead, NtReleaseKeyedEvent
18 // (unlike WakeByAddressSingle) *blocks* until it woke up a thread waiting for
19 // it by NtWaitForKeyedEvent. This way, we can be sure no events are missed,
20 // but we need to be careful not to block unpark() if park_timeout() was woken
21 // up by a timeout instead of unpark().
22 //
23 // Unlike WaitOnAddress, NtWaitForKeyedEvent/NtReleaseKeyedEvent operate on a
24 // HANDLE (created with NtCreateKeyedEvent). This means that we can be sure
25 // a successfully awoken park() was awoken by unpark() and not a
26 // NtReleaseKeyedEvent call from some other code, as these events are not only
27 // matched by the key (address of the parker (state)), but also by this HANDLE.
28 // We lazily allocate this handle the first time it is needed.
29 //
30 // The fast path (calling park() after unpark() was already called) and the
31 // possible states are the same for both implementations. This is used here to
32 // make sure the fast path does not even check which API to use, but can return
33 // right away, independent of the used API. Only the slow paths (which will
34 // actually block/wake a thread) check which API is available and have
35 // different implementations.
36 //
37 // Unfortunately, NT Keyed Events are an undocumented Windows API. However:
38 // - This API is relatively simple with obvious behaviour, and there are
39 //   several (unofficial) articles documenting the details. [1]
40 // - `parking_lot` has been using this API for years (on Windows versions
41 //   before Windows 8). [2] Many big projects extensively use parking_lot,
42 //   such as servo and the Rust compiler itself.
43 // - It is the underlying API used by Windows SRW locks and Windows critical
44 //   sections. [3] [4]
45 // - The source code of the implementations of Wine, ReactOs, and Windows XP
46 //   are available and match the expected behaviour.
47 // - The main risk with an undocumented API is that it might change in the
48 //   future. But since we only use it for older versions of Windows, that's not
49 //   a problem.
50 // - Even if these functions do not block or wake as we expect (which is
51 //   unlikely, see all previous points), this implementation would still be
52 //   memory safe. The NT Keyed Events API is only used to sleep/block in the
53 //   right place.
54 //
55 // [1]: http://www.locklessinc.com/articles/keyed_events/
56 // [2]: https://github.com/Amanieu/parking_lot/commit/43abbc964e
57 // [3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c
58 // [4]: Windows Internals, Part 1, ISBN 9780735671300
59
60 use crate::pin::Pin;
61 use crate::ptr;
62 use crate::sync::atomic::{
63     AtomicI8, AtomicPtr,
64     Ordering::{Acquire, Relaxed, Release},
65 };
66 use crate::sys::{c, dur2timeout};
67 use crate::time::Duration;
68
69 pub struct Parker {
70     state: AtomicI8,
71 }
72
73 const PARKED: i8 = -1;
74 const EMPTY: i8 = 0;
75 const NOTIFIED: i8 = 1;
76
77 // Notes about memory ordering:
78 //
79 // Memory ordering is only relevant for the relative ordering of operations
80 // between different variables. Even Ordering::Relaxed guarantees a
81 // monotonic/consistent order when looking at just a single atomic variable.
82 //
83 // So, since this parker is just a single atomic variable, we only need to look
84 // at the ordering guarantees we need to provide to the 'outside world'.
85 //
86 // The only memory ordering guarantee that parking and unparking provide, is
87 // that things which happened before unpark() are visible on the thread
88 // returning from park() afterwards. Otherwise, it was effectively unparked
89 // before unpark() was called while still consuming the 'token'.
90 //
91 // In other words, unpark() needs to synchronize with the part of park() that
92 // consumes the token and returns.
93 //
94 // This is done with a release-acquire synchronization, by using
95 // Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
96 // Ordering::Acquire when reading this state in park() after waking up.
97 impl Parker {
98     /// Construct the Windows parker. The UNIX parker implementation
99     /// requires this to happen in-place.
100     pub unsafe fn new_in_place(parker: *mut Parker) {
101         parker.write(Self { state: AtomicI8::new(EMPTY) });
102     }
103
104     // Assumes this is only called by the thread that owns the Parker,
105     // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
106     // but other implementations do.
107     pub unsafe fn park(self: Pin<&Self>) {
108         // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
109         // first case.
110         if self.state.fetch_sub(1, Acquire) == NOTIFIED {
111             return;
112         }
113
114         if let Some(wait_on_address) = c::WaitOnAddress::option() {
115             loop {
116                 // Wait for something to happen, assuming it's still set to PARKED.
117                 wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
118                 // Change NOTIFIED=>EMPTY but leave PARKED alone.
119                 if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() {
120                     // Actually woken up by unpark().
121                     return;
122                 } else {
123                     // Spurious wake up. We loop to try again.
124                 }
125             }
126         } else {
127             // Wait for unpark() to produce this event.
128             c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
129             // Set the state back to EMPTY (from either PARKED or NOTIFIED).
130             // Note that we don't just write EMPTY, but use swap() to also
131             // include an acquire-ordered read to synchronize with unpark()'s
132             // release-ordered write.
133             self.state.swap(EMPTY, Acquire);
134         }
135     }
136
137     // Assumes this is only called by the thread that owns the Parker,
138     // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
139     // but other implementations do.
140     pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
141         // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
142         // first case.
143         if self.state.fetch_sub(1, Acquire) == NOTIFIED {
144             return;
145         }
146
147         if let Some(wait_on_address) = c::WaitOnAddress::option() {
148             // Wait for something to happen, assuming it's still set to PARKED.
149             wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
150             // Set the state back to EMPTY (from either PARKED or NOTIFIED).
151             // Note that we don't just write EMPTY, but use swap() to also
152             // include an acquire-ordered read to synchronize with unpark()'s
153             // release-ordered write.
154             if self.state.swap(EMPTY, Acquire) == NOTIFIED {
155                 // Actually woken up by unpark().
156             } else {
157                 // Timeout or spurious wake up.
158                 // We return either way, because we can't easily tell if it was the
159                 // timeout or not.
160             }
161         } else {
162             // Need to wait for unpark() using NtWaitForKeyedEvent.
163             let handle = keyed_event_handle();
164
165             // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
166             // values to indicate a relative time on the monotonic clock.
167             // This is documented here for the underlying KeWaitForSingleObject function:
168             // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
169             let mut timeout = match i64::try_from((timeout.as_nanos() + 99) / 100) {
170                 Ok(t) => -t,
171                 Err(_) => i64::MIN,
172             };
173
174             // Wait for unpark() to produce this event.
175             let unparked =
176                 c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;
177
178             // Set the state back to EMPTY (from either PARKED or NOTIFIED).
179             let prev_state = self.state.swap(EMPTY, Acquire);
180
181             if !unparked && prev_state == NOTIFIED {
182                 // We were awoken by a timeout, not by unpark(), but the state
183                 // was set to NOTIFIED, which means we *just* missed an
184                 // unpark(), which is now blocked on us to wait for it.
185                 // Wait for it to consume the event and unblock that thread.
186                 c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
187             }
188         }
189     }
190
191     // This implementation doesn't require `Pin`, but other implementations do.
192     pub fn unpark(self: Pin<&Self>) {
193         // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
194         // wake the thread in the first case.
195         //
196         // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
197         // purpose, to make sure every unpark() has a release-acquire ordering
198         // with park().
199         if self.state.swap(NOTIFIED, Release) == PARKED {
200             unsafe {
201                 if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() {
202                     wake_by_address_single(self.ptr());
203                 } else {
204                     // If we run NtReleaseKeyedEvent before the waiting thread runs
205                     // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
206                     // If the waiting thread wakes up before we run NtReleaseKeyedEvent
207                     // (e.g. due to a timeout), this blocks until we do wake up a thread.
208                     // To prevent this thread from blocking indefinitely in that case,
209                     // park_impl() will, after seeing the state set to NOTIFIED after
210                     // waking up, call NtWaitForKeyedEvent again to unblock us.
211                     c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
212                 }
213             }
214         }
215     }
216
217     fn ptr(&self) -> c::LPVOID {
218         &self.state as *const _ as c::LPVOID
219     }
220 }
221
222 fn keyed_event_handle() -> c::HANDLE {
223     const INVALID: c::HANDLE = ptr::invalid_mut(!0);
224     static HANDLE: AtomicPtr<libc::c_void> = AtomicPtr::new(INVALID);
225     match HANDLE.load(Relaxed) {
226         INVALID => {
227             let mut handle = c::INVALID_HANDLE_VALUE;
228             unsafe {
229                 match c::NtCreateKeyedEvent(
230                     &mut handle,
231                     c::GENERIC_READ | c::GENERIC_WRITE,
232                     ptr::null_mut(),
233                     0,
234                 ) {
235                     c::STATUS_SUCCESS => {}
236                     r => panic!("Unable to create keyed event handle: error {r}"),
237                 }
238             }
239             match HANDLE.compare_exchange(INVALID, handle, Relaxed, Relaxed) {
240                 Ok(_) => handle,
241                 Err(h) => {
242                     // Lost the race to another thread initializing HANDLE before we did.
243                     // Closing our handle and using theirs instead.
244                     unsafe {
245                         c::CloseHandle(handle);
246                     }
247                     h
248                 }
249             }
250         }
251         handle => handle,
252     }
253 }