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:
Mara Bos 2020-09-20 14:46:10 +02:00
parent d92d28e523
commit 8b2bdfd453
2 changed files with 36 additions and 29 deletions

View File

@ -1032,7 +1032,7 @@ extern "system" {
// Functions that aren't available on every version of Windows that we support, // 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. // but we still use them and just provide some form of a fallback implementation.
compat_fn! { compat_fn! {
kernel32: "kernel32":
pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR,
_lpTargetFileName: LPCWSTR, _lpTargetFileName: LPCWSTR,

View File

@ -12,7 +12,6 @@
//! function is available but afterwards it's just a load and a jump. //! function is available but afterwards it's just a load and a jump.
use crate::ffi::CString; use crate::ffi::CString;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::c; use crate::sys::c;
pub fn lookup(module: &str, symbol: &str) -> Option<usize> { 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 { macro_rules! compat_fn {
($module:ident: $( ($module:literal: $(
$(#[$meta:meta])* $(#[$meta:meta])*
pub fn $symbol:ident($($argname:ident: $argtype:ty),*) pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $body:block
-> $rettype:ty {
$($body:expr);*
}
)*) => ($( )*) => ($(
#[allow(unused_variables)]
$(#[$meta])* $(#[$meta])*
pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { pub mod $symbol {
use super::*;
use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::mem; use crate::mem;
type F = unsafe extern "system" fn($($argtype),*) -> $rettype;
static PTR: AtomicUsize = AtomicUsize::new(0); static PTR: AtomicUsize = AtomicUsize::new(0);
#[allow(unused_variables)]
unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype $body
#[cold]
fn load() -> usize { fn load() -> usize {
crate::sys::compat::store_func(&PTR, // There is no locking here. It's okay if this is executed by multiple threads in
stringify!($module), // parallel. `lookup` will result in the same value, and it's okay if they overwrite
stringify!($symbol), // eachothers result as long as they do so atomically. We don't need any guarantees
fallback as usize) // about memory ordering, as this involves just a single atomic variable which is
} // not used to protect or order anything else.
unsafe extern "system" fn fallback($($argname: $argtype),*) let addr = crate::sys::compat::lookup($module, stringify!($symbol))
-> $rettype { .unwrap_or(fallback as usize);
$($body);* PTR.store(addr, Ordering::Relaxed);
addr
} }
let addr = match PTR.load(Ordering::SeqCst) { fn addr() -> usize {
0 => load(), match PTR.load(Ordering::Relaxed) {
n => n, 0 => load(),
}; addr => addr,
mem::transmute::<usize, F>(addr)($($argname),*) }
}
#[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;
)*) )*)
} }