use static_init::{destructor, dynamic, Finaly, LazyAccess, Phase}; use std::panic::catch_unwind; use std::sync::atomic::{AtomicU32, Ordering}; static FINALIZE_A_COUNT: AtomicU32 = AtomicU32::new(0); struct A(u32); impl A { fn new(v: u32) -> A { A(v) } } impl Finaly for A { fn finaly(&self) { FINALIZE_A_COUNT.fetch_add(1, Ordering::Relaxed); } } #[dynamic(lazy, finalize)] static NORMAL: A = A::new(33); #[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.0, 33); assert!(LazyAccess::phase(&NORMAL) == Phase::INITIALIZED | Phase::REGISTERED); assert_eq!(LazyAccess::try_get(&NORMAL).unwrap().0, 33); assert!(LazyAccess::phase(&NORMAL) == Phase::INITIALIZED | Phase::REGISTERED); assert_eq!(NORMAL.0, 33); assert_eq!(LazyAccess::get(&NORMAL).0, 33); } #[destructor(10)] extern "C" fn check_a_finalized() { assert_eq!(FINALIZE_A_COUNT.load(Ordering::Relaxed), 1) } static FINALIZE_B_COUNT: AtomicU32 = AtomicU32::new(0); struct B(u32); impl B { fn new(v: u32) -> Self { B(v) } } impl Finaly for B { fn finaly(&self) { FINALIZE_B_COUNT.fetch_add(1, Ordering::Relaxed); } } #[destructor(10)] extern "C" fn check_b_finalized() { assert_eq!(FINALIZE_B_COUNT.load(Ordering::Relaxed), 1) } static UNINIT_COUNT: AtomicU32 = AtomicU32::new(0); #[dynamic(lazy, finalize)] static INIT_MAY_PANICK: B = { if UNINIT_COUNT.fetch_add(1, Ordering::Relaxed) < 2 { panic!("Should not be seen"); } B::new(42) }; #[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.0).is_err()); assert_eq!(UNINIT_COUNT.load(Ordering::Relaxed), 1); assert_eq!( LazyAccess::phase(&INIT_MAY_PANICK), Phase::REGISTERED | Phase::INITIALIZATION_PANICKED | Phase::INITIALIZATION_SKIPED ); assert!(catch_unwind(|| INIT_MAY_PANICK.0).is_err()); assert_eq!(UNINIT_COUNT.load(Ordering::Relaxed), 2); assert_eq!( LazyAccess::phase(&INIT_MAY_PANICK), Phase::REGISTERED | Phase::INITIALIZATION_PANICKED | Phase::INITIALIZATION_SKIPED ); assert_eq!(INIT_MAY_PANICK.0, 42); assert_eq!(UNINIT_COUNT.load(Ordering::Relaxed), 3); assert!(LazyAccess::phase(&INIT_MAY_PANICK) == Phase::REGISTERED | Phase::INITIALIZED); assert_eq!(LazyAccess::try_get(&INIT_MAY_PANICK).unwrap().0, 42); assert!(LazyAccess::phase(&INIT_MAY_PANICK) == Phase::REGISTERED | Phase::INITIALIZED); assert_eq!(INIT_MAY_PANICK.0, 42); assert_eq!(LazyAccess::get(&INIT_MAY_PANICK).0, 42); } static FINALIZE_C_COUNT: AtomicU32 = AtomicU32::new(0); struct C(u32); impl C { fn new(v: u32) -> C { C(v) } } impl Finaly for C { fn finaly(&self) { FINALIZE_C_COUNT.fetch_add(1, Ordering::Relaxed); } } #[destructor(10)] extern "C" fn check_c_finalized() { println!("Without this message threads cannot be spawned in destructors..."); assert_eq!(FINALIZE_C_COUNT.load(Ordering::Relaxed), 1) } static UNINIT_ONCE_COUNT: AtomicU32 = AtomicU32::new(0); #[dynamic(lazy, finalize, try_init_once)] static UNINITIALIZABLE: C = { 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.0).is_err()); assert_eq!(UNINIT_ONCE_COUNT.load(Ordering::Relaxed), 1); assert_eq!( LazyAccess::phase(&UNINITIALIZABLE), Phase::REGISTERED | Phase::INITIALIZATION_PANICKED | Phase::INITIALIZATION_SKIPED ); assert!(catch_unwind(|| UNINITIALIZABLE.0).is_err()); assert_eq!(UNINIT_ONCE_COUNT.load(Ordering::Relaxed), 1); assert_eq!( LazyAccess::phase(&UNINITIALIZABLE), Phase::REGISTERED | Phase::INITIALIZATION_PANICKED | Phase::INITIALIZATION_SKIPED ); assert_eq!(UNINIT_ONCE_COUNT.load(Ordering::Relaxed), 1); } #[dynamic(lazy, finalize, try_init_once)] static NORMAL_WITH_TOLERANCE: C = C::new(33); #[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.0, 33); assert!(LazyAccess::phase(&NORMAL_WITH_TOLERANCE) == Phase::INITIALIZED | Phase::REGISTERED); assert_eq!(LazyAccess::try_get(&NORMAL_WITH_TOLERANCE).unwrap().0, 33); assert!(LazyAccess::phase(&NORMAL_WITH_TOLERANCE) == Phase::INITIALIZED | Phase::REGISTERED); assert_eq!(NORMAL_WITH_TOLERANCE.0, 33); assert_eq!(LazyAccess::get(&NORMAL_WITH_TOLERANCE).0, 33); } #[dynamic(lazy, finalize)] static ATEMPT_AFTER_MAIN: D = D::new(33); #[dynamic(lazy, finalize, tolerate_leak)] //never droped static ATEMPT_AFTER_MAIN_TOL: D = D::new(33); static FINALIZE_D_COUNT: AtomicU32 = AtomicU32::new(0); struct D(u32); impl D { fn new(v: u32) -> D { D(v) } } impl Finaly for D { fn finaly(&self) { FINALIZE_D_COUNT.fetch_add(1, Ordering::Relaxed); } } #[destructor(100)] extern "C" fn check_d_finalized() { assert_eq!(FINALIZE_D_COUNT.load(Ordering::Relaxed), 0) } #[destructor(10)] extern "C" fn check_late() { assert!(LazyAccess::phase(&ATEMPT_AFTER_MAIN_TOL).is_empty()); assert!(LazyAccess::try_get(&ATEMPT_AFTER_MAIN_TOL).is_err()); assert!(LazyAccess::phase(&ATEMPT_AFTER_MAIN_TOL).is_empty()); assert_eq!(ATEMPT_AFTER_MAIN_TOL.0, 33); assert!( LazyAccess::phase(&ATEMPT_AFTER_MAIN_TOL) == Phase::INITIALIZED | Phase::REGISTRATION_REFUSED ); assert_eq!(LazyAccess::try_get(&ATEMPT_AFTER_MAIN_TOL).unwrap().0, 33); assert!( LazyAccess::phase(&ATEMPT_AFTER_MAIN_TOL) == Phase::INITIALIZED | Phase::REGISTRATION_REFUSED ); assert_eq!(ATEMPT_AFTER_MAIN_TOL.0, 33); assert_eq!(LazyAccess::get(&ATEMPT_AFTER_MAIN_TOL).0, 33); //It should refuse to initialize be registration are closed assert!(LazyAccess::phase(&ATEMPT_AFTER_MAIN).is_empty()); assert!(LazyAccess::try_get(&ATEMPT_AFTER_MAIN).is_err()); assert!(LazyAccess::phase(&ATEMPT_AFTER_MAIN).is_empty()); //assert!(catch_unwind(|| ATEMPT_AFTER_MAIN.0).is_err()); }