use std::cell::LazyCell; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::SeqCst; use std::sync::{LazyLock, Mutex, OnceLock}; use std::{panic, thread}; fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() } #[test] fn lazy_default() { static CALLED: AtomicUsize = AtomicUsize::new(0); struct Foo(u8); impl Default for Foo { fn default() -> Self { CALLED.fetch_add(1, SeqCst); Foo(42) } } let lazy: LazyCell> = <_>::default(); assert_eq!(CALLED.load(SeqCst), 0); assert_eq!(lazy.lock().unwrap().0, 42); assert_eq!(CALLED.load(SeqCst), 1); lazy.lock().unwrap().0 = 21; assert_eq!(lazy.lock().unwrap().0, 21); assert_eq!(CALLED.load(SeqCst), 1); } #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn sync_lazy_new() { static CALLED: AtomicUsize = AtomicUsize::new(0); static SYNC_LAZY: LazyLock = LazyLock::new(|| { CALLED.fetch_add(1, SeqCst); 92 }); assert_eq!(CALLED.load(SeqCst), 0); spawn_and_wait(|| { let y = *SYNC_LAZY - 30; assert_eq!(y, 62); assert_eq!(CALLED.load(SeqCst), 1); }); let y = *SYNC_LAZY - 30; assert_eq!(y, 62); assert_eq!(CALLED.load(SeqCst), 1); } #[test] fn sync_lazy_default() { static CALLED: AtomicUsize = AtomicUsize::new(0); struct Foo(u8); impl Default for Foo { fn default() -> Self { CALLED.fetch_add(1, SeqCst); Foo(42) } } let lazy: LazyLock> = <_>::default(); assert_eq!(CALLED.load(SeqCst), 0); assert_eq!(lazy.lock().unwrap().0, 42); assert_eq!(CALLED.load(SeqCst), 1); lazy.lock().unwrap().0 = 21; assert_eq!(lazy.lock().unwrap().0, 21); assert_eq!(CALLED.load(SeqCst), 1); } #[test] #[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads fn static_sync_lazy() { static XS: LazyLock> = LazyLock::new(|| { let mut xs = Vec::new(); xs.push(1); xs.push(2); xs.push(3); xs }); spawn_and_wait(|| { assert_eq!(&*XS, &vec![1, 2, 3]); }); assert_eq!(&*XS, &vec![1, 2, 3]); } #[test] fn static_sync_lazy_via_fn() { fn xs() -> &'static Vec { static XS: OnceLock> = OnceLock::new(); XS.get_or_init(|| { let mut xs = Vec::new(); xs.push(1); xs.push(2); xs.push(3); xs }) } assert_eq!(xs(), &vec![1, 2, 3]); } // Check that we can infer `T` from closure's type. #[test] fn lazy_type_inference() { let _ = LazyCell::new(|| ()); } #[test] fn is_sync_send() { fn assert_traits() {} assert_traits::>(); } #[test] fn lazy_force_mut() { let s = "abc".to_owned(); let mut lazy = LazyLock::new(move || s); LazyLock::force_mut(&mut lazy); let p = LazyLock::force_mut(&mut lazy); p.clear(); LazyLock::force_mut(&mut lazy); } #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn lazy_poisoning() { let x: LazyCell = LazyCell::new(|| panic!("kaboom")); for _ in 0..2 { let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); assert!(res.is_err()); } } /// Verifies that when a `LazyLock` is poisoned, it panics with the correct error message ("LazyLock /// instance has previously been poisoned") instead of the underlying `Once` error message. #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] #[should_panic(expected = "LazyLock instance has previously been poisoned")] fn lazy_lock_deref_panic() { let lazy: LazyLock = LazyLock::new(|| panic!("initialization failed")); // First access will panic during initialization. let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { let _ = &*lazy; })); // Second access should panic with the poisoned message. let _ = &*lazy; } #[test] #[should_panic(expected = "LazyLock instance has previously been poisoned")] fn lazy_lock_deref_mut_panic() { let mut lazy: LazyLock = LazyLock::new(|| panic!("initialization failed")); // First access will panic during initialization. let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { let _ = LazyLock::force_mut(&mut lazy); })); // Second access should panic with the poisoned message. let _ = &*lazy; } /// Verifies that when the initialization closure panics with a custom message, that message is /// preserved and not overridden by `LazyLock`. #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] #[should_panic(expected = "custom panic message from closure")] fn lazy_lock_preserves_closure_panic_message() { let lazy: LazyLock = LazyLock::new(|| panic!("custom panic message from closure")); // This should panic with the original message from the closure. let _ = &*lazy; }