use std::fmt;
use std::fmt::Debug;
use std::ops::Deref;
use crate::LazyMutexId;
pub struct LazyLock<T, F = fn() -> T> {
#[allow(clippy::incompatible_msrv)]
inner: std::sync::LazyLock<T, F>,
id: LazyMutexId,
}
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
pub const fn new(f: F) -> LazyLock<T, F> {
Self {
id: LazyMutexId::new(),
#[allow(clippy::incompatible_msrv)]
inner: std::sync::LazyLock::new(f),
}
}
pub fn force(this: &LazyLock<T, F>) -> &T {
this
}
}
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.id.with_held(|| &*self.inner)
}
}
impl<T: Default> Default for LazyLock<T> {
fn default() -> Self {
Self::new(Default::default)
}
}
impl<T: Debug, F> Debug for LazyLock<T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
#[cfg(test)]
mod tests {
use crate::stdsync::Mutex;
use super::*;
#[test]
fn test_only_init_once() {
let mut init_counter = 0;
let lock = LazyLock::new(|| {
init_counter += 1;
42
});
assert_eq!(*lock, 42);
LazyLock::force(&lock);
drop(lock);
assert_eq!(init_counter, 1);
}
#[test]
#[should_panic(expected = "Found cycle")]
fn test_panic_with_cycle() {
let mutex = Mutex::new(());
let lock = LazyLock::new(|| *mutex.lock().unwrap());
LazyLock::force(&lock);
let _guard = mutex.lock().unwrap();
LazyLock::force(&lock);
}
}