blob: 8b4d6f92108e8f36b75bba7a5dd04edf79860cbe [file] [log] [blame]
#![no_std]
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use atomic_polyfill::{AtomicBool, Ordering};
/// Statically allocated, initialized at runtime cell.
///
/// It has two states: "empty" and "full". It is created "empty", and obtaining a reference
/// to the contents permanently changes it to "full". This allows that reference to be valid
/// forever.
///
/// See the [crate-level docs](crate) for usage.
pub struct StaticCell<T> {
used: AtomicBool,
val: UnsafeCell<MaybeUninit<T>>,
}
unsafe impl<T> Send for StaticCell<T> {}
unsafe impl<T> Sync for StaticCell<T> {}
impl<T> StaticCell<T> {
/// Create a new, empty `StaticCell`.
///
/// It can be initialized at runtime with [`StaticCell::init()`] or similar methods.
#[inline]
pub const fn new() -> Self {
Self {
used: AtomicBool::new(false),
val: UnsafeCell::new(MaybeUninit::uninit()),
}
}
/// Initialize the `StaticCell` with a value, returning a mutable reference to it.
///
/// Using this method, the compiler usually constructs `val` in the stack and then moves
/// it into the `StaticCell`. If `T` is big, this is likely to cause stack overflows.
/// Considering using [`StaticCell::init_with`] instead, which will construct it in-place inside the `StaticCell`.
///
/// # Panics
///
/// Panics if this `StaticCell` is already full.
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn init(&'static self, val: T) -> &'static mut T {
self.uninit().write(val)
}
/// Initialize the `StaticCell` with the closure's return value, returning a mutable reference to it.
///
/// The advantage over [`StaticCell::init`] is that this method allows the closure to construct
/// the `T` value in-place directly inside the `StaticCell`, saving stack space.
///
/// # Panics
///
/// Panics if this `StaticCell` is already full.
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn init_with(&'static self, val: impl FnOnce() -> T) -> &'static mut T {
self.uninit().write(val())
}
/// Return a mutable reference to the uninitialized memory owned by the `StaticCell`.
///
/// Using this method directly is not recommended, but it can be used to construct `T` in-place directly
/// in a guaranteed fashion.
///
/// # Panics
///
/// Panics if this `StaticCell` is already full.
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn uninit(&'static self) -> &'static mut MaybeUninit<T> {
if let Some(val) = self.try_uninit() {
val
} else {
panic!("`StaticCell` is already full, it can't be initialized twice.");
}
}
/// Try initializing the `StaticCell` with a value, returning a mutable reference to it.
///
/// If this `StaticCell` is already full, it returns `None`.
///
/// Using this method, the compiler usually constructs `val` in the stack and then moves
/// it into the `StaticCell`. If `T` is big, this is likely to cause stack overflows.
/// Considering using [`StaticCell::try_init_with`] instead, which will construct it in-place inside the `StaticCell`.
///
/// Will only return a Some(&'static mut T) when the `StaticCell` was not yet initialized.
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn try_init(&'static self, val: T) -> Option<&'static mut T> {
Some(self.try_uninit()?.write(val))
}
/// Try initializing the `StaticCell` with the closure's return value, returning a mutable reference to it.
///
/// If this `StaticCell` is already full, it returns `None`.
///
/// The advantage over [`StaticCell::init`] is that this method allows the closure to construct
/// the `T` value in-place directly inside the `StaticCell`, saving stack space.
///
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn try_init_with(&'static self, val: impl FnOnce() -> T) -> Option<&'static mut T> {
Some(self.try_uninit()?.write(val()))
}
/// Try returning a mutable reference to the uninitialized memory owned by the `StaticCell`.
///
/// If this `StaticCell` is already full, it returns `None`.
///
/// Using this method directly is not recommended, but it can be used to construct `T` in-place directly
/// in a guaranteed fashion.
#[inline]
pub fn try_uninit(&'static self) -> Option<&'static mut MaybeUninit<T>> {
if self
.used
.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`.
/// Therefore, each instance can only be run once. Next runs will panic. The `static` can additionally be
/// decorated with attributes, such as `#[link_section]`, `#[used]`, et al.
///
/// This macro is nightly-only. It requires `#![feature(type_alias_impl_trait)]` in the crate using it.
///
/// # Examples
///
/// ```
/// # #![feature(type_alias_impl_trait)]
/// use static_cell::make_static;
///
/// # fn main() {
/// let x: &'static mut u32 = make_static!(42);
///
/// // This attribute instructs the linker to allocate it in the external RAM's BSS segment.
/// // This specific example is for ESP32S3 with PSRAM support.
/// let buf = make_static!([0u8; 4096], #[link_section = ".ext_ram.bss.buf"]);
///
/// // Multiple attributes can be supplied.
/// let s = make_static!(0usize, #[used] #[export_name = "exported_symbol_name"]);
/// # }
/// ```
#[cfg(feature = "nightly")]
#[cfg_attr(docsrs, doc(cfg(feature = "nightly")))]
#[macro_export]
macro_rules! make_static {
($val:expr) => ($crate::make_static!($val, ));
($val:expr, $(#[$m:meta])*) => {{
type T = impl ::core::marker::Sized;
$(#[$m])*
static STATIC_CELL: $crate::StaticCell<T> = $crate::StaticCell::new();
#[deny(unused_attributes)]
let (x,) = unsafe { STATIC_CELL.uninit().write(($val,)) };
x
}};
}