1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use std::mem;
use std::sync;
pub struct Lazy<T> {
pub lock: sync::Once,
pub ptr: *const T,
}
impl<T> Lazy<T> {
pub fn get<F>(&'static mut self, init: F) -> &'static T
where
F : FnOnce() -> T,
{
let lock: &sync::Once = unsafe { mem::transmute(&self.lock) };
lock.call_once(|| unsafe {
self.ptr = mem::transmute(Box::new(init()));
});
unsafe { &*self.ptr }
}
}
pub const ONCE_INIT: sync::Once = sync::ONCE_INIT;
#[cfg(test)]
mod test {
use super::{Lazy, ONCE_INIT};
use std::thread;
use std::sync::{Arc, Barrier};
use std::sync::atomic::{ATOMIC_ISIZE_INIT, AtomicIsize, Ordering};
#[test]
fn many_threads_calling_get() {
const N_THREADS: usize = 32;
const N_ITERS_IN_THREAD: usize = 32;
const N_ITERS: usize = 16;
static mut LAZY: Lazy<String> = Lazy {
lock: ONCE_INIT,
ptr: 0 as *const String,
};
static CALL_COUNT: AtomicIsize = ATOMIC_ISIZE_INIT;
let value = "Hello, world!".to_owned();
for _ in 0..N_ITERS {
unsafe {
LAZY = Lazy {
lock: ONCE_INIT,
ptr: 0 as *const String,
}
}
CALL_COUNT.store(0, Ordering::SeqCst);
let mut threads = vec![];
let barrier = Arc::new(Barrier::new(N_THREADS));
for _ in 0..N_THREADS {
let cloned_value_thread = value.clone();
let cloned_barrier = barrier.clone();
threads.push(thread::spawn(move || {
cloned_barrier.wait();
for _ in 0..N_ITERS_IN_THREAD {
assert_eq!(&cloned_value_thread, unsafe {
LAZY.get(|| {
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
cloned_value_thread.clone()
})
});
}
}));
}
for thread in threads {
thread.join().unwrap();
}
assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
}
}
}