Skip to content

Commit

Permalink
Add const init cell
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesmunns committed Apr 25, 2024
1 parent 86734c8 commit eece3c5
Showing 1 changed file with 71 additions and 0 deletions.
71 changes: 71 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
used: AtomicBool,
Expand Down Expand Up @@ -131,6 +134,74 @@ impl<T> StaticCell<T> {
}
}

// ---

/// 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<T> {
taken: AtomicBool,
val: UnsafeCell<T>,
}

unsafe impl<T> Send for ConstInitCell<T> {}
unsafe impl<T> Sync for ConstInitCell<T> {}

impl<T> ConstInitCell<T> {
/// 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`.
Expand Down

0 comments on commit eece3c5

Please sign in to comment.