state/cell.rs
1use std::fmt;
2
3use crate::shim::cell::UnsafeCell;
4use crate::init::Init;
5
6/// An init-once cell for global access to a value.
7///
8/// A `InitCell` instance can hold a single value in a global context. A
9/// `InitCell` instance begins without a value and must be initialized via the
10/// [`set()`](#method.set) method. Once a value has been set, it can be
11/// retrieved at any time and in any thread via the [`get()`](#method.get)
12/// method. The [`try_get()`](#method.try_get) can be used to determine whether
13/// the `InitCell` has been initialized before attempting to retrieve the value.
14///
15/// # Example
16///
17/// The following example uses `InitCell` to hold a global instance of a
18/// `HashMap` which can be modified at will:
19///
20/// ```rust
21/// use std::collections::HashMap;
22/// use std::sync::Mutex;
23/// use std::thread;
24///
25/// use state::InitCell;
26///
27/// static GLOBAL_MAP: InitCell<Mutex<HashMap<String, String>>> = InitCell::new();
28///
29/// fn run_program() {
30/// let mut map = GLOBAL_MAP.get().lock().unwrap();
31/// map.insert("another_key".into(), "another_value".into());
32/// }
33///
34/// fn main() {
35/// // Create the initial map and store it in `GLOBAL_MAP`.
36/// let mut initial_map = HashMap::new();
37/// initial_map.insert("key".into(), "value".into());
38/// GLOBAL_MAP.set(Mutex::new(initial_map));
39///
40/// // For illustration, we spawn a new thread that modified the map.
41/// thread::spawn(|| run_program()).join().expect("thread");
42///
43/// // Assert that the modification took place.
44/// let map = GLOBAL_MAP.get().lock().unwrap();
45/// assert_eq!(map.get("another_key").unwrap(), "another_value");
46/// }
47pub struct InitCell<T> {
48 item: UnsafeCell<Option<T>>,
49 init: Init
50}
51
52/// Defaults to [`InitCell::new()`].
53impl<T> Default for InitCell<T> {
54 fn default() -> Self {
55 InitCell::new()
56 }
57}
58
59impl<T> InitCell<T> {
60 /// Create a new, uninitialized cell.
61 ///
62 /// To create a cell initializd with a value, use [`InitCell::from()`].
63 ///
64 /// # Example
65 ///
66 /// ```rust
67 /// use state::InitCell;
68 ///
69 /// static MY_GLOBAL: InitCell<String> = InitCell::new();
70 /// ```
71 #[cfg(not(loom))]
72 pub const fn new() -> InitCell<T> {
73 InitCell {
74 item: UnsafeCell::new(None),
75 init: Init::new()
76 }
77 }
78
79 /// New, for loom.
80 #[cfg(loom)]
81 pub fn new() -> InitCell<T> {
82 InitCell {
83 item: UnsafeCell::new(None),
84 init: Init::new()
85 }
86 }
87
88 /// Sets this cell's value to `value` if it is not already initialized.
89 ///
90 /// If there are multiple simultaneous callers, exactly one is guaranteed to
91 /// receive `true`, indicating its value was set. All other callers receive
92 /// `false`, indicating the value was ignored.
93 ///
94 /// # Example
95 ///
96 /// ```rust
97 /// # use state::InitCell;
98 /// static MY_GLOBAL: InitCell<&'static str> = InitCell::new();
99 ///
100 /// assert_eq!(MY_GLOBAL.set("Hello, world!"), true);
101 /// assert_eq!(MY_GLOBAL.set("Goodbye, world!"), false);
102 /// ```
103 pub fn set(&self, value: T) -> bool {
104 if self.init.needed() {
105 unsafe { self.item.with_mut(|ptr| *ptr = Some(value)); }
106 self.init.mark_complete();
107 return true;
108 }
109
110 false
111 }
112
113 /// Resets the cell to an uninitialized state.
114 ///
115 /// # Example
116 ///
117 /// ```rust
118 /// use state::InitCell;
119 ///
120 /// let mut cell = InitCell::from(5);
121 /// assert_eq!(cell.get(), &5);
122 ///
123 /// cell.reset();
124 /// assert!(cell.try_get().is_none());
125 /// ```
126 pub fn reset(&mut self) {
127 *self = Self::new();
128 }
129
130 /// If the cell is not initialized, it is set `f()`. Returns a borrow to the
131 /// value in this cell.
132 ///
133 /// If `f()` panics during initialization, the cell is left uninitialized.
134 ///
135 /// # Example
136 ///
137 /// ```rust
138 /// # use state::InitCell;
139 /// static MY_GLOBAL: InitCell<&'static str> = InitCell::new();
140 ///
141 /// assert_eq!(*MY_GLOBAL.get_or_init(|| "Hello, world!"), "Hello, world!");
142 /// ```
143 #[inline]
144 pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
145 if let Some(value) = self.try_get() {
146 value
147 } else {
148 self.set(f());
149 self.try_get().expect("cell::get_or_init(): set() => get() ok")
150 }
151 }
152
153 /// Waits (blocks) until the cell has a value and then borrows it.
154 ///
155 /// # Example
156 ///
157 /// ```rust
158 /// # use state::InitCell;
159 /// static MY_GLOBAL: InitCell<&'static str> = InitCell::new();
160 ///
161 /// MY_GLOBAL.set("Hello, world!");
162 /// assert_eq!(*MY_GLOBAL.get(), "Hello, world!");
163 /// ```
164 #[inline]
165 pub fn wait(&self) -> &T {
166 self.init.wait_until_complete();
167 self.try_get().expect("cell::wait(): broken (init await complete w/o value)")
168 }
169
170 /// Get a reference to the underlying value, if one is set.
171 ///
172 /// Returns `Some` if the state has previously been set via methods like
173 /// [`InitCell::set()`] or [`InitCell::get_or_init()`]. Otherwise returns
174 /// `None`.
175 ///
176 /// # Example
177 ///
178 /// ```rust
179 /// # use state::InitCell;
180 /// static MY_GLOBAL: InitCell<&'static str> = InitCell::new();
181 ///
182 /// assert_eq!(MY_GLOBAL.try_get(), None);
183 ///
184 /// MY_GLOBAL.set("Hello, world!");
185 ///
186 /// assert_eq!(MY_GLOBAL.try_get(), Some(&"Hello, world!"));
187 /// ```
188 #[inline]
189 pub fn try_get(&self) -> Option<&T> {
190 if self.init.has_completed() {
191 unsafe { self.item.with(|ptr| (*ptr).as_ref()) }
192 } else {
193 None
194 }
195 }
196
197 /// Returns a mutable reference to the underlying data if any is set.
198 ///
199 /// This call borrows `InitCell` mutably (at compile-time) so there is no
200 /// need for dynamic checks.
201 ///
202 /// # Example
203 ///
204 /// ```rust
205 /// use state::InitCell;
206 ///
207 /// let mut cell = InitCell::from(5);
208 /// *cell.try_get_mut().unwrap() += 1;
209 ///
210 /// let mut cell: InitCell<usize> = InitCell::new();
211 /// assert!(cell.try_get_mut().is_none());
212 /// ```
213 pub fn try_get_mut(&mut self) -> Option<&mut T> {
214 self.item.get_mut().as_mut()
215 }
216
217 /// Borrows the value in this cell, panicking if there is no value.
218 ///
219 /// # Panics
220 ///
221 /// Panics if a value has not previously been [`set()`](#method.set). Use
222 /// [`try_get()`](#method.try_get) for a non-panicking version.
223 ///
224 /// # Example
225 ///
226 /// ```rust
227 /// # use state::InitCell;
228 /// static MY_GLOBAL: InitCell<&'static str> = InitCell::new();
229 ///
230 /// MY_GLOBAL.set("Hello, world!");
231 /// assert_eq!(*MY_GLOBAL.get(), "Hello, world!");
232 /// ```
233 #[inline]
234 pub fn get(&self) -> &T {
235 self.try_get().expect("cell::get(): called get() before set()")
236 }
237
238 /// Resets the cell to an uninitialized state and returns the inner value if
239 /// any was set.
240 ///
241 /// # Example
242 ///
243 /// ```rust
244 /// use state::InitCell;
245 ///
246 /// let mut cell = InitCell::from(5);
247 /// assert_eq!(cell.get(), &5);
248 /// assert_eq!(cell.get(), &5);
249 ///
250 /// assert_eq!(cell.take(), Some(5));
251 /// assert_eq!(cell.take(), None);
252 /// ```
253 pub fn take(&mut self) -> Option<T> {
254 std::mem::replace(self, Self::new()).into_inner()
255 }
256
257 /// Returns the inner value if any is set.
258 ///
259 /// # Example
260 ///
261 /// ```rust
262 /// use state::InitCell;
263 ///
264 /// let cell = InitCell::from(5);
265 /// assert_eq!(cell.into_inner().unwrap(), 5);
266 ///
267 /// let cell: InitCell<usize> = InitCell::new();
268 /// assert!(cell.into_inner().is_none());
269 /// ```
270 pub fn into_inner(self) -> Option<T> {
271 self.item.into_inner()
272 }
273
274 /// Applies the function `f` to the inner value, if there is any, leaving
275 /// the updated value in the cell.
276 ///
277 /// If `f()` panics during updating, the cell is left uninitialized.
278 ///
279 /// # Example
280 ///
281 /// ```rust
282 /// use state::InitCell;
283 ///
284 /// let mut cell = InitCell::from(5);
285 /// cell.update(|v| v + 10);
286 /// assert_eq!(cell.wait(), &15);
287 ///
288 /// let mut cell = InitCell::new();
289 /// cell.update(|v: u8| v + 10);
290 /// assert!(cell.try_get().is_none());
291 /// ```
292 pub fn update<F: FnOnce(T) -> T>(&mut self, f: F) {
293 self.take().map(|v| self.set(f(v)));
294 }
295
296 /// Applies the function `f` to the inner value, if there is any, and
297 /// returns a new `InitCell` with mapped value.
298 ///
299 /// # Example
300 ///
301 /// ```rust
302 /// use state::InitCell;
303 ///
304 /// let cell = InitCell::from(5);
305 /// assert_eq!(cell.get(), &5);
306 ///
307 /// let cell = cell.map(|v| v + 10);
308 /// assert_eq!(cell.get(), &15);
309 /// ```
310 pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> InitCell<U> {
311 self.into_inner().map_or_else(InitCell::new, |v| InitCell::from(f(v)))
312 }
313}
314
315unsafe impl<T: Send> Send for InitCell<T> { }
316
317unsafe impl<T: Send + Sync> Sync for InitCell<T> { }
318
319impl<T: fmt::Debug> fmt::Debug for InitCell<T> {
320 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
321 match self.try_get() {
322 Some(object) => object.fmt(f),
323 None => write!(f, "[uninitialized cell]")
324 }
325 }
326}
327
328impl<T> From<T> for InitCell<T> {
329 fn from(value: T) -> InitCell<T> {
330 let cell = InitCell::new();
331 assert!(cell.set(value));
332 cell
333 }
334}
335
336impl<T: Clone> Clone for InitCell<T> {
337 fn clone(&self) -> InitCell<T> {
338 match self.try_get() {
339 Some(val) => InitCell::from(val.clone()),
340 None => InitCell::new()
341 }
342 }
343}