use core::{
cell::UnsafeCell,
mem::ManuallyDrop,
ops::Deref,
panic::{RefUnwindSafe, UnwindSafe},
};
use crate::{ptr_macros::ptr_to_field, sync::Once};
union Data<T, F> {
value: ManuallyDrop<T>,
f: ManuallyDrop<F>,
}
pub struct LazyLock<T, F = fn() -> T> {
once: Once,
data: UnsafeCell<Data<T, F>>,
}
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
#[must_use]
pub const unsafe fn init(this: *const Self, f: F) -> LazyLock<T, F> {
let once_ptr = ptr_to_field!(this, once);
LazyLock {
once: unsafe { Once::init(once_ptr) },
data: UnsafeCell::new(Data {
f: ManuallyDrop::new(f),
}),
}
}
pub fn force(&self) -> &T {
self.once.call_once(|| {
let data = unsafe { &mut *self.data.get() };
let f = unsafe { ManuallyDrop::take(&mut data.f) };
let value = f();
data.value = ManuallyDrop::new(value);
});
unsafe { &(*self.data.get()).value }
}
}
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.force()
}
}
unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {}
impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {}
impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {}
#[macro_export]
macro_rules! lazy_lock {
($name: ident, $type: ty, $fn: expr) => {
static $name: $crate::sync::LazyLock<$type> = {
let ptr = &raw const $name;
let fn_ = $fn;
unsafe { $crate::sync::LazyLock::init(ptr, fn_) }
};
};
}