Skip to content

Commit 00b3450

Browse files
authored
Rollup merge of #76979 - fusion-engineering-forks:windows-fallback-check, r=dtolnay
Improve std::sys::windows::compat Improves the compat_fn macro in sys::windows, which is used for conditionally loading APIs that might not be available. - The module (dll) name can now be any string, not just an ident. (Not all Windows api modules are valid Rust identifiers. E.g. `WaitOnAddress` comes from `API-MS-Win-Core-Synch-l1-2-0.dll`.) - Adds `FuncName::is_available()` for checking if a function is really available without having to do a duplicate lookup. - Add comment explaining the lack of locking. - Use `$_:block` to simplify the macro_rules. - Apply `allow(unused_variables)` only to the fallback instead of everything. --- The second point (`is_available()`) simplifies code that needs to pick an implementation depening on what is available, like `sys/windows/mutex.rs`. Before this change, it'd do its own lookup and keep its own `AtomicUsize` to track the result. Now it can just use `c::AcquireSRWLockExclusive::is_available()` directly. This will also be useful when park/unpark/CondVar/etc. get improved implementations (e.g. from parking_lot or something else), as the best APIs for those are not available before Windows 8.
2 parents 1fa5f8f + 63b6007 commit 00b3450

File tree

3 files changed

+53
-45
lines changed

3 files changed

+53
-45
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1032,7 +1032,7 @@ extern "system" {
10321032
// Functions that aren't available on every version of Windows that we support,
10331033
// but we still use them and just provide some form of a fallback implementation.
10341034
compat_fn! {
1035-
kernel32:
1035+
"kernel32":
10361036

10371037
pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
10381038
_lpTargetFileName: LPCWSTR,

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

+49-26
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
//! function is available but afterwards it's just a load and a jump.
1313
1414
use crate::ffi::CString;
15-
use crate::sync::atomic::{AtomicUsize, Ordering};
1615
use crate::sys::c;
1716

1817
pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
@@ -28,45 +27,69 @@ pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
2827
}
2928
}
3029

31-
pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, fallback: usize) -> usize {
32-
let value = lookup(module, symbol).unwrap_or(fallback);
33-
ptr.store(value, Ordering::SeqCst);
34-
value
35-
}
36-
3730
macro_rules! compat_fn {
38-
($module:ident: $(
31+
($module:literal: $(
3932
$(#[$meta:meta])*
40-
pub fn $symbol:ident($($argname:ident: $argtype:ty),*)
41-
-> $rettype:ty {
42-
$($body:expr);*
43-
}
33+
pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $body:block
4434
)*) => ($(
45-
#[allow(unused_variables)]
4635
$(#[$meta])*
47-
pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
36+
pub mod $symbol {
37+
use super::*;
4838
use crate::sync::atomic::{AtomicUsize, Ordering};
4939
use crate::mem;
40+
5041
type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
5142

5243
static PTR: AtomicUsize = AtomicUsize::new(0);
5344

45+
#[allow(unused_variables)]
46+
unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype $body
47+
48+
/// This address is stored in `PTR` to incidate an unavailable API.
49+
///
50+
/// This way, call() will end up calling fallback() if it is unavailable.
51+
///
52+
/// This is a `static` to avoid rustc duplicating `fn fallback()`
53+
/// into both load() and is_available(), which would break
54+
/// is_available()'s comparison. By using the same static variable
55+
/// in both places, they'll refer to the same (copy of the)
56+
/// function.
57+
///
58+
/// LLVM merging the address of fallback with other functions
59+
/// (because of unnamed_addr) is fine, since it's only compared to
60+
/// an address from GetProcAddress from an external dll.
61+
static FALLBACK: F = fallback;
62+
63+
#[cold]
5464
fn load() -> usize {
55-
crate::sys::compat::store_func(&PTR,
56-
stringify!($module),
57-
stringify!($symbol),
58-
fallback as usize)
65+
// There is no locking here. It's okay if this is executed by multiple threads in
66+
// parallel. `lookup` will result in the same value, and it's okay if they overwrite
67+
// eachothers result as long as they do so atomically. We don't need any guarantees
68+
// about memory ordering, as this involves just a single atomic variable which is
69+
// not used to protect or order anything else.
70+
let addr = crate::sys::compat::lookup($module, stringify!($symbol))
71+
.unwrap_or(FALLBACK as usize);
72+
PTR.store(addr, Ordering::Relaxed);
73+
addr
5974
}
60-
unsafe extern "system" fn fallback($($argname: $argtype),*)
61-
-> $rettype {
62-
$($body);*
75+
76+
fn addr() -> usize {
77+
match PTR.load(Ordering::Relaxed) {
78+
0 => load(),
79+
addr => addr,
80+
}
81+
}
82+
83+
#[allow(dead_code)]
84+
pub fn is_available() -> bool {
85+
addr() != FALLBACK as usize
6386
}
6487

65-
let addr = match PTR.load(Ordering::SeqCst) {
66-
0 => load(),
67-
n => n,
68-
};
69-
mem::transmute::<usize, F>(addr)($($argname),*)
88+
pub unsafe fn call($($argname: $argtype),*) -> $rettype {
89+
mem::transmute::<usize, F>(addr())($($argname),*)
90+
}
7091
}
92+
93+
pub use $symbol::call as $symbol;
7194
)*)
7295
}

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

+3-18
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use crate::cell::{Cell, UnsafeCell};
2323
use crate::mem::{self, MaybeUninit};
2424
use crate::sync::atomic::{AtomicUsize, Ordering};
2525
use crate::sys::c;
26-
use crate::sys::compat;
2726

2827
pub struct Mutex {
2928
// This is either directly an SRWLOCK (if supported), or a Box<Inner> otherwise.
@@ -40,8 +39,8 @@ struct Inner {
4039

4140
#[derive(Clone, Copy)]
4241
enum Kind {
43-
SRWLock = 1,
44-
CriticalSection = 2,
42+
SRWLock,
43+
CriticalSection,
4544
}
4645

4746
#[inline]
@@ -130,21 +129,7 @@ impl Mutex {
130129
}
131130

132131
fn kind() -> Kind {
133-
static KIND: AtomicUsize = AtomicUsize::new(0);
134-
135-
let val = KIND.load(Ordering::SeqCst);
136-
if val == Kind::SRWLock as usize {
137-
return Kind::SRWLock;
138-
} else if val == Kind::CriticalSection as usize {
139-
return Kind::CriticalSection;
140-
}
141-
142-
let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") {
143-
None => Kind::CriticalSection,
144-
Some(..) => Kind::SRWLock,
145-
};
146-
KIND.store(ret as usize, Ordering::SeqCst);
147-
ret
132+
if c::AcquireSRWLockExclusive::is_available() { Kind::SRWLock } else { Kind::CriticalSection }
148133
}
149134

150135
pub struct ReentrantMutex {

0 commit comments

Comments
 (0)