blob: 4ba10a516d9353903018496825c6c9e61a9e8f0a [file] [log] [blame]
Haibo Huang52aa7852020-07-10 20:23:55 -07001//! Lazily initialized data.
2//! Used in generated code.
3
4use std::cell::UnsafeCell;
5use std::sync;
6
7/// Lazily initialized data.
8pub struct LazyV2<T: Sync> {
9 lock: sync::Once,
10 ptr: UnsafeCell<*const T>,
11}
12
13unsafe impl<T: Sync> Sync for LazyV2<T> {}
14
15impl<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.
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)]
35mod 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]
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}