diff options
Diffstat (limited to 'embassy-sync/src')
| -rw-r--r-- | embassy-sync/src/lazy_lock.rs | 41 |
1 files changed, 31 insertions, 10 deletions
diff --git a/embassy-sync/src/lazy_lock.rs b/embassy-sync/src/lazy_lock.rs index 2b5742491..cf88bfdf8 100644 --- a/embassy-sync/src/lazy_lock.rs +++ b/embassy-sync/src/lazy_lock.rs | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | //! Synchronization primitive for initializing a value once, allowing others to get a reference to the value. | 1 | //! Synchronization primitive for initializing a value once, allowing others to get a reference to the value. |
| 2 | 2 | ||
| 3 | use core::cell::Cell; | 3 | use core::cell::UnsafeCell; |
| 4 | use core::mem::MaybeUninit; | 4 | use core::mem::ManuallyDrop; |
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | 5 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 6 | 6 | ||
| 7 | /// The `LazyLock` is a synchronization primitive that allows for | 7 | /// The `LazyLock` is a synchronization primitive that allows for |
| @@ -23,8 +23,12 @@ use core::sync::atomic::{AtomicBool, Ordering}; | |||
| 23 | /// ``` | 23 | /// ``` |
| 24 | pub struct LazyLock<T, F = fn() -> T> { | 24 | pub struct LazyLock<T, F = fn() -> T> { |
| 25 | init: AtomicBool, | 25 | init: AtomicBool, |
| 26 | init_fn: Cell<Option<F>>, | 26 | data: UnsafeCell<Data<T, F>>, |
| 27 | data: Cell<MaybeUninit<T>>, | 27 | } |
| 28 | |||
| 29 | union Data<T, F> { | ||
| 30 | value: ManuallyDrop<T>, | ||
| 31 | f: ManuallyDrop<F>, | ||
| 28 | } | 32 | } |
| 29 | 33 | ||
| 30 | unsafe impl<T, F> Sync for LazyLock<T, F> {} | 34 | unsafe impl<T, F> Sync for LazyLock<T, F> {} |
| @@ -34,8 +38,9 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { | |||
| 34 | pub const fn new(init_fn: F) -> Self { | 38 | pub const fn new(init_fn: F) -> Self { |
| 35 | Self { | 39 | Self { |
| 36 | init: AtomicBool::new(false), | 40 | init: AtomicBool::new(false), |
| 37 | init_fn: Cell::new(Some(init_fn)), | 41 | data: UnsafeCell::new(Data { |
| 38 | data: Cell::new(MaybeUninit::zeroed()), | 42 | f: ManuallyDrop::new(init_fn), |
| 43 | }), | ||
| 39 | } | 44 | } |
| 40 | } | 45 | } |
| 41 | 46 | ||
| @@ -44,7 +49,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { | |||
| 44 | #[inline] | 49 | #[inline] |
| 45 | pub fn get(&self) -> &T { | 50 | pub fn get(&self) -> &T { |
| 46 | self.ensure_init_fast(); | 51 | self.ensure_init_fast(); |
| 47 | unsafe { (*self.data.as_ptr()).assume_init_ref() } | 52 | unsafe { &(*self.data.get()).value } |
| 48 | } | 53 | } |
| 49 | 54 | ||
| 50 | /// Consume the `LazyLock`, returning the underlying value. The | 55 | /// Consume the `LazyLock`, returning the underlying value. The |
| @@ -53,7 +58,10 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { | |||
| 53 | #[inline] | 58 | #[inline] |
| 54 | pub fn into_inner(self) -> T { | 59 | pub fn into_inner(self) -> T { |
| 55 | self.ensure_init_fast(); | 60 | self.ensure_init_fast(); |
| 56 | unsafe { self.data.into_inner().assume_init() } | 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 }) | ||
| 57 | } | 65 | } |
| 58 | 66 | ||
| 59 | /// Initialize the `LazyLock` if it has not been initialized yet. | 67 | /// Initialize the `LazyLock` if it has not been initialized yet. |
| @@ -75,10 +83,23 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { | |||
| 75 | fn ensure_init(&self) { | 83 | fn ensure_init(&self) { |
| 76 | critical_section::with(|_| { | 84 | critical_section::with(|_| { |
| 77 | if !self.init.load(Ordering::Acquire) { | 85 | if !self.init.load(Ordering::Acquire) { |
| 78 | let init_fn = self.init_fn.take().unwrap(); | 86 | let data = unsafe { &mut *self.data.get() }; |
| 79 | self.data.set(MaybeUninit::new(init_fn())); | 87 | let f = unsafe { ManuallyDrop::take(&mut data.f) }; |
| 88 | let value = f(); | ||
| 89 | data.value = ManuallyDrop::new(value); | ||
| 90 | |||
| 80 | self.init.store(true, Ordering::Release); | 91 | self.init.store(true, Ordering::Release); |
| 81 | } | 92 | } |
| 82 | }); | 93 | }); |
| 83 | } | 94 | } |
| 84 | } | 95 | } |
| 96 | |||
| 97 | impl<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 | } | ||
