Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7a31b4a

Browse files
committedFeb 19, 2024
Always use WaitOnAddress on Win10+
1 parent 3246e79 commit 7a31b4a

File tree

3 files changed

+154
-106
lines changed

3 files changed

+154
-106
lines changed
 

‎library/std/src/sys/pal/windows/c.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,14 @@ pub type SIZE_T = usize;
2828
pub type WORD = u16;
2929
pub type CHAR = c_char;
3030
pub type ULONG = c_ulong;
31-
pub type ACCESS_MASK = DWORD;
3231

3332
pub type LPCVOID = *const c_void;
34-
pub type LPHANDLE = *mut HANDLE;
3533
pub type LPOVERLAPPED = *mut OVERLAPPED;
3634
pub type LPSECURITY_ATTRIBUTES = *mut SECURITY_ATTRIBUTES;
3735
pub type LPVOID = *mut c_void;
3836
pub type LPWCH = *mut WCHAR;
3937
pub type LPWSTR = *mut WCHAR;
4038

41-
pub type PLARGE_INTEGER = *mut c_longlong;
4239
pub type PSRWLOCK = *mut SRWLOCK;
4340

4441
pub type socklen_t = c_int;
@@ -345,6 +342,19 @@ compat_fn_with_fallback! {
345342
}
346343
}
347344

345+
#[cfg(not(target_vendor = "win7"))]
346+
#[link(name = "synchronization")]
347+
extern "system" {
348+
pub fn WaitOnAddress(
349+
address: *const c_void,
350+
compareaddress: *const c_void,
351+
addresssize: usize,
352+
dwmilliseconds: u32,
353+
) -> BOOL;
354+
pub fn WakeByAddressSingle(address: *const c_void);
355+
}
356+
357+
#[cfg(target_vendor = "win7")]
348358
compat_fn_optional! {
349359
crate::sys::compat::load_synch_functions();
350360
pub fn WaitOnAddress(
@@ -356,30 +366,34 @@ compat_fn_optional! {
356366
pub fn WakeByAddressSingle(address: *const ::core::ffi::c_void);
357367
}
358368

369+
#[cfg(any(target_vendor = "win7", target_vendor = "uwp"))]
359370
compat_fn_with_fallback! {
360371
pub static NTDLL: &CStr = c"ntdll";
361372

373+
#[cfg(target_vendor = "win7")]
362374
pub fn NtCreateKeyedEvent(
363-
KeyedEventHandle: LPHANDLE,
364-
DesiredAccess: ACCESS_MASK,
375+
KeyedEventHandle: *mut HANDLE,
376+
DesiredAccess: DWORD,
365377
ObjectAttributes: LPVOID,
366378
Flags: ULONG
367379
) -> NTSTATUS {
368380
panic!("keyed events not available")
369381
}
382+
#[cfg(target_vendor = "win7")]
370383
pub fn NtReleaseKeyedEvent(
371384
EventHandle: HANDLE,
372385
Key: LPVOID,
373386
Alertable: BOOLEAN,
374-
Timeout: PLARGE_INTEGER
387+
Timeout: *mut c_longlong
375388
) -> NTSTATUS {
376389
panic!("keyed events not available")
377390
}
391+
#[cfg(target_vendor = "win7")]
378392
pub fn NtWaitForKeyedEvent(
379393
EventHandle: HANDLE,
380394
Key: LPVOID,
381395
Alertable: BOOLEAN,
382-
Timeout: PLARGE_INTEGER
396+
Timeout: *mut c_longlong
383397
) -> NTSTATUS {
384398
panic!("keyed events not available")
385399
}

‎library/std/src/sys/pal/windows/compat.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
2222
use crate::ffi::{c_void, CStr};
2323
use crate::ptr::NonNull;
24-
use crate::sync::atomic::Ordering;
2524
use crate::sys::c;
2625

2726
// This uses a static initializer to preload some imported functions.
@@ -38,6 +37,7 @@ use crate::sys::c;
3837
// file an issue for discussion; currently we don't guarantee any functionality
3938
// before main.
4039
// See https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-initialization?view=msvc-170
40+
#[cfg(target_vendor = "win7")]
4141
#[used]
4242
#[link_section = ".CRT$XCT"]
4343
static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
@@ -52,6 +52,7 @@ static INIT_TABLE_ENTRY: unsafe extern "C" fn() = init;
5252
/// negative performance impact in practical situations.
5353
///
5454
/// Currently we only preload `WaitOnAddress` and `WakeByAddressSingle`.
55+
#[cfg(target_vendor = "win7")]
5556
unsafe extern "C" fn init() {
5657
// In an exe this code is executed before main() so is single threaded.
5758
// In a DLL the system's loader lock will be held thereby synchronizing
@@ -183,6 +184,7 @@ macro_rules! compat_fn_with_fallback {
183184
func($($argname),*)
184185
}
185186
}
187+
#[allow(unused)]
186188
$(#[$meta])*
187189
$vis use $symbol::call as $symbol;
188190
)*)
@@ -191,6 +193,7 @@ macro_rules! compat_fn_with_fallback {
191193
/// Optionally loaded functions.
192194
///
193195
/// Actual loading of the function defers to $load_functions.
196+
#[cfg(target_vendor = "win7")]
194197
macro_rules! compat_fn_optional {
195198
($load_functions:expr;
196199
$(
@@ -218,13 +221,19 @@ macro_rules! compat_fn_optional {
218221
NonNull::new(PTR.load(Ordering::Relaxed)).map(|f| unsafe { mem::transmute(f) })
219222
}
220223
}
224+
#[inline]
225+
pub unsafe extern "system" fn $symbol($($argname: $argtype),*) $(-> $rettype)? {
226+
$symbol::option().unwrap()($($argname),*)
227+
}
221228
)+
222229
)
223230
}
224231

225232
/// Load all needed functions from "api-ms-win-core-synch-l1-2-0".
233+
#[cfg(target_vendor = "win7")]
226234
pub(super) fn load_synch_functions() {
227235
fn try_load() -> Option<()> {
236+
use crate::sync::atomic::Ordering;
228237
const MODULE_NAME: &CStr = c"api-ms-win-core-synch-l1-2-0";
229238
const WAIT_ON_ADDRESS: &CStr = c"WaitOnAddress";
230239
const WAKE_BY_ADDRESS_SINGLE: &CStr = c"WakeByAddressSingle";

‎library/std/src/sys/pal/windows/thread_parking.rs

Lines changed: 123 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,9 @@
5858
// [4]: Windows Internals, Part 1, ISBN 9780735671300
5959

6060
use crate::pin::Pin;
61-
use crate::ptr;
6261
use crate::sync::atomic::{
63-
AtomicI8, AtomicPtr,
64-
Ordering::{Acquire, Relaxed, Release},
62+
AtomicI8,
63+
Ordering::{Acquire, Release},
6564
};
6665
use crate::sys::{c, dur2timeout};
6766
use crate::time::Duration;
@@ -111,26 +110,21 @@ impl Parker {
111110
return;
112111
}
113112

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-
}
113+
#[cfg(target_vendor = "win7")]
114+
if c::WaitOnAddress::option().is_none() {
115+
return keyed_events::park(self);
116+
}
117+
118+
loop {
119+
// Wait for something to happen, assuming it's still set to PARKED.
120+
c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
121+
// Change NOTIFIED=>EMPTY but leave PARKED alone.
122+
if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() {
123+
// Actually woken up by unpark().
124+
return;
125+
} else {
126+
// Spurious wake up. We loop to try again.
125127
}
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);
134128
}
135129
}
136130

@@ -144,47 +138,23 @@ impl Parker {
144138
return;
145139
}
146140

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-
}
141+
#[cfg(target_vendor = "win7")]
142+
if c::WaitOnAddress::option().is_none() {
143+
return keyed_events::park_timeout(self, timeout);
144+
}
145+
146+
// Wait for something to happen, assuming it's still set to PARKED.
147+
c::WaitOnAddress(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
148+
// Set the state back to EMPTY (from either PARKED or NOTIFIED).
149+
// Note that we don't just write EMPTY, but use swap() to also
150+
// include an acquire-ordered read to synchronize with unpark()'s
151+
// release-ordered write.
152+
if self.state.swap(EMPTY, Acquire) == NOTIFIED {
153+
// Actually woken up by unpark().
161154
} 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-
}
155+
// Timeout or spurious wake up.
156+
// We return either way, because we can't easily tell if it was the
157+
// timeout or not.
188158
}
189159
}
190160

@@ -198,18 +168,11 @@ impl Parker {
198168
// with park().
199169
if self.state.swap(NOTIFIED, Release) == PARKED {
200170
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());
171+
#[cfg(target_vendor = "win7")]
172+
if c::WakeByAddressSingle::option().is_none() {
173+
return keyed_events::unpark(self);
212174
}
175+
c::WakeByAddressSingle(self.ptr());
213176
}
214177
}
215178
}
@@ -219,35 +182,97 @@ impl Parker {
219182
}
220183
}
221184

222-
fn keyed_event_handle() -> c::HANDLE {
223-
const INVALID: c::HANDLE = ptr::invalid_mut(!0);
224-
static HANDLE: AtomicPtr<crate::ffi::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}"),
185+
#[cfg(target_vendor = "win7")]
186+
mod keyed_events {
187+
use super::{Parker, EMPTY, NOTIFIED};
188+
use crate::sys::c;
189+
use core::pin::Pin;
190+
use core::ptr;
191+
use core::sync::atomic::{
192+
AtomicPtr,
193+
Ordering::{Acquire, Relaxed},
194+
};
195+
use core::time::Duration;
196+
197+
pub unsafe fn park(parker: Pin<&Parker>) {
198+
// Wait for unpark() to produce this event.
199+
c::NtWaitForKeyedEvent(keyed_event_handle(), parker.ptr(), 0, ptr::null_mut());
200+
// Set the state back to EMPTY (from either PARKED or NOTIFIED).
201+
// Note that we don't just write EMPTY, but use swap() to also
202+
// include an acquire-ordered read to synchronize with unpark()'s
203+
// release-ordered write.
204+
parker.state.swap(EMPTY, Acquire);
205+
return;
206+
}
207+
pub unsafe fn park_timeout(parker: Pin<&Parker>, timeout: Duration) {
208+
// Need to wait for unpark() using NtWaitForKeyedEvent.
209+
let handle = keyed_event_handle();
210+
211+
// NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
212+
// values to indicate a relative time on the monotonic clock.
213+
// This is documented here for the underlying KeWaitForSingleObject function:
214+
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
215+
let mut timeout = match i64::try_from((timeout.as_nanos() + 99) / 100) {
216+
Ok(t) => -t,
217+
Err(_) => i64::MIN,
218+
};
219+
220+
// Wait for unpark() to produce this event.
221+
let unparked =
222+
c::NtWaitForKeyedEvent(handle, parker.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;
223+
224+
// Set the state back to EMPTY (from either PARKED or NOTIFIED).
225+
let prev_state = parker.state.swap(EMPTY, Acquire);
226+
227+
if !unparked && prev_state == NOTIFIED {
228+
// We were awoken by a timeout, not by unpark(), but the state
229+
// was set to NOTIFIED, which means we *just* missed an
230+
// unpark(), which is now blocked on us to wait for it.
231+
// Wait for it to consume the event and unblock that thread.
232+
c::NtWaitForKeyedEvent(handle, parker.ptr(), 0, ptr::null_mut());
233+
}
234+
}
235+
pub unsafe fn unpark(parker: Pin<&Parker>) {
236+
// If we run NtReleaseKeyedEvent before the waiting thread runs
237+
// NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
238+
// If the waiting thread wakes up before we run NtReleaseKeyedEvent
239+
// (e.g. due to a timeout), this blocks until we do wake up a thread.
240+
// To prevent this thread from blocking indefinitely in that case,
241+
// park_impl() will, after seeing the state set to NOTIFIED after
242+
// waking up, call NtWaitForKeyedEvent again to unblock us.
243+
c::NtReleaseKeyedEvent(keyed_event_handle(), parker.ptr(), 0, ptr::null_mut());
244+
}
245+
246+
fn keyed_event_handle() -> c::HANDLE {
247+
const INVALID: c::HANDLE = ptr::invalid_mut(!0);
248+
static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(INVALID);
249+
match HANDLE.load(Relaxed) {
250+
INVALID => {
251+
let mut handle = c::INVALID_HANDLE_VALUE;
252+
unsafe {
253+
match c::NtCreateKeyedEvent(
254+
&mut handle,
255+
c::GENERIC_READ | c::GENERIC_WRITE,
256+
ptr::null_mut(),
257+
0,
258+
) {
259+
c::STATUS_SUCCESS => {}
260+
r => panic!("Unable to create keyed event handle: error {r}"),
261+
}
237262
}
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);
263+
match HANDLE.compare_exchange(INVALID, handle, Relaxed, Relaxed) {
264+
Ok(_) => handle,
265+
Err(h) => {
266+
// Lost the race to another thread initializing HANDLE before we did.
267+
// Closing our handle and using theirs instead.
268+
unsafe {
269+
c::CloseHandle(handle);
270+
}
271+
h
246272
}
247-
h
248273
}
249274
}
275+
handle => handle,
250276
}
251-
handle => handle,
252277
}
253278
}

0 commit comments

Comments
 (0)
Please sign in to comment.