#[cfg(test)]
#[doc(hidden)]
mod tests;
use std::cell::UnsafeCell;
use std::fmt::{Debug, Display, Formatter};
use std::sync::atomic::{AtomicBool, Ordering};
pub struct DoOnce(bool);
impl DoOnce {
pub const fn new() -> Self { DoOnce(false) }
pub fn do_once<F: FnOnce()>(&mut self, func: F) {
if !self.0 {
func();
self.0 = true;
}
}
pub fn done(&self) -> bool { self.0 }
}
pub struct DoOnceSync(AtomicBool);
impl DoOnceSync {
pub fn new() -> Self { DoOnceSync(AtomicBool::new(false)) }
pub fn do_once<F: FnOnce()>(&self, func: F) {
let prev = self.0.swap(true, Ordering::SeqCst);
if !prev {
func();
}
}
pub fn done(&self) -> bool { self.0.load(Ordering::SeqCst) }
}
unsafe impl Send for DoOnceSync {}
unsafe impl Sync for DoOnceSync {}
pub struct InitOnce<T> {
inner: UnsafeCell<Option<T>>,
lock: AtomicBool,
}
impl<T> InitOnce<T> {
pub const fn uninitialized() -> Self {
InitOnce { inner: UnsafeCell::new(None), lock: AtomicBool::new(false) }
}
pub fn try_get(&self) -> Option<&T> {
if self.lock.swap(true, Ordering::SeqCst) { return None }
let inner: &Option<T> = unsafe { &*self.inner.get() };
self.lock.store(false, Ordering::SeqCst);
inner.as_ref()
}
pub fn get(&self) -> &T {
unsafe {
let r = self.inner.get().as_ref().unwrap();
match r {
Some(r) => r,
None => panic!("Tried to access InitOnce<{}> before initialization", std::any::type_name::<T>())
}
}
}
pub fn get_or_init<F>(&self, func: F) -> Result<&T, String> where F: FnOnce() -> T {
self.init_internal(func, false)?;
Ok(self.get())
}
pub fn initialize(&self, value: T) -> Result<(), String> {
self.init_internal(|| value, true)?;
Ok(())
}
fn init_internal<F>(&self, func: F, fail_on_reinit: bool) -> Result<&T, String> where F: FnOnce() -> T {
let prev = self.lock.swap(true, Ordering::SeqCst);
if prev {
return Err(format!("Tried to initialize InitOnce<{}> twice at the same time", std::any::type_name::<T>()));
}
unsafe {
let ptr = self.inner.get();
if (*ptr).is_some() {
if fail_on_reinit {
return Err(format!("Tried to initialize InitOnce<{}> a second time", std::any::type_name::<T>()));
}
}
else {
ptr.write(Some(func()));
}
}
self.lock.store(false, Ordering::SeqCst);
Ok(self.get())
}
}
impl <T: Debug> Debug for InitOnce<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
T::fmt(self.get(), f)
}
}
impl <T: Display> Display for InitOnce<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
T::fmt(self.get(), f)
}
}
unsafe impl<T: Send> Send for InitOnce<T> {}
unsafe impl<T: Sync> Sync for InitOnce<T> {}