use core::cell::UnsafeCell;
use core::ptr::NonNull;
use core::sync::atomic::{AtomicBool, Ordering};
use super::traits::StaticStorage;
pub struct StorageCell<S>(UnsafeCell<S>, AtomicBool);
impl<S> StorageCell<S> {
pub const fn new(val: S) -> StorageCell<S> {
StorageCell(UnsafeCell::new(val), AtomicBool::new(false))
}
pub fn try_claim<T>(&'static self) -> Option<T>
where
T: StaticStorage<S>,
{
if self.inner_try_claim() {
Some(T::take_cell(self))
} else {
None
}
}
pub fn claim<T>(&'static self) -> T
where
T: StaticStorage<S>,
{
self.try_claim::<T>()
.unwrap_or_else(|| panic!("StorageCell already claimed by existing storage"))
}
pub(crate) fn release(&self) {
assert!(self.inner_try_release(), "Couldn't release StorageCell");
}
fn inner_try_claim(&self) -> bool {
self.1
.compare_exchange(false, true, Ordering::SeqCst, Ordering::Acquire)
.map_or(false, |val| !val)
}
fn inner_try_release(&self) -> bool {
self.1
.compare_exchange(true, false, Ordering::SeqCst, Ordering::Relaxed)
.is_ok()
}
pub(super) unsafe fn as_ptr(&self) -> NonNull<S> {
debug_assert!(
self.1.load(Ordering::SeqCst),
"Cell accessed while not claimed"
);
unsafe { NonNull::new_unchecked(self.0.get()) }
}
}
unsafe impl<S: Send> Send for StorageCell<S> {}
unsafe impl<S: Sync> Sync for StorageCell<S> {}
impl<S> Default for StorageCell<S>
where
S: Default,
{
fn default() -> StorageCell<S> {
StorageCell::new(S::default())
}
}