Skip to content

Commit

Permalink
Merge pull request #14 from jamesmunns/james/add-const-init-cell
Browse files Browse the repository at this point in the history
Add `ConstInitCell`
  • Loading branch information
Dirbaio authored Apr 26, 2024
2 parents 86734c8 + 5af69a0 commit 380b73a
Showing 1 changed file with 72 additions and 0 deletions.
72 changes: 72 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 [`ConstStaticCell`]
/// 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,75 @@ impl<T> StaticCell<T> {
}
}

// ---

/// Statically allocated and initialized, taken at runtime cell.
///
/// 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.
///
/// If your value can be const defined, for example a large, zero filled buffer used for DMA
/// 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 `ConstStaticCell` should be placed
/// in `.bss`, not taking flash space for initialization either.
///
/// See the [crate-level docs](crate) for usage.
pub struct ConstStaticCell<T> {
taken: AtomicBool,
val: UnsafeCell<T>,
}

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

impl<T> ConstStaticCell<T> {
/// Create a new, empty `ConstStaticCell`.
///
/// It can be taken at runtime with [`ConstStaticCell::take()`] or similar methods.
#[inline]
pub const fn new(value: T) -> Self {
Self {
taken: AtomicBool::new(false),
val: UnsafeCell::new(value),
}
}

/// Take the `ConstStaticCell`, returning a mutable reference to it.
///
/// # Panics
///
/// 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!("`ConstStaticCell` is already taken, it can't be taken twice")
}
}

/// 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> {
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 380b73a

Please sign in to comment.