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}