use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
pub struct LazyUsize(AtomicUsize);
impl LazyUsize {
pub const fn new() -> Self {
Self(AtomicUsize::new(Self::UNINIT))
}
pub const UNINIT: usize = usize::max_value();
pub const ACTIVE: usize = usize::max_value() - 1;
pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
let mut val = self.0.load(Relaxed);
if val == Self::UNINIT {
val = init();
self.0.store(val, Relaxed);
}
val
}
pub fn sync_init(&self, init: impl FnOnce() -> usize, mut wait: impl FnMut()) -> usize {
match self.0.load(Relaxed) {
Self::UNINIT | Self::ACTIVE => {}
val => return val,
}
loop {
match self.0.compare_and_swap(Self::UNINIT, Self::ACTIVE, Relaxed) {
Self::UNINIT => {
let val = init();
self.0.store(
match val {
Self::UNINIT | Self::ACTIVE => Self::UNINIT,
val => val,
},
Relaxed,
);
return val;
}
Self::ACTIVE => wait(),
val => return val,
}
}
}
}
pub struct LazyBool(LazyUsize);
impl LazyBool {
pub const fn new() -> Self {
Self(LazyUsize::new())
}
pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
self.0.unsync_init(|| init() as usize) != 0
}
}