Improve std::sys::windows::compat.
- Module name can now be any string, not just an ident. (Not all Windows api modules are valid Rust identifiers.) - Adds c::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.
This commit is contained in:
parent
d92d28e523
commit
8b2bdfd453
|
@ -1032,7 +1032,7 @@ extern "system" {
|
|||
// Functions that aren't available on every version of Windows that we support,
|
||||
// but we still use them and just provide some form of a fallback implementation.
|
||||
compat_fn! {
|
||||
kernel32:
|
||||
"kernel32":
|
||||
|
||||
pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
|
||||
_lpTargetFileName: LPCWSTR,
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
//! function is available but afterwards it's just a load and a jump.
|
||||
|
||||
use crate::ffi::CString;
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
use crate::sys::c;
|
||||
|
||||
pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
|
||||
|
@ -28,45 +27,53 @@ pub fn lookup(module: &str, symbol: &str) -> Option<usize> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, fallback: usize) -> usize {
|
||||
let value = lookup(module, symbol).unwrap_or(fallback);
|
||||
ptr.store(value, Ordering::SeqCst);
|
||||
value
|
||||
}
|
||||
|
||||
macro_rules! compat_fn {
|
||||
($module:ident: $(
|
||||
($module:literal: $(
|
||||
$(#[$meta:meta])*
|
||||
pub fn $symbol:ident($($argname:ident: $argtype:ty),*)
|
||||
-> $rettype:ty {
|
||||
$($body:expr);*
|
||||
}
|
||||
pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $body:block
|
||||
)*) => ($(
|
||||
#[allow(unused_variables)]
|
||||
$(#[$meta])*
|
||||
pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype {
|
||||
pub mod $symbol {
|
||||
use super::*;
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
use crate::mem;
|
||||
type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
|
||||
|
||||
static PTR: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[allow(unused_variables)]
|
||||
unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype $body
|
||||
|
||||
#[cold]
|
||||
fn load() -> usize {
|
||||
crate::sys::compat::store_func(&PTR,
|
||||
stringify!($module),
|
||||
stringify!($symbol),
|
||||
fallback as usize)
|
||||
}
|
||||
unsafe extern "system" fn fallback($($argname: $argtype),*)
|
||||
-> $rettype {
|
||||
$($body);*
|
||||
// There is no locking here. It's okay if this is executed by multiple threads in
|
||||
// parallel. `lookup` will result in the same value, and it's okay if they overwrite
|
||||
// eachothers result as long as they do so atomically. We don't need any guarantees
|
||||
// about memory ordering, as this involves just a single atomic variable which is
|
||||
// not used to protect or order anything else.
|
||||
let addr = crate::sys::compat::lookup($module, stringify!($symbol))
|
||||
.unwrap_or(fallback as usize);
|
||||
PTR.store(addr, Ordering::Relaxed);
|
||||
addr
|
||||
}
|
||||
|
||||
let addr = match PTR.load(Ordering::SeqCst) {
|
||||
fn addr() -> usize {
|
||||
match PTR.load(Ordering::Relaxed) {
|
||||
0 => load(),
|
||||
n => n,
|
||||
};
|
||||
mem::transmute::<usize, F>(addr)($($argname),*)
|
||||
addr => addr,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_available() -> bool {
|
||||
addr() != fallback as usize
|
||||
}
|
||||
|
||||
pub unsafe fn call($($argname: $argtype),*) -> $rettype {
|
||||
type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
|
||||
mem::transmute::<usize, F>(addr())($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
pub use $symbol::call as $symbol;
|
||||
)*)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue