From eece3c51715e88d50d1a15af13a7f56521c8c13c Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 25 Apr 2024 09:44:10 +0200 Subject: [PATCH 1/4] Add const init cell --- src/lib.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index bf99ee7..3649294 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,9 @@ use portable_atomic::{AtomicBool, Ordering}; /// to the contents permanently changes it to "full". This allows that reference to be valid /// forever. /// +/// If your value can be initialized as a `const` value, consider using [`ConstInitCell`] +/// instead if you only need to take the value at runtime. +/// /// See the [crate-level docs](crate) for usage. pub struct StaticCell { used: AtomicBool, @@ -131,6 +134,74 @@ impl StaticCell { } } +// --- + +/// Statically allocated and initialized, taken at runtime cell. +/// +/// It has two states: "untake" and "taken". It is created "untake", and obtaining a reference +/// to the contents permanently changes it to "taken". This allows that reference to be valid +/// forever. +/// +/// If your value can be const defined, for example a large, zero filled buffer used for DMA +/// or other scratch memory usage, `ConstInitCell` can be used to guarantee the initializer +/// will never take up stack memory. +/// +/// If your values are all zero initialized, the resulting `ConstInitCell` should be placed +/// in `.bss`, not taking flash space for initialization either. +/// +/// See the [crate-level docs](crate) for usage. +pub struct ConstInitCell { + taken: AtomicBool, + val: UnsafeCell, +} + +unsafe impl Send for ConstInitCell {} +unsafe impl Sync for ConstInitCell {} + +impl ConstInitCell { + /// Create a new, empty `ConstInitCell`. + /// + /// It can be taken at runtime with [`ConstInitCell::take()`] or similar methods. + #[inline] + pub const fn new(value: T) -> Self { + Self { + taken: AtomicBool::new(false), + val: UnsafeCell::new(value), + } + } + + /// Take the `ConstInitCell`, returning a mutable reference to it. + /// + /// # Panics + /// + /// Panics if this `ConstInitCell` was already taken. + #[inline] + #[allow(clippy::mut_from_ref)] + pub fn take(&'static self) -> &'static mut T { + if let Some(val) = self.try_take() { + val + } else { + panic!("`ConstInitCell` is already taken, it can't be taken twice") + } + } + + #[inline] + #[allow(clippy::mut_from_ref)] + pub fn try_take(&'static self) -> Option<&'static mut T> { + if self + .taken + .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) + .is_ok() + { + // SAFETY: We just checked that the value is not yet taken and marked it as taken. + let val = unsafe { &mut *self.val.get() }; + Some(val) + } else { + None + } + } +} + /// Convert a `T` to a `&'static mut T`. /// /// The macro declares a `static StaticCell` and then initializes it when run, returning the `&'static mut`. From 0d5ed827f8e4cd23671db37ee26a203bd35092d5 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 25 Apr 2024 09:49:47 +0200 Subject: [PATCH 2/4] Add missing doc comment --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 3649294..4980372 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -185,6 +185,7 @@ impl ConstInitCell { } } + /// Try to take the `ConstInitCell`, returning None if it was already taken #[inline] #[allow(clippy::mut_from_ref)] pub fn try_take(&'static self) -> Option<&'static mut T> { From c0337f742edcfc5d7bf91f008e37ccdb53cf2cce Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 25 Apr 2024 09:53:16 +0200 Subject: [PATCH 3/4] More docs typo fixes --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 4980372..5ba3a2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,7 @@ impl StaticCell { /// Statically allocated and initialized, taken at runtime cell. /// -/// It has two states: "untake" and "taken". It is created "untake", and obtaining a reference +/// It has two states: "untaken" and "taken". It is created "untaken", and obtaining a reference /// to the contents permanently changes it to "taken". This allows that reference to be valid /// forever. /// From 5af69a06adcde65dfd399797839a6d01c146323c Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 26 Apr 2024 22:46:49 +0200 Subject: [PATCH 4/4] My bike shed is painted ConstStaticCell --- src/lib.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5ba3a2f..3e3acfa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use portable_atomic::{AtomicBool, Ordering}; /// to the contents permanently changes it to "full". This allows that reference to be valid /// forever. /// -/// If your value can be initialized as a `const` value, consider using [`ConstInitCell`] +/// If your value can be initialized as a `const` value, consider using [`ConstStaticCell`] /// instead if you only need to take the value at runtime. /// /// See the [crate-level docs](crate) for usage. @@ -143,25 +143,25 @@ impl StaticCell { /// forever. /// /// If your value can be const defined, for example a large, zero filled buffer used for DMA -/// or other scratch memory usage, `ConstInitCell` can be used to guarantee the initializer +/// or other scratch memory usage, `ConstStaticCell` can be used to guarantee the initializer /// will never take up stack memory. /// -/// If your values are all zero initialized, the resulting `ConstInitCell` should be placed +/// If your values are all zero initialized, the resulting `ConstStaticCell` should be placed /// in `.bss`, not taking flash space for initialization either. /// /// See the [crate-level docs](crate) for usage. -pub struct ConstInitCell { +pub struct ConstStaticCell { taken: AtomicBool, val: UnsafeCell, } -unsafe impl Send for ConstInitCell {} -unsafe impl Sync for ConstInitCell {} +unsafe impl Send for ConstStaticCell {} +unsafe impl Sync for ConstStaticCell {} -impl ConstInitCell { - /// Create a new, empty `ConstInitCell`. +impl ConstStaticCell { + /// Create a new, empty `ConstStaticCell`. /// - /// It can be taken at runtime with [`ConstInitCell::take()`] or similar methods. + /// It can be taken at runtime with [`ConstStaticCell::take()`] or similar methods. #[inline] pub const fn new(value: T) -> Self { Self { @@ -170,22 +170,22 @@ impl ConstInitCell { } } - /// Take the `ConstInitCell`, returning a mutable reference to it. + /// Take the `ConstStaticCell`, returning a mutable reference to it. /// /// # Panics /// - /// Panics if this `ConstInitCell` was already taken. + /// Panics if this `ConstStaticCell` was already taken. #[inline] #[allow(clippy::mut_from_ref)] pub fn take(&'static self) -> &'static mut T { if let Some(val) = self.try_take() { val } else { - panic!("`ConstInitCell` is already taken, it can't be taken twice") + panic!("`ConstStaticCell` is already taken, it can't be taken twice") } } - /// Try to take the `ConstInitCell`, returning None if it was already taken + /// Try to take the `ConstStaticCell`, returning None if it was already taken #[inline] #[allow(clippy::mut_from_ref)] pub fn try_take(&'static self) -> Option<&'static mut T> {