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}