Skip to content

Commit 77db317

Browse files
authored
Rollup merge of #100710 - ChrisDenton:load-library, r=thomcc
Windows: Load synch functions together Attempt to load all the required sync functions and fail if any one of them fails. This fixes a FIXME by going back to optional loading of `WakeByAddressSingle`. Also reintroduces a macro for optional loading of functions but keeps it separate from the fallback macro rather than having that do two different jobs. r? `@thomcc`
2 parents 45568bd + 625e7e9 commit 77db317

File tree

3 files changed

+93
-81
lines changed

3 files changed

+93
-81
lines changed

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

+12-14
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ pub const IPV6_ADD_MEMBERSHIP: c_int = 12;
228228
pub const IPV6_DROP_MEMBERSHIP: c_int = 13;
229229
pub const MSG_PEEK: c_int = 0x2;
230230

231+
pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 0x800;
232+
231233
#[repr(C)]
232234
#[derive(Copy, Clone)]
233235
pub struct linger {
@@ -1030,6 +1032,7 @@ extern "system" {
10301032
pub fn GetProcAddress(handle: HMODULE, name: LPCSTR) -> *mut c_void;
10311033
pub fn GetModuleHandleA(lpModuleName: LPCSTR) -> HMODULE;
10321034
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
1035+
pub fn LoadLibraryExA(lplibfilename: *const i8, hfile: HANDLE, dwflags: u32) -> HINSTANCE;
10331036

10341037
pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME);
10351038
pub fn GetSystemInfo(lpSystemInfo: LPSYSTEM_INFO);
@@ -1250,21 +1253,16 @@ compat_fn_with_fallback! {
12501253
}
12511254
}
12521255

1253-
compat_fn_with_fallback! {
1254-
pub static SYNCH_API: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0");
1255-
#[allow(unused)]
1256-
fn WakeByAddressSingle(Address: LPVOID) -> () {
1257-
// This fallback is currently tightly coupled to its use in Parker::unpark.
1258-
//
1259-
// FIXME: If `WakeByAddressSingle` needs to be used anywhere other than
1260-
// Parker::unpark then this fallback will be wrong and will need to be decoupled.
1261-
crate::sys::windows::thread_parker::unpark_keyed_event(Address)
1262-
}
1256+
compat_fn_optional! {
1257+
crate::sys::compat::load_synch_functions();
1258+
pub fn WaitOnAddress(
1259+
Address: LPVOID,
1260+
CompareAddress: LPVOID,
1261+
AddressSize: SIZE_T,
1262+
dwMilliseconds: DWORD
1263+
);
1264+
pub fn WakeByAddressSingle(Address: LPVOID);
12631265
}
1264-
pub use crate::sys::compat::WaitOnAddress;
1265-
// Change exported name of `WakeByAddressSingle` to make the strange fallback
1266-
// behaviour clear.
1267-
pub use WakeByAddressSingle::call as wake_by_address_single_or_unpark_keyed_event;
12681266

12691267
compat_fn_with_fallback! {
12701268
pub static NTDLL: &CStr = ansi_str!("ntdll");

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

+69-52
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
2222
use crate::ffi::{c_void, CStr};
2323
use crate::ptr::NonNull;
24+
use crate::sync::atomic::{AtomicBool, Ordering};
2425
use crate::sys::c;
2526

2627
/// Helper macro for creating CStrs from literals and symbol names.
@@ -74,6 +75,20 @@ impl Module {
7475
NonNull::new(module).map(Self)
7576
}
7677

78+
/// Load the library (if not already loaded)
79+
///
80+
/// # Safety
81+
///
82+
/// The module must not be unloaded.
83+
pub unsafe fn load_system_library(name: &CStr) -> Option<Self> {
84+
let module = c::LoadLibraryExA(
85+
name.as_ptr(),
86+
crate::ptr::null_mut(),
87+
c::LOAD_LIBRARY_SEARCH_SYSTEM32,
88+
);
89+
NonNull::new(module).map(Self)
90+
}
91+
7792
// Try to get the address of a function.
7893
pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
7994
// SAFETY:
@@ -144,61 +159,63 @@ macro_rules! compat_fn_with_fallback {
144159
)*)
145160
}
146161

147-
/// Optionally load `WaitOnAddress`.
148-
/// Unlike the dynamic loading described above, this does not have a fallback.
162+
/// Optionally loaded functions.
149163
///
150-
/// This is rexported from sys::c. You should prefer to import
151-
/// from there in case this changes again in the future.
152-
pub mod WaitOnAddress {
153-
use super::*;
154-
use crate::mem;
155-
use crate::ptr;
156-
use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
157-
use crate::sys::c;
158-
159-
static MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0");
160-
static SYMBOL_NAME: &CStr = ansi_str!("WaitOnAddress");
161-
162-
// WaitOnAddress function signature.
163-
type F = unsafe extern "system" fn(
164-
Address: c::LPVOID,
165-
CompareAddress: c::LPVOID,
166-
AddressSize: c::SIZE_T,
167-
dwMilliseconds: c::DWORD,
168-
);
169-
170-
// A place to store the loaded function atomically.
171-
static WAIT_ON_ADDRESS: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
172-
173-
// We can skip trying to load again if we already tried.
174-
static LOAD_MODULE: AtomicBool = AtomicBool::new(true);
175-
176-
#[inline(always)]
177-
pub fn option() -> Option<F> {
178-
let f = WAIT_ON_ADDRESS.load(Ordering::Acquire);
179-
if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() }
180-
}
164+
/// Actual loading of the function defers to $load_functions.
165+
macro_rules! compat_fn_optional {
166+
($load_functions:expr;
167+
$(
168+
$(#[$meta:meta])*
169+
$vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) $(-> $rettype:ty)?;
170+
)+) => (
171+
$(
172+
pub mod $symbol {
173+
use super::*;
174+
use crate::ffi::c_void;
175+
use crate::mem;
176+
use crate::ptr::{self, NonNull};
177+
use crate::sync::atomic::{AtomicPtr, Ordering};
178+
179+
pub(in crate::sys) static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
180+
181+
type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?;
182+
183+
#[inline(always)]
184+
pub fn option() -> Option<F> {
185+
let f = PTR.load(Ordering::Acquire);
186+
if !f.is_null() { Some(unsafe { mem::transmute(f) }) } else { try_load() }
187+
}
181188

182-
#[cold]
183-
fn try_load() -> Option<F> {
184-
if LOAD_MODULE.load(Ordering::Acquire) {
185-
// load the module
186-
let mut wait_on_address = None;
187-
if let Some(func) = try_load_inner() {
188-
WAIT_ON_ADDRESS.store(func.as_ptr(), Ordering::Release);
189-
wait_on_address = Some(unsafe { mem::transmute(func) });
189+
#[cold]
190+
fn try_load() -> Option<F> {
191+
$load_functions;
192+
NonNull::new(PTR.load(Ordering::Acquire)).map(|f| unsafe { mem::transmute(f) })
193+
}
190194
}
191-
// Don't try to load the module again even if loading failed.
192-
LOAD_MODULE.store(false, Ordering::Release);
193-
wait_on_address
194-
} else {
195-
None
196-
}
197-
}
195+
)+
196+
)
197+
}
198198

199-
// In the future this could be a `try` block but until then I think it's a
200-
// little bit cleaner as a separate function.
201-
fn try_load_inner() -> Option<NonNull<c_void>> {
202-
unsafe { Module::new(MODULE_NAME)?.proc_address(SYMBOL_NAME) }
199+
/// Load all needed functions from "api-ms-win-core-synch-l1-2-0".
200+
pub(super) fn load_synch_functions() {
201+
fn try_load() -> Option<()> {
202+
const MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0");
203+
const WAIT_ON_ADDRESS: &CStr = ansi_str!("WaitOnAddress");
204+
const WAKE_BY_ADDRESS_SINGLE: &CStr = ansi_str!("WakeByAddressSingle");
205+
206+
// Try loading the library and all the required functions.
207+
// If any step fails, then they all fail.
208+
let library = unsafe { Module::load_system_library(MODULE_NAME) }?;
209+
let wait_on_address = library.proc_address(WAIT_ON_ADDRESS)?;
210+
let wake_by_address_single = library.proc_address(WAKE_BY_ADDRESS_SINGLE)?;
211+
212+
c::WaitOnAddress::PTR.store(wait_on_address.as_ptr(), Ordering::Release);
213+
c::WakeByAddressSingle::PTR.store(wake_by_address_single.as_ptr(), Ordering::Release);
214+
Some(())
203215
}
216+
217+
// Try to load the module but skip loading if a previous attempt failed.
218+
static LOAD_MODULE: AtomicBool = AtomicBool::new(true);
219+
let module_loaded = LOAD_MODULE.load(Ordering::Acquire) && try_load().is_some();
220+
LOAD_MODULE.store(module_loaded, Ordering::Release)
204221
}

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

+12-15
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,18 @@ impl Parker {
198198
// with park().
199199
if self.state.swap(NOTIFIED, Release) == PARKED {
200200
unsafe {
201-
// This calls either WakeByAddressSingle or unpark_keyed_event (see below).
202-
c::wake_by_address_single_or_unpark_keyed_event(self.ptr());
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+
}
203213
}
204214
}
205215
}
@@ -209,19 +219,6 @@ impl Parker {
209219
}
210220
}
211221

212-
// This function signature makes it compatible with c::WakeByAddressSingle
213-
// so that it can be used as a fallback for that function.
214-
pub unsafe extern "C" fn unpark_keyed_event(address: c::LPVOID) {
215-
// If we run NtReleaseKeyedEvent before the waiting thread runs
216-
// NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
217-
// If the waiting thread wakes up before we run NtReleaseKeyedEvent
218-
// (e.g. due to a timeout), this blocks until we do wake up a thread.
219-
// To prevent this thread from blocking indefinitely in that case,
220-
// park_impl() will, after seeing the state set to NOTIFIED after
221-
// waking up, call NtWaitForKeyedEvent again to unblock us.
222-
c::NtReleaseKeyedEvent(keyed_event_handle(), address, 0, ptr::null_mut());
223-
}
224-
225222
fn keyed_event_handle() -> c::HANDLE {
226223
const INVALID: c::HANDLE = ptr::invalid_mut(!0);
227224
static HANDLE: AtomicPtr<libc::c_void> = AtomicPtr::new(INVALID);

0 commit comments

Comments
 (0)