1 | #![no_std ] |
2 | #![doc = include_str!("../README.md" )] |
3 | #![cfg_attr (docsrs, feature(doc_cfg))] |
4 | |
5 | use core::cell::UnsafeCell; |
6 | use core::mem::MaybeUninit; |
7 | |
8 | use 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. |
20 | pub struct StaticCell<T> { |
21 | used: AtomicBool, |
22 | val: UnsafeCell<MaybeUninit<T>>, |
23 | } |
24 | |
25 | unsafe impl<T> Send for StaticCell<T> {} |
26 | unsafe impl<T> Sync for StaticCell<T> {} |
27 | |
28 | impl<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. |
153 | pub struct ConstStaticCell<T> { |
154 | taken: AtomicBool, |
155 | val: UnsafeCell<T>, |
156 | } |
157 | |
158 | unsafe impl<T> Send for ConstStaticCell<T> {} |
159 | unsafe impl<T> Sync for ConstStaticCell<T> {} |
160 | |
161 | impl<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 ] |
234 | macro_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 | |