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