use crate::cell::UnsafeCell;
#[doc(hidden)]
#[allow_internal_unstable(thread_local_internals)]
#[allow_internal_unsafe]
#[unstable(feature = "thread_local_internals", issue = "none")]
#[rustc_macro_transparency = "semitransparent"]
pub macro thread_local_inner {
(@key $t:ty, const $init:expr) => {{
const __INIT: $t = $init;
unsafe {
use $crate::thread::LocalKey;
use $crate::thread::local_impl::EagerStorage;
LocalKey::new(|_| {
static VAL: EagerStorage<$t> = EagerStorage { value: __INIT };
&VAL.value
})
}
}},
(@key $t:ty, $init:expr) => {{
#[inline]
fn __init() -> $t { $init }
unsafe {
use $crate::thread::LocalKey;
use $crate::thread::local_impl::LazyStorage;
LocalKey::new(|init| {
static VAL: LazyStorage<$t> = LazyStorage::new();
VAL.get(init, __init)
})
}
}},
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
},
}
#[allow(missing_debug_implementations)]
pub struct EagerStorage<T> {
pub value: T,
}
unsafe impl<T> Sync for EagerStorage<T> {}
#[allow(missing_debug_implementations)]
pub struct LazyStorage<T> {
value: UnsafeCell<Option<T>>,
}
impl<T> LazyStorage<T> {
pub const fn new() -> LazyStorage<T> {
LazyStorage { value: UnsafeCell::new(None) }
}
#[inline]
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
let value = unsafe { &*self.value.get() };
match value {
Some(v) => v,
None => self.initialize(i, f),
}
}
#[cold]
fn initialize(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
let value = i.and_then(Option::take).unwrap_or_else(f);
unsafe {
self.value.get().replace(Some(value));
}
unsafe { (*self.value.get()).as_ref().unwrap_unchecked() }
}
}
unsafe impl<T> Sync for LazyStorage<T> {}