use std::{
cell::UnsafeCell,
panic::{RefUnwindSafe, UnwindSafe},
sync::atomic::{AtomicBool, Ordering},
};
use parking_lot::{lock_api::RawMutex as _RawMutex, RawMutex};
pub(crate) struct OnceCell<T> {
mutex: Mutex,
is_initialized: AtomicBool,
pub(crate) value: UnsafeCell<Option<T>>,
}
unsafe impl<T: Sync + Send> Sync for OnceCell<T> {}
unsafe impl<T: Send> Send for OnceCell<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceCell<T> {}
impl<T: UnwindSafe> UnwindSafe for OnceCell<T> {}
impl<T> OnceCell<T> {
pub(crate) const fn new() -> OnceCell<T> {
OnceCell {
mutex: Mutex::new(),
is_initialized: AtomicBool::new(false),
value: UnsafeCell::new(None),
}
}
#[inline]
pub(crate) fn is_initialized(&self) -> bool {
self.is_initialized.load(Ordering::Acquire)
}
#[cold]
pub(crate) fn initialize<F, E>(&self, f: F) -> Result<(), E>
where
F: FnOnce() -> Result<T, E>,
{
let _guard = self.mutex.lock();
if !self.is_initialized() {
let value = f()?;
let slot: &mut Option<T> = unsafe { &mut *self.value.get() };
debug_assert!(slot.is_none());
*slot = Some(value);
self.is_initialized.store(true, Ordering::Release);
}
Ok(())
}
}
struct Mutex {
inner: RawMutex,
}
impl Mutex {
const fn new() -> Mutex {
Mutex { inner: RawMutex::INIT }
}
fn lock(&self) -> MutexGuard<'_> {
self.inner.lock();
MutexGuard { inner: &self.inner }
}
}
struct MutexGuard<'a> {
inner: &'a RawMutex,
}
impl Drop for MutexGuard<'_> {
fn drop(&mut self) {
self.inner.unlock();
}
}
#[test]
#[cfg(pointer_width = "64")]
fn test_size() {
use std::mem::size_of;
assert_eq!(size_of::<OnceCell<u32>>, 2 * size_of::<u32>);
}