use iceoryx2_bb_concurrency::atomic::AtomicBool;
use iceoryx2_bb_concurrency::atomic::Ordering;
use iceoryx2_bb_concurrency::cell::UnsafeCell;
#[derive(Debug)]
pub struct LazySingleton<T> {
data: UnsafeCell<Option<T>>,
is_initialized: AtomicBool,
is_finalized: AtomicBool,
}
unsafe impl<T: Send> Send for LazySingleton<T> {}
unsafe impl<T: Send + Sync> Sync for LazySingleton<T> {}
impl<T> Default for LazySingleton<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> LazySingleton<T> {
#[cfg(not(all(test, loom, feature = "std")))]
pub const fn new() -> Self {
Self {
data: UnsafeCell::new(None),
is_initialized: AtomicBool::new(false),
is_finalized: AtomicBool::new(false),
}
}
#[cfg(all(test, loom, feature = "std"))]
pub fn new() -> Self {
Self {
data: UnsafeCell::new(None),
is_initialized: IoxAtomicBool::new(false),
is_finalized: IoxAtomicBool::new(false),
}
}
pub fn is_initialized(&self) -> bool {
self.is_initialized.load(Ordering::Relaxed)
}
pub fn set_value(&self, value: T) -> bool {
let is_initialized =
self.is_initialized
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed);
if is_initialized.is_err() {
return false;
}
unsafe { *self.data.get() = Some(value) };
self.is_finalized.store(true, Ordering::Release);
true
}
pub fn get(&self) -> &T {
if self.is_finalized.load(Ordering::Acquire) {
return unsafe { self.data.get().as_ref().unwrap().as_ref().unwrap() };
}
if !self.is_initialized.load(Ordering::Relaxed) {
panic!("You cannot acquire an unset value");
}
while !self.is_finalized.load(Ordering::Acquire) {
core::hint::spin_loop()
}
unsafe { self.data.get().as_ref().unwrap().as_ref().unwrap() }
}
}