]>
Commit | Line | Data |
---|---|---|
532ac7d7 XL |
1 | use crate::cell::Cell; |
2 | use crate::ptr; | |
3 | use crate::sync::Arc; | |
4 | use crate::sys_common; | |
5 | use crate::sys_common::mutex::Mutex; | |
c34b1796 AL |
6 | |
7 | pub struct Lazy<T> { | |
b7449926 | 8 | // We never call `lock.init()`, so it is UB to attempt to acquire this mutex reentrantly! |
a7813a04 | 9 | lock: Mutex, |
62682a34 | 10 | ptr: Cell<*mut Arc<T>>, |
c34b1796 AL |
11 | } |
12 | ||
94b46f34 | 13 | #[inline] |
dfeec247 XL |
14 | const fn done<T>() -> *mut Arc<T> { |
15 | 1_usize as *mut _ | |
16 | } | |
94b46f34 | 17 | |
c34b1796 AL |
18 | unsafe impl<T> Sync for Lazy<T> {} |
19 | ||
b7449926 XL |
20 | impl<T> Lazy<T> { |
21 | pub const fn new() -> Lazy<T> { | |
dfeec247 | 22 | Lazy { lock: Mutex::new(), ptr: Cell::new(ptr::null_mut()) } |
62682a34 | 23 | } |
b7449926 | 24 | } |
62682a34 | 25 | |
b7449926 XL |
26 | impl<T: Send + Sync + 'static> Lazy<T> { |
27 | /// Safety: `init` must not call `get` on the variable that is being | |
28 | /// initialized. | |
29 | pub unsafe fn get(&'static self, init: fn() -> Arc<T>) -> Option<Arc<T>> { | |
30 | let _guard = self.lock.lock(); | |
31 | let ptr = self.ptr.get(); | |
32 | if ptr.is_null() { | |
33 | Some(self.init(init)) | |
34 | } else if ptr == done() { | |
35 | None | |
36 | } else { | |
37 | Some((*ptr).clone()) | |
c34b1796 AL |
38 | } |
39 | } | |
40 | ||
b7449926 XL |
41 | // Must only be called with `lock` held |
42 | unsafe fn init(&'static self, init: fn() -> Arc<T>) -> Arc<T> { | |
c34b1796 AL |
43 | // If we successfully register an at exit handler, then we cache the |
44 | // `Arc` allocation in our own internal box (it will get deallocated by | |
45 | // the at exit handler). Otherwise we just return the freshly allocated | |
46 | // `Arc`. | |
e9174d1e | 47 | let registered = sys_common::at_exit(move || { |
94b46f34 XL |
48 | let ptr = { |
49 | let _guard = self.lock.lock(); | |
50 | self.ptr.replace(done()) | |
51 | }; | |
c34b1796 AL |
52 | drop(Box::from_raw(ptr)) |
53 | }); | |
b7449926 XL |
54 | // This could reentrantly call `init` again, which is a problem |
55 | // because our `lock` allows reentrancy! | |
56 | // That's why `get` is unsafe and requires the caller to ensure no reentrancy happens. | |
57 | let ret = init(); | |
c34b1796 | 58 | if registered.is_ok() { |
62682a34 | 59 | self.ptr.set(Box::into_raw(Box::new(ret.clone()))); |
c34b1796 | 60 | } |
e9174d1e | 61 | ret |
c34b1796 AL |
62 | } |
63 | } |