#![no_std]
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::sync::atomic::{AtomicBool, Ordering};
pub struct SpinCell<T: Sized> {
lock: AtomicBool,
is_initialized: AtomicBool,
cell: MaybeUninit<UnsafeCell<T>>,
}
unsafe impl<T: Sized + Sync> Sync for SpinCell<T> {}
unsafe impl<T: Sized + Send> Send for SpinCell<T> {}
impl<T: Sized> SpinCell<T> {
#[inline(always)]
pub const fn new(data: T) -> Self {
Self {
lock: AtomicBool::new(false),
is_initialized: AtomicBool::new(true),
cell: MaybeUninit::new(UnsafeCell::new(data)),
}
}
#[inline(always)]
pub const fn uninit() -> Self {
Self {
lock: AtomicBool::new(false),
is_initialized: AtomicBool::new(false),
cell: MaybeUninit::uninit(),
}
}
pub unsafe fn force_initialize<F>(&self, init_func: F)
where
F: FnOnce() -> T,
{
while self
.lock
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
if self.is_initialized.swap(false, Ordering::AcqRel) {
let cell_ptr = self.cell.as_ptr() as *mut UnsafeCell<T>;
unsafe {
core::ptr::drop_in_place((*cell_ptr).get());
}
}
let newcell = UnsafeCell::new(init_func());
let ptr = self.cell.as_ptr() as *mut UnsafeCell<T>;
unsafe {
core::ptr::write(ptr, newcell);
}
self.is_initialized.store(true, Ordering::Release);
self.lock.store(false, Ordering::Release);
}
pub fn try_initialize<F>(&self, init_func: F) -> Result<(), ()>
where
F: FnOnce() -> T,
{
if self.is_initialized.load(Ordering::Acquire) {
return Err(());
}
unsafe { self.force_initialize(init_func) };
Ok(())
}
}
impl<T: Sized> core::ops::Deref for SpinCell<T> {
type Target = T;
fn deref(&self) -> &T {
assert!(
self.is_initialized.load(Ordering::Acquire),
"SpinCell is not initialized yet."
);
unsafe { &*self.cell.assume_init_ref().get() }
}
}