embassy_sync/
lazy_lock.rs1use core::cell::UnsafeCell;
4use core::mem::ManuallyDrop;
5use core::sync::atomic::{AtomicBool, Ordering};
6
7pub struct LazyLock<T, F = fn() -> T> {
25 init: AtomicBool,
26 data: UnsafeCell<Data<T, F>>,
27}
28
29union Data<T, F> {
30 value: ManuallyDrop<T>,
31 f: ManuallyDrop<F>,
32}
33
34unsafe impl<T, F> Sync for LazyLock<T, F> {}
35
36impl<T, F: FnOnce() -> T> LazyLock<T, F> {
37 pub const fn new(init_fn: F) -> Self {
39 Self {
40 init: AtomicBool::new(false),
41 data: UnsafeCell::new(Data {
42 f: ManuallyDrop::new(init_fn),
43 }),
44 }
45 }
46
47 #[inline]
50 pub fn get(&self) -> &T {
51 self.ensure_init_fast();
52 unsafe { &(*self.data.get()).value }
53 }
54
55 #[inline]
59 pub fn into_inner(self) -> T {
60 self.ensure_init_fast();
61 let this = ManuallyDrop::new(self);
62 let data = unsafe { core::ptr::read(&this.data) }.into_inner();
63
64 ManuallyDrop::into_inner(unsafe { data.value })
65 }
66
67 #[inline]
74 fn ensure_init_fast(&self) {
75 if !self.init.load(Ordering::Acquire) {
76 self.ensure_init();
77 }
78 }
79
80 fn ensure_init(&self) {
84 critical_section::with(|_| {
85 if !self.init.load(Ordering::Acquire) {
86 let data = unsafe { &mut *self.data.get() };
87 let f = unsafe { ManuallyDrop::take(&mut data.f) };
88 let value = f();
89 data.value = ManuallyDrop::new(value);
90
91 self.init.store(true, Ordering::Release);
92 }
93 });
94 }
95}
96
97impl<T, F> Drop for LazyLock<T, F> {
98 fn drop(&mut self) {
99 if self.init.load(Ordering::Acquire) {
100 unsafe { ManuallyDrop::drop(&mut self.data.get_mut().value) };
101 } else {
102 unsafe { ManuallyDrop::drop(&mut self.data.get_mut().f) };
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use core::sync::atomic::{AtomicU32, Ordering};
110
111 use super::*;
112
113 #[test]
114 fn test_lazy_lock() {
115 static VALUE: LazyLock<u32> = LazyLock::new(|| 20);
116 let reference = VALUE.get();
117 assert_eq!(reference, &20);
118 }
119 #[test]
120 fn test_lazy_lock_into_inner() {
121 let lazy: LazyLock<u32> = LazyLock::new(|| 20);
122 let value = lazy.into_inner();
123 assert_eq!(value, 20);
124 }
125
126 static DROP_CHECKER: AtomicU32 = AtomicU32::new(0);
127 struct DropCheck;
128
129 impl Drop for DropCheck {
130 fn drop(&mut self) {
131 DROP_CHECKER.fetch_add(1, Ordering::Acquire);
132 }
133 }
134
135 #[test]
136 fn test_lazy_drop() {
137 let lazy: LazyLock<DropCheck> = LazyLock::new(|| DropCheck);
138 assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 0);
139 lazy.get();
140 drop(lazy);
141 assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 1);
142
143 let dropper = DropCheck;
144 let lazy_fn: LazyLock<u32, _> = LazyLock::new(move || {
145 let _a = dropper;
146 20
147 });
148 assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 1);
149 drop(lazy_fn);
150 assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 2);
151 }
152}