1 //! Lazily initialized data. 2 //! Used in generated code. 3 4 use std::cell::UnsafeCell; 5 use std::sync; 6 7 /// Lazily initialized data. 8 pub struct LazyV2<T: Sync> { 9 lock: sync::Once, 10 ptr: UnsafeCell<*const T>, 11 } 12 13 unsafe impl<T: Sync> Sync for LazyV2<T> {} 14 15 impl<T: Sync> LazyV2<T> { 16 /// Uninitialized `Lazy` object. 17 pub const INIT: LazyV2<T> = LazyV2 { 18 lock: sync::Once::new(), 19 ptr: UnsafeCell::new(0 as *const T), 20 }; 21 22 /// Get lazy field value, initialize it with given function if not yet. get<F>(&'static self, init: F) -> &'static T where F: FnOnce() -> T,23 pub fn get<F>(&'static self, init: F) -> &'static T 24 where 25 F: FnOnce() -> T, 26 { 27 self.lock.call_once(|| unsafe { 28 *self.ptr.get() = Box::into_raw(Box::new(init())); 29 }); 30 unsafe { &**self.ptr.get() } 31 } 32 } 33 34 #[cfg(test)] 35 mod test { 36 use super::LazyV2; 37 use std::sync::atomic::AtomicIsize; 38 use std::sync::atomic::Ordering; 39 use std::sync::Arc; 40 use std::sync::Barrier; 41 use std::thread; 42 43 #[test] many_threads_calling_get()44 fn many_threads_calling_get() { 45 const N_THREADS: usize = 32; 46 const N_ITERS_IN_THREAD: usize = 32; 47 const N_ITERS: usize = 16; 48 49 static mut LAZY: LazyV2<String> = LazyV2::INIT; 50 static CALL_COUNT: AtomicIsize = AtomicIsize::new(0); 51 52 let value = "Hello, world!".to_owned(); 53 54 for _ in 0..N_ITERS { 55 // Reset mutable state. 56 unsafe { 57 LAZY = LazyV2::INIT; 58 } 59 CALL_COUNT.store(0, Ordering::SeqCst); 60 61 // Create a bunch of threads, all calling .get() at the same time. 62 let mut threads = vec![]; 63 let barrier = Arc::new(Barrier::new(N_THREADS)); 64 65 for _ in 0..N_THREADS { 66 let cloned_value_thread = value.clone(); 67 let cloned_barrier = barrier.clone(); 68 threads.push(thread::spawn(move || { 69 // Ensure all threads start at once to maximise contention. 70 cloned_barrier.wait(); 71 for _ in 0..N_ITERS_IN_THREAD { 72 assert_eq!(&cloned_value_thread, unsafe { 73 LAZY.get(|| { 74 CALL_COUNT.fetch_add(1, Ordering::SeqCst); 75 cloned_value_thread.clone() 76 }) 77 }); 78 } 79 })); 80 } 81 82 for thread in threads { 83 thread.join().unwrap(); 84 } 85 86 assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1); 87 } 88 } 89 } 90