1#![no_std]
2use ach_once::Once;
3use core::mem::MaybeUninit;
4use core::ops::Deref;
5use core::ptr;
6use core::sync::atomic::{AtomicBool, Ordering::SeqCst};
7
8pub struct Lazy<T, F = fn() -> T> {
9 val: Once<T>,
10 has_val: AtomicBool,
11 init: MaybeUninit<F>,
12}
13impl<T, F> Lazy<T, F> {
14 pub const fn new(f: F) -> Lazy<T, F> {
15 Lazy {
16 val: Once::new(),
17 has_val: AtomicBool::new(true),
18 init: MaybeUninit::new(f),
19 }
20 }
21}
22impl<T, F: FnOnce() -> T> Lazy<T, F> {
23 pub fn force(this: &Lazy<T, F>) -> &T {
25 if this
26 .has_val
27 .compare_exchange(true, false, SeqCst, SeqCst)
28 .is_ok()
29 {
30 let val = unsafe { ptr::read(this.init.as_ptr()) };
31 let value = val();
32 this.val.get_or_init(value)
33 } else {
34 this.val
35 .get()
36 .expect("Lazy instance has previously been poisoned")
37 }
38 }
39}
40impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> {
41 type Target = T;
42 fn deref(&self) -> &Self::Target {
43 Self::force(self)
44 }
45}
46impl<T, F> Drop for Lazy<T, F> {
47 fn drop(&mut self) {
48 if self
49 .has_val
50 .compare_exchange(true, false, SeqCst, SeqCst)
51 .is_ok()
52 {
53 unsafe { ptr::drop_in_place(self.init.as_mut_ptr()) };
54 }
55 }
56}