department/statics/
cell.rs

1use core::cell::UnsafeCell;
2use core::ptr::NonNull;
3use core::sync::atomic::{AtomicBool, Ordering};
4
5use super::traits::StaticStorage;
6
7/// A cell to use in statics, allowing them to be 'claimed' by a storage,
8/// preventing aliased usage of the backing item.
9pub struct StorageCell<S>(UnsafeCell<S>, AtomicBool);
10
11impl<S> StorageCell<S> {
12    /// Create a new storage cell containing the provided value
13    pub const fn new(val: S) -> StorageCell<S> {
14        StorageCell(UnsafeCell::new(val), AtomicBool::new(false))
15    }
16
17    /// Attempt to claim this `StorageCell` without locking. Returns
18    /// `Some` with the newly created storage if the cell is unclaimed,
19    /// otherwise returns `None`.
20    pub fn try_claim<T>(&'static self) -> Option<T>
21    where
22        T: StaticStorage<S>,
23    {
24        if self.inner_try_claim() {
25            Some(T::take_cell(self))
26        } else {
27            None
28        }
29    }
30
31    /// Attempt to claim this `StorageCell` without locking.
32    ///
33    /// # Panics
34    ///
35    /// If the `StorageCell` has already been claimed, either by this or another thread.
36    pub fn claim<T>(&'static self) -> T
37    where
38        T: StaticStorage<S>,
39    {
40        self.try_claim::<T>()
41            .unwrap_or_else(|| panic!("StorageCell already claimed by existing storage"))
42    }
43
44    pub(crate) fn release(&self) {
45        assert!(self.inner_try_release(), "Couldn't release StorageCell");
46    }
47
48    fn inner_try_claim(&self) -> bool {
49        self.1
50            .compare_exchange(false, true, Ordering::SeqCst, Ordering::Acquire)
51            .map_or(false, |val| !val)
52    }
53
54    fn inner_try_release(&self) -> bool {
55        self.1
56            .compare_exchange(true, false, Ordering::SeqCst, Ordering::Relaxed)
57            .is_ok()
58    }
59
60    pub(super) unsafe fn as_ptr(&self) -> NonNull<S> {
61        debug_assert!(
62            self.1.load(Ordering::SeqCst),
63            "Cell accessed while not claimed"
64        );
65        // SAFETY: UnsafeCell should never return a null pointer
66        unsafe { NonNull::new_unchecked(self.0.get()) }
67    }
68}
69
70// SAFETY: This type requires as a safety invariant that the inner cell is only accessed while
71//         atomically claimed
72unsafe impl<S: Send> Send for StorageCell<S> {}
73// SAFETY: This type requires as a safety invariant that the inner cell is only accessed while
74//         atomically claimed
75unsafe impl<S: Sync> Sync for StorageCell<S> {}
76
77impl<S> Default for StorageCell<S>
78where
79    S: Default,
80{
81    fn default() -> StorageCell<S> {
82        StorageCell::new(S::default())
83    }
84}