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