#[cfg_attr(windows, path = "lazyfn/win32.rs")]
#[cfg_attr(unix, path = "lazyfn/unix.rs")]
mod os;
mod loader;
use std::{
cell,
ffi::CStr,
mem,
sync::{
self,
atomic::{AtomicPtr, Ordering},
},
};
use crate::error;
struct DefaultLinker;
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash, Debug)]
pub enum LinkType<'a> {
Vulkan,
General(&'a [&'a CStr]),
}
#[derive(Debug)]
pub struct LazyFn<'a, F: Copy + Sync + Send> {
pub(crate) once: sync::Once,
status: cell::RefCell<Option<error::DylinkError>>,
pub(crate) addr_ptr: AtomicPtr<F>,
pub(crate) addr: cell::Cell<F>,
fn_name: &'a CStr,
link_ty: LinkType<'a>,
}
unsafe impl<F: Copy + Sync + Send> Sync for LazyFn<'_, F> {}
impl<'a, F: Copy + Sync + Send> LazyFn<'a, F> {
#[inline]
pub const fn new(thunk: &'a F, fn_name: &'a CStr, link_ty: LinkType<'a>) -> Self {
assert!(mem::size_of::<crate::FnPtr>() == mem::size_of::<F>());
Self {
addr_ptr: AtomicPtr::new(thunk as *const _ as *mut _),
once: sync::Once::new(),
status: cell::RefCell::new(None),
addr: cell::Cell::new(*thunk),
fn_name,
link_ty,
}
}
pub fn try_link(&self) -> crate::Result<&F> {
self.try_link_with::<DefaultLinker>()
}
pub(crate) fn try_link_with<L: crate::RTLinker>(&self) -> crate::Result<&F> {
self.once.call_once(|| {
let maybe = match self.link_ty {
LinkType::Vulkan => unsafe { loader::vulkan_loader(self.fn_name) },
LinkType::General(lib_list) => {
let mut errors = vec![];
lib_list
.iter()
.find_map(|lib| {
loader::general_loader::<L>(lib, self.fn_name)
.map_err(|e| errors.push(e))
.ok()
})
.ok_or_else(|| {
let err: String = errors
.iter()
.map(|e| e.to_string() + "\n")
.collect();
error::DylinkError::ListNotLoaded(err)
})
}
};
match maybe {
Ok(addr) => {
unsafe {
self.addr.set(mem::transmute_copy(&addr));
}
self.addr_ptr.store(self.addr.as_ptr(), Ordering::Release);
}
Err(err) => {
let _ = self.status.replace(Some(err));
}
}
});
match (*self.status.borrow()).clone() {
None => Ok(self.load(Ordering::Acquire)),
Some(err) => Err(err),
}
}
#[inline]
fn load(&self, order: Ordering) -> &F {
unsafe { self.addr_ptr.load(order).as_ref().unwrap_unchecked() }
}
pub fn into_inner(self) -> F {
self.addr.into_inner()
}
}
impl<F: Copy + Sync + Send> std::ops::Deref for LazyFn<'_, F> {
type Target = F;
fn deref(&self) -> &Self::Target {
self.load(Ordering::Relaxed)
}
}