use core::cell::UnsafeCell;
use core::future::Future;
use core::sync::atomic::Ordering;
use core::{fmt, mem};
use super::state::OnceLock;
pub struct Once<T> {
value: UnsafeCell<mem::MaybeUninit<T>>,
lock: OnceLock,
}
impl<T> Once<T> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
lock: OnceLock::new(),
value: UnsafeCell::new(mem::MaybeUninit::uninit()),
}
}
#[inline]
#[must_use]
pub const fn with_value(value: T) -> Self {
Self {
lock: OnceLock::done(),
value: UnsafeCell::new(mem::MaybeUninit::new(value)),
}
}
#[inline]
pub fn is_done(&self) -> bool {
self.lock.is_done(Ordering::Relaxed)
}
#[inline]
pub fn get(&self) -> Option<&T> {
if self.is_done() {
Some(unsafe { self.get_unchecked() })
} else {
None
}
}
#[inline]
pub fn get_mut(&mut self) -> Option<&mut T> {
if self.is_done() {
Some(unsafe { self.get_unchecked_mut() })
} else {
None
}
}
#[inline]
pub fn try_set(&self, value: T) -> Result<&T, T> {
let Some(guard) = self.lock.try_lock() else {
return Err(value);
};
let refv = unsafe { (*self.value.get()).write(value) };
guard.commit();
Ok(refv)
}
#[inline]
pub fn replace_mut(&mut self, value: T) -> Option<T> {
let mref = self.value.get_mut();
if self.lock.set_done() {
mref.write(value);
None
} else {
let old_value = unsafe { mref.assume_init_mut() };
Some(mem::replace(old_value, value))
}
}
#[inline]
pub fn get_mut_or_set(&mut self, value: T) -> &mut T {
if self.lock.set_done() {
self.value.get_mut().write(value);
}
unsafe { self.get_unchecked_mut() }
}
#[inline]
pub fn get_mut_or_default(&mut self) -> &mut T
where
T: Default,
{
if self.lock.set_done() {
self.value.get_mut().write(T::default());
}
unsafe { self.get_unchecked_mut() }
}
#[inline]
pub unsafe fn get_unchecked(&self) -> &T {
debug_assert!(self.is_done(), "get_unchecked called on uninitialized Once");
(*self.value.get()).assume_init_ref()
}
#[inline]
pub unsafe fn get_unchecked_mut(&mut self) -> &mut T {
debug_assert!(
self.is_done(),
"get_unchecked_mut called on uninitialized Once"
);
unsafe { (*self.value.get()).assume_init_mut() }
}
#[inline]
pub fn take(&mut self) -> Option<T> {
if self.lock.set_uninit() {
unsafe { Some((*self.value.get()).assume_init_read()) }
} else {
None
}
}
#[inline]
pub fn set(&self, value: T) -> Result<(), T> {
match self.try_insert(value) {
Ok(_) => Ok(()), Err((_current_value, original_value)) => Err(original_value), }
}
#[inline]
pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
let mut value_opt = Some(value);
let res_ref = self.get_or_init(|| value_opt.take().unwrap());
match value_opt {
None => Ok(res_ref), Some(original_value) => Err((res_ref, original_value)), }
}
#[inline]
pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
{
if let Some(value) = self.get() {
return value;
}
self.initialize(f);
unsafe { self.get_unchecked() }
}
#[inline]
pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T
where
F: FnOnce() -> T,
{
if self.is_done() {
} else if let Some(guard) = self.lock.try_lock() {
unsafe { (*self.value.get()).write(f()) };
guard.commit();
} else {
unreachable!("Could not lock for init despite having exclusive access");
}
unsafe { self.get_unchecked_mut() }
}
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
{
if let Some(value) = self.get() {
return Ok(value);
}
self.try_initialize(f)?;
debug_assert!(self.is_done());
Ok(unsafe { self.get_unchecked() })
}
pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
{
if let Some(guard) = self.lock.try_lock() {
self.value.get_mut().write(f()?);
guard.commit();
}
Ok(unsafe { self.get_unchecked_mut() })
}
#[inline]
pub async fn get_or_init_async<F, Fut>(&self, f: F) -> &T
where
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
if let Some(value) = self.get() {
return value;
}
self.initialize_async(f).await;
unsafe { self.get_unchecked() }
}
#[inline]
pub async fn get_mut_or_init_async<F, Fut>(&mut self, f: F) -> &mut T
where
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
if let Some(guard) = self.lock.try_lock() {
self.value.get_mut().write(f().await);
guard.commit();
}
unsafe { self.get_unchecked_mut() }
}
pub async fn get_or_try_init_async<F, Fut, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Fut,
Fut: Future<Output = Result<T, E>>,
{
if let Some(value) = self.get() {
return Ok(value);
}
self.try_initialize_async(f).await?;
debug_assert!(self.is_done());
Ok(unsafe { self.get_unchecked() })
}
pub async fn get_mut_or_try_init_async<F, Fut, E>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Fut,
Fut: Future<Output = Result<T, E>>,
{
if let Some(guard) = self.lock.try_lock() {
self.value.get_mut().write(f().await?);
guard.commit();
}
Ok(unsafe { self.get_unchecked_mut() })
}
#[cold]
async fn initialize_async<F, Fut>(&self, f: F)
where
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
let Some(guard) = self.lock.lock_async().await else {
return; };
unsafe { (*self.value.get()).write(f().await) };
guard.commit(); }
#[cold]
async fn try_initialize_async<Fn, Fut, E>(&self, f: Fn) -> Result<(), E>
where
Fn: FnOnce() -> Fut,
Fut: Future<Output = Result<T, E>>,
{
let Some(guard) = self.lock.lock_async().await else {
return Ok(()); };
let value = f().await?; unsafe { (*self.value.get()).write(value) };
guard.commit(); Ok(())
}
#[cold]
fn initialize<F>(&self, f: F)
where
F: FnOnce() -> T,
{
let Some(guard) = self.lock.lock() else {
return; };
unsafe { (*self.value.get()).write(f()) };
guard.commit(); }
#[cold]
fn try_initialize<F, E>(&self, f: F) -> Result<(), E>
where
F: FnOnce() -> Result<T, E>,
{
let Some(guard) = self.lock.lock() else {
return Ok(()); };
let value = f()?; unsafe { (*self.value.get()).write(value) };
guard.commit(); Ok(())
}
}
impl<T> From<Option<T>> for Once<T> {
fn from(value: Option<T>) -> Self {
match value {
Some(value) => Self::with_value(value),
None => Self::new(),
}
}
}
unsafe impl<T: Sync + Send> Sync for Once<T> {}
unsafe impl<T: Send> Send for Once<T> {}
impl<T> Default for Once<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T: fmt::Display> fmt::Display for Once<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.get() {
Some(v) => fmt::Display::fmt(v, f),
None => f.write_str("<uninit>"),
}
}
}
impl<T: fmt::Debug> fmt::Debug for Once<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_tuple("Once");
match self.get() {
Some(v) => d.field(v),
None => d.field(&format_args!("<uninit>")),
};
d.finish()
}
}
impl<T: Clone> Clone for Once<T> {
#[inline]
fn clone(&self) -> Self {
match self.get() {
Some(value) => Self::with_value(value.clone()),
None => Self::new(),
}
}
}
impl<T> From<T> for Once<T> {
#[inline]
fn from(value: T) -> Self {
Self::with_value(value)
}
}
impl<T: PartialEq> PartialEq for Once<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl<T: Eq> Eq for Once<T> {}
impl<T> Drop for Once<T> {
#[inline]
fn drop(&mut self) {
if self.is_done() {
unsafe { self.value.get_mut().assume_init_drop() };
}
}
}