1 | #![allow (dead_code)] // Only used on some platforms. |
2 | |
3 | // This is used to wrap pthread {Mutex, Condvar, RwLock} in. |
4 | |
5 | use crate::marker::PhantomData; |
6 | use crate::ops::{Deref, DerefMut}; |
7 | use crate::ptr::null_mut; |
8 | use crate::sync::atomic::{ |
9 | AtomicPtr, |
10 | Ordering::{AcqRel, Acquire}, |
11 | }; |
12 | |
13 | pub(crate) struct LazyBox<T: LazyInit> { |
14 | ptr: AtomicPtr<T>, |
15 | _phantom: PhantomData<T>, |
16 | } |
17 | |
18 | pub(crate) trait LazyInit { |
19 | /// This is called before the box is allocated, to provide the value to |
20 | /// move into the new box. |
21 | /// |
22 | /// It might be called more than once per LazyBox, as multiple threads |
23 | /// might race to initialize it concurrently, each constructing and initializing |
24 | /// their own box. All but one of them will be passed to `cancel_init` right after. |
25 | fn init() -> Box<Self>; |
26 | |
27 | /// Any surplus boxes from `init()` that lost the initialization race |
28 | /// are passed to this function for disposal. |
29 | /// |
30 | /// The default implementation calls destroy(). |
31 | fn cancel_init(x: Box<Self>) { |
32 | Self::destroy(x); |
33 | } |
34 | |
35 | /// This is called to destroy a used box. |
36 | /// |
37 | /// The default implementation just drops it. |
38 | fn destroy(_: Box<Self>) {} |
39 | } |
40 | |
41 | impl<T: LazyInit> LazyBox<T> { |
42 | #[inline ] |
43 | pub const fn new() -> Self { |
44 | Self { ptr: AtomicPtr::new(null_mut()), _phantom: PhantomData } |
45 | } |
46 | |
47 | #[inline ] |
48 | fn get_pointer(&self) -> *mut T { |
49 | let ptr = self.ptr.load(Acquire); |
50 | if ptr.is_null() { self.initialize() } else { ptr } |
51 | } |
52 | |
53 | #[cold ] |
54 | fn initialize(&self) -> *mut T { |
55 | let new_ptr = Box::into_raw(T::init()); |
56 | match self.ptr.compare_exchange(null_mut(), new_ptr, AcqRel, Acquire) { |
57 | Ok(_) => new_ptr, |
58 | Err(ptr) => { |
59 | // Lost the race to another thread. |
60 | // Drop the box we created, and use the one from the other thread instead. |
61 | T::cancel_init(unsafe { Box::from_raw(new_ptr) }); |
62 | ptr |
63 | } |
64 | } |
65 | } |
66 | } |
67 | |
68 | impl<T: LazyInit> Deref for LazyBox<T> { |
69 | type Target = T; |
70 | #[inline ] |
71 | fn deref(&self) -> &T { |
72 | unsafe { &*self.get_pointer() } |
73 | } |
74 | } |
75 | |
76 | impl<T: LazyInit> DerefMut for LazyBox<T> { |
77 | #[inline ] |
78 | fn deref_mut(&mut self) -> &mut T { |
79 | unsafe { &mut *self.get_pointer() } |
80 | } |
81 | } |
82 | |
83 | impl<T: LazyInit> Drop for LazyBox<T> { |
84 | fn drop(&mut self) { |
85 | let ptr: *mut T = *self.ptr.get_mut(); |
86 | if !ptr.is_null() { |
87 | T::destroy(unsafe { Box::from_raw(ptr) }); |
88 | } |
89 | } |
90 | } |
91 | |