1 //! Lazily initialized data. 2 //! Used in generated code. 3 4 use std::mem; 5 use std::sync; 6 7 /// Lasily initialized data. 8 pub struct Lazy<T> { 9 #[doc(hidden)] 10 pub lock: sync::Once, 11 #[doc(hidden)] 12 pub ptr: *const T, 13 } 14 15 impl<T> Lazy<T> { 16 /// Uninitialized `Lazy` object. 17 /// 18 /// The initializer is added in rust-protobuf 2.11, for compatibility with 19 /// previously generated code, existing fields are kept public. 20 pub const INIT: Lazy<T> = Lazy { 21 lock: sync::Once::new(), 22 ptr: 0 as *const T, 23 }; 24 25 /// Get lazy field value, initialize it with given function if not yet. get<F>(&'static mut self, init: F) -> &'static T where F: FnOnce() -> T,26 pub fn get<F>(&'static mut self, init: F) -> &'static T 27 where 28 F: FnOnce() -> T, 29 { 30 // ~ decouple the lifetimes of 'self' and 'self.lock' such we 31 // can initialize self.ptr in the call_once closure (note: we 32 // do have to initialize self.ptr in the closure to guarantee 33 // the ptr is valid for all calling threads at any point in 34 // time) 35 let lock: &sync::Once = unsafe { mem::transmute(&self.lock) }; 36 lock.call_once(|| unsafe { 37 self.ptr = mem::transmute(Box::new(init())); 38 }); 39 unsafe { &*self.ptr } 40 } 41 } 42 43 /// Used to initialize `lock` field in `Lazy` struct. 44 #[deprecated( 45 since = "2.11", 46 note = "Regenerate .proto files to use safer initializer" 47 )] 48 pub const ONCE_INIT: sync::Once = sync::Once::new(); 49 50 #[cfg(test)] 51 mod test { 52 use super::Lazy; 53 use std::sync::atomic::AtomicIsize; 54 use std::sync::atomic::Ordering; 55 use std::sync::Arc; 56 use std::sync::Barrier; 57 use std::thread; 58 59 #[test] many_threads_calling_get()60 fn many_threads_calling_get() { 61 const N_THREADS: usize = 32; 62 const N_ITERS_IN_THREAD: usize = 32; 63 const N_ITERS: usize = 16; 64 65 static mut LAZY: Lazy<String> = Lazy::INIT; 66 static CALL_COUNT: AtomicIsize = AtomicIsize::new(0); 67 68 let value = "Hello, world!".to_owned(); 69 70 for _ in 0..N_ITERS { 71 // Reset mutable state. 72 unsafe { 73 LAZY = Lazy::INIT; 74 } 75 CALL_COUNT.store(0, Ordering::SeqCst); 76 77 // Create a bunch of threads, all calling .get() at the same time. 78 let mut threads = vec![]; 79 let barrier = Arc::new(Barrier::new(N_THREADS)); 80 81 for _ in 0..N_THREADS { 82 let cloned_value_thread = value.clone(); 83 let cloned_barrier = barrier.clone(); 84 threads.push(thread::spawn(move || { 85 // Ensure all threads start at once to maximise contention. 86 cloned_barrier.wait(); 87 for _ in 0..N_ITERS_IN_THREAD { 88 assert_eq!(&cloned_value_thread, unsafe { 89 LAZY.get(|| { 90 CALL_COUNT.fetch_add(1, Ordering::SeqCst); 91 cloned_value_thread.clone() 92 }) 93 }); 94 } 95 })); 96 } 97 98 for thread in threads { 99 thread.join().unwrap(); 100 } 101 102 assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1); 103 } 104 } 105 } 106