pub use abi_stable as macro_support;
use abi_stable::std_types::{RBox, RStr};
pub struct LocalKey<T: 'static> {
#[doc(hidden)]
pub read: unsafe extern "C" fn() -> *const T,
}
#[macro_export]
macro_rules! thread_local {
() => {};
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => {
$crate::__thread_local_inner!($(#[$attr])* $vis $name: $t = $init);
$crate::thread_local!($($rest)*);
};
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => {
$crate::__thread_local_inner!($(#[$attr])* $vis $name: $t = $init);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __thread_local_inner {
($(#[$attr:meta])* $vis:vis $name:ident: $t:ty = $init:expr) => {
$(#[$attr])*
$vis static $name: $crate::LocalKey<$t> = {
use $crate::macro_support::{pointer_trait::TransmuteElement, std_types::{RBox, RStr}};
extern "C" fn __thread_local_init() -> RBox<()> {
let __thread_local_val: $t = $init;
unsafe { RBox::new(__thread_local_val).transmute_element() }
}
std::thread_local! {
static VALUE: &'static $t = $crate::__get_tls::<$t>(
&RStr::from_str(std::concat!(std::stringify!($name), std::stringify!($t), std::module_path!())),
__thread_local_init,
);
}
unsafe extern "C" fn __thread_local_read() -> *const $t {
VALUE.with(|v| *v as *const $t)
}
$crate::LocalKey { read: __thread_local_read }
};
}
}
#[macro_export]
macro_rules! lazy_static {
() => {};
($(#[$attr:meta])* static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
$crate::__lazy_static_inner!($(#[$attr])* () static ref $N : $T = $e; $($t)*);
};
($(#[$attr:meta])* pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
$crate::__lazy_static_inner!($(#[$attr])* (pub) static ref $N : $T = $e; $($t)*);
};
($(#[$attr:meta])* pub ($($vis:tt)+) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
$crate::__lazy_static_inner!($(#[$attr])* (pub ($($vis)+)) static ref $N : $T = $e; $($t)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __lazy_static_inner {
($(#[$attr:meta])* ($($vis:tt)*) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => {
#[allow(missing_copy_implementations)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
$(#[$attr])*
$($vis)* struct $N {__private_field: ()}
#[doc(hidden)]
$($vis)* static $N: $N = $N {__private_field: ()};
impl std::ops::Deref for $N {
type Target = $T;
fn deref(&self) -> &$T {
use $crate::macro_support::{pointer_trait::TransmuteElement, std_types::{RBox, RStr}};
use std::sync::Once;
use std::mem::MaybeUninit;
extern "C" fn __initialize() -> RBox<()> {
let __val: $T = $e;
unsafe { RBox::new(__val).transmute_element() }
}
static VALUE_ONCE: Once = Once::new();
static mut VALUE: MaybeUninit<&'static $T> = MaybeUninit::uninit();
VALUE_ONCE.call_once(|| {
unsafe {
VALUE.write($crate::__get_static(
&RStr::from_str(std::concat!(std::stringify!($N), std::stringify!($T), std::module_path!())),
__initialize
));
}
});
unsafe { VALUE.assume_init_read() }
}
}
$crate::lazy_static!($($t)*);
};
}
#[cfg(feature = "host")]
mod host {
use abi_stable::std_types::{RBox, RStr};
use std::cell::RefCell;
use std::collections::BTreeMap as Map;
use std::mem::MaybeUninit;
use std::sync::{Mutex, Once};
std::thread_local! {
static PLUGIN_TLS: RefCell<Map<RStr<'static>, RBox<()>>> = RefCell::new(Default::default());
}
static PLUGIN_STATIC_ONCE: Once = Once::new();
static mut PLUGIN_STATIC: MaybeUninit<Mutex<Map<RStr<'static>, RBox<()>>>> =
MaybeUninit::uninit();
fn static_map() -> std::sync::MutexGuard<'static, Map<RStr<'static>, RBox<()>>> {
PLUGIN_STATIC_ONCE.call_once(|| unsafe {
PLUGIN_STATIC.write(Default::default());
});
unsafe { PLUGIN_STATIC.assume_init_mut() }.lock().unwrap()
}
pub unsafe extern "C" fn tls(
id: &RStr<'static>,
init: extern "C" fn() -> RBox<()>,
) -> *const () {
PLUGIN_TLS.with(|m| {
let mut m = m.borrow_mut();
if !m.contains_key(id) {
m.insert(id.clone(), init());
}
m.get(id).unwrap().as_ref() as *const ()
})
}
pub unsafe extern "C" fn statics(
id: &RStr<'static>,
init: extern "C" fn() -> RBox<()>,
) -> *const () {
let mut m = static_map();
if !m.contains_key(id) {
m.insert(id.clone(), init());
}
m.get(id).unwrap().as_ref() as *const ()
}
pub fn reset() {
PLUGIN_TLS.with(|m| m.borrow_mut().clear());
static_map().clear();
}
}
type AccessFunction =
unsafe extern "C" fn(&RStr<'static>, extern "C" fn() -> RBox<()>) -> *const ();
#[repr(C)]
pub struct Context {
tls: AccessFunction,
statics: AccessFunction,
}
impl Context {
pub unsafe fn initialize(self) {
HOST_TLS = Some(self.tls);
HOST_STATICS = Some(self.statics);
}
}
#[cfg(feature = "host")]
impl Context {
pub fn get() -> Self {
Context {
tls: host::tls,
statics: host::statics,
}
}
pub fn reset() {
host::reset();
}
}
#[cfg(feature = "host")]
static mut HOST_TLS: Option<AccessFunction> = Some(host::tls);
#[cfg(not(feature = "host"))]
static mut HOST_TLS: Option<AccessFunction> = None;
#[cfg(feature = "host")]
static mut HOST_STATICS: Option<AccessFunction> = Some(host::statics);
#[cfg(not(feature = "host"))]
static mut HOST_STATICS: Option<AccessFunction> = None;
#[doc(hidden)]
pub fn __get_tls<T>(id: &RStr<'static>, init: extern "C" fn() -> RBox<()>) -> &'static T {
let host_tls =
unsafe { HOST_TLS.as_ref() }.expect("host thread local storage improperly initialized");
unsafe { (host_tls(id, init) as *const T).as_ref().unwrap() }
}
#[doc(hidden)]
pub fn __get_static<T>(id: &RStr<'static>, init: extern "C" fn() -> RBox<()>) -> &'static T {
let host_statics =
unsafe { HOST_STATICS.as_ref() }.expect("host static storage improperly initialized");
unsafe { (host_statics(id, init) as *const T).as_ref().unwrap() }
}
impl<T: 'static> LocalKey<T> {
#[cfg(any(feature = "host", feature = "plugin"))]
pub fn with<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
f(unsafe { (self.read)().as_ref().unwrap() })
}
#[cfg(not(any(feature = "host", feature = "plugin")))]
pub fn with<F, R>(&'static self, _f: F) -> R
where
F: FnOnce(&T) -> R,
{
panic!("plugin_tls built without 'host' or 'plugin' enabled")
}
}