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