use core::{
cell::UnsafeCell,
fmt,
mem::ManuallyDrop,
ops::Deref,
panic::{RefUnwindSafe, UnwindSafe},
ptr,
};
use crate::sync::{nonpoison::Once, ExclusiveState};
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> {
#[inline]
pub const fn new(f: F) -> LazyLock<T, F> {
LazyLock {
once: Once::new(),
data: UnsafeCell::new(Data {
f: ManuallyDrop::new(f),
}),
}
}
#[inline]
#[cfg(test)]
pub(crate) fn preinit(value: T) -> LazyLock<T, F> {
let once = Once::new();
once.call_once(|| {});
LazyLock {
once,
data: UnsafeCell::new(Data {
value: ManuallyDrop::new(value),
}),
}
}
pub fn into_inner(mut this: Self) -> Result<T, F> {
let state = this.once.state();
match state {
ExclusiveState::Poisoned => panic_poisoned(),
state => {
let this = ManuallyDrop::new(this);
let data = unsafe { ptr::read(&this.data) }.into_inner();
match state {
ExclusiveState::Incomplete => Err(ManuallyDrop::into_inner(unsafe { data.f })),
ExclusiveState::Complete => Ok(ManuallyDrop::into_inner(unsafe { data.value })),
ExclusiveState::Poisoned => unreachable!(),
}
},
}
}
#[inline]
pub fn force_mut(this: &mut LazyLock<T, F>) -> &mut T {
#[cold]
unsafe fn really_init_mut<T, F: FnOnce() -> T>(this: &mut LazyLock<T, F>) -> &mut T {
struct PoisonOnPanic<'a, T, F>(&'a mut LazyLock<T, F>);
impl<T, F> Drop for PoisonOnPanic<'_, T, F> {
#[inline]
fn drop(&mut self) {
self.0.once.set_state(ExclusiveState::Poisoned);
}
}
let f = unsafe { ManuallyDrop::take(&mut this.data.get_mut().f) };
let guard = PoisonOnPanic(this);
let data = f();
guard.0.data.get_mut().value = ManuallyDrop::new(data);
guard.0.once.set_state(ExclusiveState::Complete);
core::mem::forget(guard);
unsafe { &mut this.data.get_mut().value }
}
let state = this.once.state();
match state {
ExclusiveState::Poisoned => panic_poisoned(),
ExclusiveState::Complete => unsafe { &mut this.data.get_mut().value },
ExclusiveState::Incomplete => unsafe { really_init_mut(this) },
}
}
#[inline]
pub fn force(this: &LazyLock<T, F>) -> &T {
this.once.call_once(|| {
let data = unsafe { &mut *this.data.get() };
let f = unsafe { ManuallyDrop::take(&mut data.f) };
let value = f();
data.value = ManuallyDrop::new(value);
});
unsafe { &(*this.data.get()).value }
}
}
impl<T, F> LazyLock<T, F> {
#[inline]
pub fn get_mut(this: &mut LazyLock<T, F>) -> Option<&mut T> {
let state = this.once.state();
match state {
ExclusiveState::Complete => Some(unsafe { &mut this.data.get_mut().value }),
_ => None,
}
}
#[inline]
pub fn get(this: &LazyLock<T, F>) -> Option<&T> {
if this.once.is_completed() {
Some(unsafe { &(*this.data.get()).value })
} else {
None
}
}
}
impl<T, F> Drop for LazyLock<T, F> {
fn drop(&mut self) {
match self.once.state() {
ExclusiveState::Incomplete => unsafe { ManuallyDrop::drop(&mut self.data.get_mut().f) },
ExclusiveState::Complete => unsafe {
ManuallyDrop::drop(&mut self.data.get_mut().value)
},
ExclusiveState::Poisoned => {},
}
}
}
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
LazyLock::force(self)
}
}
impl<T: Default> Default for LazyLock<T> {
#[inline]
fn default() -> LazyLock<T> {
LazyLock::new(T::default)
}
}
impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_tuple("LazyLock");
match LazyLock::get(self) {
Some(v) => d.field(v),
None => d.field(&format_args!("<uninit>")),
};
d.finish()
}
}
#[cold]
#[inline(never)]
fn panic_poisoned() -> ! {
panic!("LazyLock instance has previously been poisoned")
}
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> {}