#![cfg(feature = "thread_local")] #![feature(thread_local)] use static_init::{dynamic, LazyAccess, Phase, UnSyncLazy}; use std::panic::catch_unwind; use std::sync::atomic::{AtomicU32, Ordering}; use std::thread::spawn; #[dynamic] #[thread_local] static NORMAL: Vec = vec![1, 2]; #[test] fn normal() { assert!(LazyAccess::phase(&NORMAL).is_empty()); assert!(LazyAccess::try_get(&NORMAL).is_err()); assert!(LazyAccess::phase(&NORMAL).is_empty()); assert_eq!(NORMAL.len(), 2); assert!(LazyAccess::phase(&NORMAL) == Phase::INITIALIZED); assert!(LazyAccess::try_get(&NORMAL).unwrap().len() == 2); assert!(LazyAccess::phase(&NORMAL) == Phase::INITIALIZED); assert_eq!(*NORMAL, vec![1, 2]); assert_eq!(*LazyAccess::get(&NORMAL), vec![1, 2]); spawn(|| { assert!(LazyAccess::phase(&NORMAL).is_empty()); assert_eq!(NORMAL.len(), 2); assert!(LazyAccess::phase(&NORMAL) == Phase::INITIALIZED); }) .join() .unwrap(); } static UNINIT_COUNT: AtomicU32 = AtomicU32::new(0); #[thread_local] #[dynamic(lazy)] //lazy or lesser_lazy are equivalent for thread_local static INIT_MAY_PANICK: Vec = { if UNINIT_COUNT.fetch_add(1, Ordering::Relaxed) < 2 { panic!("Should not be seen"); } vec![1, 2] }; #[test] fn init_may_panick() { assert!(LazyAccess::phase(&INIT_MAY_PANICK).is_empty()); assert!(LazyAccess::try_get(&INIT_MAY_PANICK).is_err()); assert!(LazyAccess::phase(&INIT_MAY_PANICK).is_empty()); assert!(catch_unwind(|| INIT_MAY_PANICK.len()).is_err()); assert_eq!(UNINIT_COUNT.load(Ordering::Relaxed), 1); assert_eq!( LazyAccess::phase(&INIT_MAY_PANICK), Phase::INITIALIZATION_PANICKED | Phase::INITIALIZATION_SKIPED ); assert!(catch_unwind(|| INIT_MAY_PANICK.len()).is_err()); assert_eq!(UNINIT_COUNT.load(Ordering::Relaxed), 2); assert_eq!( LazyAccess::phase(&INIT_MAY_PANICK), Phase::INITIALIZATION_PANICKED | Phase::INITIALIZATION_SKIPED ); assert_eq!(INIT_MAY_PANICK.len(), 2); assert_eq!(UNINIT_COUNT.load(Ordering::Relaxed), 3); assert!(LazyAccess::phase(&INIT_MAY_PANICK) == Phase::INITIALIZED); assert!(LazyAccess::try_get(&INIT_MAY_PANICK).unwrap().len() == 2); assert!(LazyAccess::phase(&INIT_MAY_PANICK) == Phase::INITIALIZED); assert_eq!(*INIT_MAY_PANICK, vec![1, 2]); assert_eq!(*LazyAccess::get(&INIT_MAY_PANICK), vec![1, 2]); spawn(|| { assert!(LazyAccess::phase(&INIT_MAY_PANICK).is_empty()); assert_eq!(INIT_MAY_PANICK.len(), 2); assert!(LazyAccess::phase(&INIT_MAY_PANICK) == Phase::INITIALIZED); }) .join() .unwrap(); } static UNINIT_ONCE_COUNT: AtomicU32 = AtomicU32::new(0); #[dynamic(lazy, try_init_once)] #[thread_local] static UNINITIALIZABLE: Vec = { UNINIT_ONCE_COUNT.fetch_add(1, Ordering::Relaxed); panic!("Panicked on purpose") }; #[test] fn init_may_panick_intolerant() { assert!(LazyAccess::phase(&UNINITIALIZABLE).is_empty()); assert!(LazyAccess::try_get(&UNINITIALIZABLE).is_err()); assert!(LazyAccess::phase(&UNINITIALIZABLE).is_empty()); assert!(catch_unwind(|| UNINITIALIZABLE.len()).is_err()); assert_eq!(UNINIT_ONCE_COUNT.load(Ordering::Relaxed), 1); assert_eq!( LazyAccess::phase(&UNINITIALIZABLE), Phase::INITIALIZATION_PANICKED | Phase::INITIALIZATION_SKIPED ); assert!(catch_unwind(|| UNINITIALIZABLE.len()).is_err()); assert_eq!(UNINIT_ONCE_COUNT.load(Ordering::Relaxed), 1); assert_eq!( LazyAccess::phase(&UNINITIALIZABLE), Phase::INITIALIZATION_PANICKED | Phase::INITIALIZATION_SKIPED ); assert_eq!(UNINIT_ONCE_COUNT.load(Ordering::Relaxed), 1); } #[dynamic(lazy, try_init_once)] #[thread_local] static NORMAL_WITH_TOLERANCE: Vec = vec![1, 2]; #[test] fn normal_with_tolerance() { assert!(LazyAccess::phase(&NORMAL_WITH_TOLERANCE).is_empty()); assert!(LazyAccess::try_get(&NORMAL_WITH_TOLERANCE).is_err()); assert!(LazyAccess::phase(&NORMAL_WITH_TOLERANCE).is_empty()); assert_eq!(NORMAL_WITH_TOLERANCE.len(), 2); assert!(LazyAccess::phase(&NORMAL_WITH_TOLERANCE) == Phase::INITIALIZED); assert_eq!( LazyAccess::try_get(&NORMAL_WITH_TOLERANCE).unwrap().len(), 2 ); assert!(LazyAccess::phase(&NORMAL_WITH_TOLERANCE) == Phase::INITIALIZED); assert_eq!(*NORMAL_WITH_TOLERANCE, vec![1, 2]); assert_eq!(LazyAccess::get(&NORMAL_WITH_TOLERANCE).len(), 2); } #[test] fn local_lazy() { let v = UnSyncLazy::new(|| vec![1, 2]); assert!(LazyAccess::phase(&v).is_empty()); assert!(LazyAccess::try_get(&v).is_err()); assert!(LazyAccess::phase(&v).is_empty()); assert_eq!(v.len(), 2); assert!(LazyAccess::phase(&v) == Phase::INITIALIZED); assert!(LazyAccess::try_get(&v).unwrap().len() == 2); assert!(LazyAccess::phase(&v) == Phase::INITIALIZED); assert_eq!(*v, vec![1, 2]); assert_eq!(*LazyAccess::get(&v), vec![1, 2]); let mut drop_count: i32 = 0; struct A<'a>(&'a mut i32); impl<'a> Drop for A<'a> { fn drop(&mut self) { *self.0 += 1; } } { UnSyncLazy::new(|| A(&mut drop_count)); } assert_eq!(drop_count, 0); { let v = UnSyncLazy::new(|| A(&mut drop_count)); LazyAccess::init(&v); } assert_eq!(drop_count, 1); } #[test] fn local_lazy_mut() { let mut v = UnSyncLazy::new(|| vec![1, 2]); assert!(LazyAccess::phase(&v).is_empty()); assert!(LazyAccess::try_get(&v).is_err()); assert!(UnSyncLazy::try_get_mut(&mut v).is_err()); assert!(LazyAccess::phase(&v).is_empty()); v.push(3); assert_eq!(LazyAccess::phase(&v), Phase::INITIALIZED); assert_eq!(LazyAccess::try_get(&v).unwrap().len(), 3); UnSyncLazy::try_get_mut(&mut v).unwrap().push(4); UnSyncLazy::get_mut(&mut v).push(5); assert_eq!(*v, vec![1, 2, 3, 4, 5]); }