1#![no_std]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5use core::cell::UnsafeCell;
6use core::mem::MaybeUninit;
7
8use portable_atomic::{AtomicBool, Ordering};
9
10/// Statically allocated, initialized at runtime cell.
11///
12/// It has two states: "empty" and "full". It is created "empty", and obtaining a reference
13/// to the contents permanently changes it to "full". This allows that reference to be valid
14/// forever.
15///
16/// If your value can be initialized as a `const` value, consider using [`ConstStaticCell`]
17/// instead if you only need to take the value at runtime.
18///
19/// See the [crate-level docs](crate) for usage.
20pub struct StaticCell<T> {
21 used: AtomicBool,
22 val: UnsafeCell<MaybeUninit<T>>,
23}
24
25unsafe impl<T> Send for StaticCell<T> {}
26unsafe impl<T> Sync for StaticCell<T> {}
27
28impl<T> StaticCell<T> {
29 /// Create a new, empty `StaticCell`.
30 ///
31 /// It can be initialized at runtime with [`StaticCell::init()`] or similar methods.
32 #[inline]
33 pub const fn new() -> Self {
34 Self {
35 used: AtomicBool::new(false),
36 val: UnsafeCell::new(MaybeUninit::uninit()),
37 }
38 }
39
40 /// Initialize the `StaticCell` with a value, returning a mutable reference to it.
41 ///
42 /// Using this method, the compiler usually constructs `val` in the stack and then moves
43 /// it into the `StaticCell`. If `T` is big, this is likely to cause stack overflows.
44 /// Considering using [`StaticCell::init_with`] instead, which will construct it in-place inside the `StaticCell`.
45 ///
46 /// # Panics
47 ///
48 /// Panics if this `StaticCell` is already full.
49 #[inline]
50 #[allow(clippy::mut_from_ref)]
51 pub fn init(&'static self, val: T) -> &'static mut T {
52 self.uninit().write(val)
53 }
54
55 /// Initialize the `StaticCell` with the closure's return value, returning a mutable reference to it.
56 ///
57 /// The advantage over [`StaticCell::init`] is that this method allows the closure to construct
58 /// the `T` value in-place directly inside the `StaticCell`, saving stack space.
59 ///
60 /// # Panics
61 ///
62 /// Panics if this `StaticCell` is already full.
63 #[inline]
64 #[allow(clippy::mut_from_ref)]
65 pub fn init_with(&'static self, val: impl FnOnce() -> T) -> &'static mut T {
66 self.uninit().write(val())
67 }
68
69 /// Return a mutable reference to the uninitialized memory owned by the `StaticCell`.
70 ///
71 /// Using this method directly is not recommended, but it can be used to construct `T` in-place directly
72 /// in a guaranteed fashion.
73 ///
74 /// # Panics
75 ///
76 /// Panics if this `StaticCell` is already full.
77 #[inline]
78 #[allow(clippy::mut_from_ref)]
79 pub fn uninit(&'static self) -> &'static mut MaybeUninit<T> {
80 if let Some(val) = self.try_uninit() {
81 val
82 } else {
83 panic!("`StaticCell` is already full, it can't be initialized twice.");
84 }
85 }
86
87 /// Try initializing the `StaticCell` with a value, returning a mutable reference to it.
88 ///
89 /// If this `StaticCell` is already full, it returns `None`.
90 ///
91 /// Using this method, the compiler usually constructs `val` in the stack and then moves
92 /// it into the `StaticCell`. If `T` is big, this is likely to cause stack overflows.
93 /// Considering using [`StaticCell::try_init_with`] instead, which will construct it in-place inside the `StaticCell`.
94 ///
95 /// Will only return a Some(&'static mut T) when the `StaticCell` was not yet initialized.
96 #[inline]
97 #[allow(clippy::mut_from_ref)]
98 pub fn try_init(&'static self, val: T) -> Option<&'static mut T> {
99 Some(self.try_uninit()?.write(val))
100 }
101
102 /// Try initializing the `StaticCell` with the closure's return value, returning a mutable reference to it.
103 ///
104 /// If this `StaticCell` is already full, it returns `None`.
105 ///
106 /// The advantage over [`StaticCell::init`] is that this method allows the closure to construct
107 /// the `T` value in-place directly inside the `StaticCell`, saving stack space.
108 ///
109 #[inline]
110 #[allow(clippy::mut_from_ref)]
111 pub fn try_init_with(&'static self, val: impl FnOnce() -> T) -> Option<&'static mut T> {
112 Some(self.try_uninit()?.write(val()))
113 }
114
115 /// Try returning a mutable reference to the uninitialized memory owned by the `StaticCell`.
116 ///
117 /// If this `StaticCell` is already full, it returns `None`.
118 ///
119 /// Using this method directly is not recommended, but it can be used to construct `T` in-place directly
120 /// in a guaranteed fashion.
121 #[inline]
122 pub fn try_uninit(&'static self) -> Option<&'static mut MaybeUninit<T>> {
123 if self
124 .used
125 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
126 .is_ok()
127 {
128 // SAFETY: We just checked that the value is not yet taken and marked it as taken.
129 let val = unsafe { &mut *self.val.get() };
130 Some(val)
131 } else {
132 None
133 }
134 }
135}
136
137// ---
138
139/// Statically allocated and initialized, taken at runtime cell.
140///
141/// It has two states: "untaken" and "taken". It is created "untaken", and obtaining a reference
142/// to the contents permanently changes it to "taken". This allows that reference to be valid
143/// forever.
144///
145/// If your value can be const defined, for example a large, zero filled buffer used for DMA
146/// or other scratch memory usage, `ConstStaticCell` can be used to guarantee the initializer
147/// will never take up stack memory.
148///
149/// If your values are all zero initialized, the resulting `ConstStaticCell` should be placed
150/// in `.bss`, not taking flash space for initialization either.
151///
152/// See the [crate-level docs](crate) for usage.
153pub struct ConstStaticCell<T> {
154 taken: AtomicBool,
155 val: UnsafeCell<T>,
156}
157
158unsafe impl<T> Send for ConstStaticCell<T> {}
159unsafe impl<T> Sync for ConstStaticCell<T> {}
160
161impl<T> ConstStaticCell<T> {
162 /// Create a new, empty `ConstStaticCell`.
163 ///
164 /// It can be taken at runtime with [`ConstStaticCell::take()`] or similar methods.
165 #[inline]
166 pub const fn new(value: T) -> Self {
167 Self {
168 taken: AtomicBool::new(false),
169 val: UnsafeCell::new(value),
170 }
171 }
172
173 /// Take the `ConstStaticCell`, returning a mutable reference to it.
174 ///
175 /// # Panics
176 ///
177 /// Panics if this `ConstStaticCell` was already taken.
178 #[inline]
179 #[allow(clippy::mut_from_ref)]
180 pub fn take(&'static self) -> &'static mut T {
181 if let Some(val) = self.try_take() {
182 val
183 } else {
184 panic!("`ConstStaticCell` is already taken, it can't be taken twice")
185 }
186 }
187
188 /// Try to take the `ConstStaticCell`, returning None if it was already taken
189 #[inline]
190 #[allow(clippy::mut_from_ref)]
191 pub fn try_take(&'static self) -> Option<&'static mut T> {
192 if self
193 .taken
194 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
195 .is_ok()
196 {
197 // SAFETY: We just checked that the value is not yet taken and marked it as taken.
198 let val = unsafe { &mut *self.val.get() };
199 Some(val)
200 } else {
201 None
202 }
203 }
204}
205
206/// Convert a `T` to a `&'static mut T`.
207///
208/// The macro declares a `static StaticCell` and then initializes it when run, returning the `&'static mut`.
209/// Therefore, each instance can only be run once. Next runs will panic. The `static` can additionally be
210/// decorated with attributes, such as `#[link_section]`, `#[used]`, et al.
211///
212/// This macro is nightly-only. It requires `#![feature(type_alias_impl_trait)]` in the crate using it.
213///
214/// # Examples
215///
216/// ```
217/// # #![feature(type_alias_impl_trait)]
218/// use static_cell::make_static;
219///
220/// # fn main() {
221/// let x: &'static mut u32 = make_static!(42);
222///
223/// // This attribute instructs the linker to allocate it in the external RAM's BSS segment.
224/// // This specific example is for ESP32S3 with PSRAM support.
225/// let buf = make_static!([0u8; 4096], #[link_section = ".ext_ram.bss.buf"]);
226///
227/// // Multiple attributes can be supplied.
228/// let s = make_static!(0usize, #[used] #[export_name = "exported_symbol_name"]);
229/// # }
230/// ```
231#[cfg(feature = "nightly")]
232#[cfg_attr(docsrs, doc(cfg(feature = "nightly")))]
233#[macro_export]
234macro_rules! make_static {
235 ($val:expr) => ($crate::make_static!($val, ));
236 ($val:expr, $(#[$m:meta])*) => {{
237 type T = impl ::core::marker::Sized;
238 $(#[$m])*
239 static STATIC_CELL: $crate::StaticCell<T> = $crate::StaticCell::new();
240 #[deny(unused_attributes)]
241 let (x,) = unsafe { STATIC_CELL.uninit().write(($val,)) };
242 x
243 }};
244}
245