diff options
| author | Samuel Tardieu <[email protected]> | 2024-07-27 11:49:02 +0200 |
|---|---|---|
| committer | Samuel Tardieu <[email protected]> | 2024-07-29 11:04:59 +0200 |
| commit | 05e0f128462ebdacd3dffc27f74502913d782589 (patch) | |
| tree | b13be999acbf294a687f2d55165eea1cc9543073 | |
| parent | 4e5a646f8b1905de014462f5f0441952ec7e209b (diff) | |
embassy-sync: add LazyLock
`LazyLock` is inspired by Rust 1.80.0's `std::sync::LazyLock` type.
| -rw-r--r-- | embassy-sync/CHANGELOG.md | 2 | ||||
| -rw-r--r-- | embassy-sync/README.md | 1 | ||||
| -rw-r--r-- | embassy-sync/src/lazy_lock.rs | 84 | ||||
| -rw-r--r-- | embassy-sync/src/lib.rs | 1 |
4 files changed, 88 insertions, 0 deletions
diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index a283adc0c..8f2d26fe0 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md | |||
| @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 7 | 7 | ||
| 8 | ## Unreleased | 8 | ## Unreleased |
| 9 | 9 | ||
| 10 | - Add LazyLock sync primitive. | ||
| 11 | |||
| 10 | ## 0.6.0 - 2024-05-29 | 12 | ## 0.6.0 - 2024-05-29 |
| 11 | 13 | ||
| 12 | - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `Channel`. | 14 | - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `Channel`. |
diff --git a/embassy-sync/README.md b/embassy-sync/README.md index 2c1c0cf68..dec1fbc32 100644 --- a/embassy-sync/README.md +++ b/embassy-sync/README.md | |||
| @@ -13,6 +13,7 @@ Synchronization primitives and data structures with async support: | |||
| 13 | - [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`. | 13 | - [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`. |
| 14 | - [`AtomicWaker`](waitqueue::AtomicWaker) - A variant of `WakerRegistration` accessible using a non-mut API. | 14 | - [`AtomicWaker`](waitqueue::AtomicWaker) - A variant of `WakerRegistration` accessible using a non-mut API. |
| 15 | - [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s. | 15 | - [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s. |
| 16 | - [`LazyLock`](lazy_lock::LazyLock) - A value which is initialized on the first access | ||
| 16 | 17 | ||
| 17 | ## Interoperability | 18 | ## Interoperability |
| 18 | 19 | ||
diff --git a/embassy-sync/src/lazy_lock.rs b/embassy-sync/src/lazy_lock.rs new file mode 100644 index 000000000..2b5742491 --- /dev/null +++ b/embassy-sync/src/lazy_lock.rs | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | //! Synchronization primitive for initializing a value once, allowing others to get a reference to the value. | ||
| 2 | |||
| 3 | use core::cell::Cell; | ||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 6 | |||
| 7 | /// The `LazyLock` is a synchronization primitive that allows for | ||
| 8 | /// initializing a value once, and allowing others to obtain a | ||
| 9 | /// reference to the value. This is useful for lazy initialization of | ||
| 10 | /// a static value. | ||
| 11 | /// | ||
| 12 | /// # Example | ||
| 13 | /// ``` | ||
| 14 | /// use futures_executor::block_on; | ||
| 15 | /// use embassy_sync::lazy_lock::LazyLock; | ||
| 16 | /// | ||
| 17 | /// // Define a static value that will be lazily initialized | ||
| 18 | /// // at runtime at the first access. | ||
| 19 | /// static VALUE: LazyLock<u32> = LazyLock::new(|| 20); | ||
| 20 | /// | ||
| 21 | /// let reference = VALUE.get(); | ||
| 22 | /// assert_eq!(reference, &20); | ||
| 23 | /// ``` | ||
| 24 | pub struct LazyLock<T, F = fn() -> T> { | ||
| 25 | init: AtomicBool, | ||
| 26 | init_fn: Cell<Option<F>>, | ||
| 27 | data: Cell<MaybeUninit<T>>, | ||
| 28 | } | ||
| 29 | |||
| 30 | unsafe impl<T, F> Sync for LazyLock<T, F> {} | ||
| 31 | |||
| 32 | impl<T, F: FnOnce() -> T> LazyLock<T, F> { | ||
| 33 | /// Create a new uninitialized `StaticLock`. | ||
| 34 | pub const fn new(init_fn: F) -> Self { | ||
| 35 | Self { | ||
| 36 | init: AtomicBool::new(false), | ||
| 37 | init_fn: Cell::new(Some(init_fn)), | ||
| 38 | data: Cell::new(MaybeUninit::zeroed()), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Get a reference to the underlying value, initializing it if it | ||
| 43 | /// has not been done already. | ||
| 44 | #[inline] | ||
| 45 | pub fn get(&self) -> &T { | ||
| 46 | self.ensure_init_fast(); | ||
| 47 | unsafe { (*self.data.as_ptr()).assume_init_ref() } | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Consume the `LazyLock`, returning the underlying value. The | ||
| 51 | /// initialization function will be called if it has not been | ||
| 52 | /// already. | ||
| 53 | #[inline] | ||
| 54 | pub fn into_inner(self) -> T { | ||
| 55 | self.ensure_init_fast(); | ||
| 56 | unsafe { self.data.into_inner().assume_init() } | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Initialize the `LazyLock` if it has not been initialized yet. | ||
| 60 | /// This function is a fast track to [`Self::ensure_init`] | ||
| 61 | /// which does not require a critical section in most cases when | ||
| 62 | /// the value has been initialized already. | ||
| 63 | /// When this function returns, `self.data` is guaranteed to be | ||
| 64 | /// initialized and visible on the current core. | ||
| 65 | #[inline] | ||
| 66 | fn ensure_init_fast(&self) { | ||
| 67 | if !self.init.load(Ordering::Acquire) { | ||
| 68 | self.ensure_init(); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Initialize the `LazyLock` if it has not been initialized yet. | ||
| 73 | /// When this function returns, `self.data` is guaranteed to be | ||
| 74 | /// initialized and visible on the current core. | ||
| 75 | fn ensure_init(&self) { | ||
| 76 | critical_section::with(|_| { | ||
| 77 | if !self.init.load(Ordering::Acquire) { | ||
| 78 | let init_fn = self.init_fn.take().unwrap(); | ||
| 79 | self.data.set(MaybeUninit::new(init_fn())); | ||
| 80 | self.init.store(true, Ordering::Release); | ||
| 81 | } | ||
| 82 | }); | ||
| 83 | } | ||
| 84 | } | ||
diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index a5eee8d02..014bf1d06 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs | |||
| @@ -12,6 +12,7 @@ mod ring_buffer; | |||
| 12 | 12 | ||
| 13 | pub mod blocking_mutex; | 13 | pub mod blocking_mutex; |
| 14 | pub mod channel; | 14 | pub mod channel; |
| 15 | pub mod lazy_lock; | ||
| 15 | pub mod mutex; | 16 | pub mod mutex; |
| 16 | pub mod once_lock; | 17 | pub mod once_lock; |
| 17 | pub mod pipe; | 18 | pub mod pipe; |
