use std::sync::{Arc, atomic::AtomicBool};
use findshlibs::{IterationControl, SharedLibrary};
#[doc(hidden)]
pub use concat_idents::concat_idents as __concat_idents;
#[doc(hidden)]
#[cfg(debug_assertions)]
pub const fn __new_container<T>() -> atomic_refcell::AtomicRefCell<Option<Arc<T>>> {
atomic_refcell::AtomicRefCell::new(None)
}
#[doc(hidden)]
#[cfg(not(debug_assertions))]
pub const fn __new_container<T>() -> core::cell::UnsafeCell<Option<Arc<T>>> {
core::cell::UnsafeCell::new(None)
}
#[doc(hidden)]
pub struct CrossDylib<T> {
#[doc(hidden)]
pub syncing: AtomicBool,
#[doc(hidden)]
#[cfg(not(debug_assertions))]
pub inner: core::cell::UnsafeCell<Option<Arc<T>>>,
#[doc(hidden)]
#[cfg(debug_assertions)]
pub inner: atomic_refcell::AtomicRefCell<Option<Arc<T>>>,
#[doc(hidden)]
pub symbol: &'static [u8],
#[doc(hidden)]
pub init: fn() -> T
}
unsafe impl<T> Sync for CrossDylib<T> {}
impl<T> CrossDylib<T> {
#[doc(hidden)]
#[inline]
#[cfg(not(debug_assertions))]
pub fn new_ref(&self) -> Option<Arc<T>> {
unsafe { &*self.inner.get() }.as_ref().map(|arc| arc.clone())
}
#[doc(hidden)]
#[inline]
#[cfg(debug_assertions)]
pub fn new_ref(&self) -> Option<Arc<T>> {
self.inner.borrow().as_ref().map(|arc| arc.clone())
}
pub unsafe fn sync(&self) -> Result<(), libloading::Error> {
assert!(self.symbol.len() > 0 && self.symbol.ends_with(&[0u8]));
let mut result = Ok(());
#[cfg(not(debug_assertions))]
let inner = &mut *self.inner.get();
#[cfg(debug_assertions)]
let mut inner = self.inner.borrow_mut();
self.syncing.store(true, std::sync::atomic::Ordering::Release);
let process = std::env::current_exe().ok();
findshlibs::TargetSharedLibrary::each(|shlib| {
if let Some(ref process) = process {
if shlib.name() == process {
return IterationControl::Continue;
}
}
let lib = match libloading::Library::new(shlib.name()) {
Ok(lib) => lib,
Err(err) => {
result = Err(err);
return IterationControl::Break
},
};
if let Ok(sym) = lib.get::<extern "Rust" fn() -> Option<Arc<T>>>(self.symbol) {
if let Some(init) = sym() {
*inner = Some(init);
return IterationControl::Break;
} else {
}
}
IterationControl::Continue
});
self.syncing.store(false, std::sync::atomic::Ordering::Release);
if inner.is_none() {
*inner = Some(Arc::new((self.init)()));
}
result
}
}
impl<T> std::ops::Deref for CrossDylib<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe {
let inner = {
#[cfg(not(debug_assertions))] {
&*self.inner.get()
}
#[cfg(debug_assertions)] {
drop(self.inner.borrow());
&*self.inner.as_ptr()
}
};
debug_assert!(inner.is_some(), "CrossDylib::sync() must be called before accessing a CrossDylib");
match &*inner {
Some(inner) => &*inner,
None => std::hint::unreachable_unchecked()
}
}
}
}
#[macro_export]
macro_rules! crossdylib {
{ $($(#[$attr:meta])* $vis:vis static $ident:ident: $ty:ty = $expr:expr;)+ } => {
$(
$(#[$attr])*
$vis static $ident: $crate::CrossDylib<$ty> = $crate::CrossDylib {
inner: $crate::__new_container::<$ty>(),
symbol: concat!("__crossdylib_", stringify!($ident), "\0").as_bytes(),
syncing: ::std::sync::atomic::AtomicBool::new(false),
init: || $expr
};
$crate::__concat_idents!(export_name = __crossdylib_, $ident {
#[no_mangle]
#[doc(hidden)]
#[allow(non_snake_case)]
pub unsafe extern "Rust" fn export_name() -> ::core::option::Option<::std::sync::Arc<$ty>> {
if $ident.syncing.load(std::sync::atomic::Ordering::Acquire) {
None
} else {
$ident.new_ref()
}
}
});
)+
};
}
pub unsafe fn scan_fn<T: Copy>(symbol: &[u8]) -> Result<Option<T>, libloading::Error> {
let mut result = Ok(None);
let process = std::env::current_exe().ok();
findshlibs::TargetSharedLibrary::each(|shlib| {
if let Some(ref process) = process {
if shlib.name() == process {
return IterationControl::Continue;
}
}
let lib = match libloading::Library::new(shlib.name()) {
Ok(lib) => lib,
Err(err) => {
result = Err(err);
return IterationControl::Break;
},
};
if let Ok(sym) = lib.get::<T>(symbol) {
result = Ok(Some(*sym));
IterationControl::Break
} else {
IterationControl::Continue
}
});
result
}