#![no_std]
use core::cell::UnsafeCell;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::sync::atomic::{AtomicBool, Ordering};
pub struct SpinCell<T, G = fn() -> T> {
lock: AtomicBool,
is_initialized: AtomicBool,
cell: MaybeUninit<UnsafeCell<T>>,
init_func: UnsafeCell<ManuallyDrop<G>>,
}
unsafe impl<T: Sync, G> Sync for SpinCell<T, G> {}
unsafe impl<T: Send, G> Send for SpinCell<T, G> {}
impl<T, G: FnOnce() -> T> SpinCell<T, G> {
#[inline(always)]
pub const fn new(init_func: G) -> SpinCell<T, G> {
Self {
lock: AtomicBool::new(false),
is_initialized: AtomicBool::new(false),
cell: MaybeUninit::uninit(),
init_func: UnsafeCell::new(ManuallyDrop::new(init_func)),
}
}
pub unsafe fn force_initialize(&self) {
while self
.lock
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
if self.is_initialized.load(Ordering::Acquire) {
self.lock.store(false, Ordering::Release);
return;
}
let data = &mut *self.init_func.get();
let init_func = ManuallyDrop::take(data);
let value = init_func();
let ptr = self.cell.as_ptr() as *mut UnsafeCell<T>;
core::ptr::write(ptr, UnsafeCell::new(value));
self.is_initialized.store(true, Ordering::Release);
self.lock.store(false, Ordering::Release);
}
pub fn try_initialize(me: &SpinCell<T, G>) -> Result<(), ()> {
if me.is_initialized.load(Ordering::Acquire) {
return Err(());
}
unsafe {
me.force_initialize();
}
Ok(())
}
}
impl<T, G: FnOnce() -> T> core::ops::Deref for SpinCell<T, G> {
type Target = T;
fn deref(&self) -> &T {
match SpinCell::try_initialize(self) {
Ok(()) => {} Err(()) => {} }
unsafe { &*self.cell.assume_init_ref().get() }
}
}
impl<T, G> Drop for SpinCell<T, G> {
fn drop(&mut self) {
if self.is_initialized.load(Ordering::Acquire) {
let cell_ptr = self.cell.as_mut_ptr() as *mut UnsafeCell<T>;
unsafe {
core::ptr::drop_in_place((*cell_ptr).get());
}
} else {
unsafe {
ManuallyDrop::drop(&mut *self.init_func.get());
}
}
}
}