|
21 | 21 |
|
22 | 22 | use crate::ffi::{c_void, CStr};
|
23 | 23 | use crate::ptr::NonNull;
|
| 24 | +use crate::sync::atomic::{AtomicBool, Ordering}; |
24 | 25 | use crate::sys::c;
|
25 | 26 |
|
26 | 27 | /// Helper macro for creating CStrs from literals and symbol names.
|
@@ -74,6 +75,20 @@ impl Module {
|
74 | 75 | NonNull::new(module).map(Self)
|
75 | 76 | }
|
76 | 77 |
|
| 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 | + |
77 | 92 | // Try to get the address of a function.
|
78 | 93 | pub fn proc_address(self, name: &CStr) -> Option<NonNull<c_void>> {
|
79 | 94 | // SAFETY:
|
@@ -144,61 +159,63 @@ macro_rules! compat_fn_with_fallback {
|
144 | 159 | )*)
|
145 | 160 | }
|
146 | 161 |
|
147 |
| -/// Optionally load `WaitOnAddress`. |
148 |
| -/// Unlike the dynamic loading described above, this does not have a fallback. |
| 162 | +/// Optionally loaded functions. |
149 | 163 | ///
|
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 | + } |
181 | 188 |
|
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 | + } |
190 | 194 | }
|
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 | +} |
198 | 198 |
|
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(()) |
203 | 215 | }
|
| 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) |
204 | 221 | }
|
0 commit comments