#[macro_export]
macro_rules! scoped_thread_local {
(static $name:ident: $ty:ty) => {
static $name: crate::utils::scoped_tls::ScopedKey<$ty> = unsafe {
static FOO: crate::utils::scoped_tls::Wrapper<
::core::cell::Cell<*const ()>,
> = crate::utils::scoped_tls::Wrapper::new(::core::cell::Cell::new(
::core::ptr::null(),
));
crate::utils::scoped_tls::ScopedKey::new(&FOO)
};
};
}
use core::cell::Cell;
use core::marker::PhantomData;
pub(crate) struct Wrapper<T>(T);
impl<T> Wrapper<T> {
#[allow(unused)]
pub(crate) const fn new(value: T) -> Self { Self(value) }
}
unsafe impl<T> Sync for Wrapper<T> {}
pub struct ScopedKey<T> {
inner: &'static Wrapper<Cell<*const ()>>,
_marker: PhantomData<T>,
}
unsafe impl<T> Sync for ScopedKey<T> {}
impl<T> ScopedKey<T> {
#[doc(hidden)]
#[allow(unused)]
pub(crate) const unsafe fn new(
inner: &'static Wrapper<Cell<*const ()>>,
) -> Self {
Self {
inner,
_marker: PhantomData,
}
}
pub fn set<F, R>(&'static self, t: &T, f: F) -> R
where
F: FnOnce() -> R,
{
struct Reset {
key: &'static Wrapper<Cell<*const ()>>,
val: *const (),
}
impl Drop for Reset {
fn drop(&mut self) { self.key.0.set(self.val); }
}
let prev = self.inner.0.get();
self.inner.0.set(t as *const T as *const ());
let _reset = Reset {
key: self.inner,
val: prev,
};
f()
}
pub fn with<F, R>(&'static self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
let val = self.inner.0.get();
assert!(
!val.is_null(),
"cannot access a scoped thread local variable without calling `set` first"
);
unsafe { f(&*(val as *const T)) }
}
pub fn is_set(&'static self) -> bool { !self.inner.0.get().is_null() }
}