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