use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ops::Deref;
use core::sync::atomic::{AtomicU8, Ordering};
pub struct Static<A> {
pub init: fn() -> A,
allocator: UnsafeCell<MaybeUninit<A>>,
state: AtomicU8,
}
unsafe impl<A> Sync for Static<A> {}
const UNINIT: u8 = 0;
const LOCKED: u8 = 1;
const INIT: u8 = 2;
impl<A> Static<A> {
pub const fn new(init: fn() -> A) -> Self
where
A: crate::Allocator,
{
Self {
init,
allocator: UnsafeCell::new(MaybeUninit::uninit()),
state: AtomicU8::new(UNINIT),
}
}
pub fn get(&self) -> &A {
while self.state.load(Ordering::SeqCst) == LOCKED {
core::hint::spin_loop();
}
if self.state.swap(LOCKED, Ordering::SeqCst) == INIT {
self.state.store(INIT, Ordering::SeqCst);
return unsafe { &*self.allocator.get().cast() };
}
let alloc = unsafe { &mut *self.allocator.get() };
alloc.write((self.init)());
self.state.store(INIT, Ordering::SeqCst);
unsafe { alloc.assume_init_ref() }
}
}
impl<A> Deref for Static<A> {
type Target = A;
fn deref(&self) -> &Self::Target {
self.get()
}
}