use super::Notify;
use crate::loom::cell::UnsafeCell;
use crate::loom::sync::atomic::AtomicBool;
use std::error::Error;
use std::fmt;
use std::future::{poll_fn, Future};
use std::mem::MaybeUninit;
use std::ops::Drop;
use std::ptr;
use std::sync::atomic::Ordering;
use std::task::Poll;
pub struct SetOnce<T> {
value_set: AtomicBool,
value: UnsafeCell<MaybeUninit<T>>,
notify: Notify,
}
impl<T> Default for SetOnce<T> {
fn default() -> SetOnce<T> {
SetOnce::new()
}
}
impl<T: fmt::Debug> fmt::Debug for SetOnce<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("SetOnce")
.field("value", &self.get())
.finish()
}
}
impl<T: Clone> Clone for SetOnce<T> {
fn clone(&self) -> SetOnce<T> {
SetOnce::new_with(self.get().cloned())
}
}
impl<T: PartialEq> PartialEq for SetOnce<T> {
fn eq(&self, other: &SetOnce<T>) -> bool {
self.get() == other.get()
}
}
impl<T: Eq> Eq for SetOnce<T> {}
impl<T> Drop for SetOnce<T> {
fn drop(&mut self) {
if self.value_set.load(Ordering::Relaxed) {
unsafe { self.value.with_mut(|ptr| ptr::drop_in_place(ptr as *mut T)) }
}
}
}
impl<T> From<T> for SetOnce<T> {
fn from(value: T) -> Self {
SetOnce {
value_set: AtomicBool::new(true),
value: UnsafeCell::new(MaybeUninit::new(value)),
notify: Notify::new(),
}
}
}
impl<T> SetOnce<T> {
pub fn new() -> Self {
Self {
value_set: AtomicBool::new(false),
value: UnsafeCell::new(MaybeUninit::uninit()),
notify: Notify::new(),
}
}
#[cfg(not(all(loom, test)))]
pub const fn const_new() -> Self {
Self {
value_set: AtomicBool::new(false),
value: UnsafeCell::new(MaybeUninit::uninit()),
notify: Notify::const_new(),
}
}
pub fn new_with(value: Option<T>) -> Self {
if let Some(v) = value {
SetOnce::from(v)
} else {
SetOnce::new()
}
}
#[cfg(not(all(loom, test)))]
pub const fn const_new_with(value: T) -> Self {
Self {
value_set: AtomicBool::new(true),
value: UnsafeCell::new(MaybeUninit::new(value)),
notify: Notify::const_new(),
}
}
pub fn initialized(&self) -> bool {
self.value_set.load(Ordering::Acquire)
}
unsafe fn get_unchecked(&self) -> &T {
&*self.value.with(|ptr| (*ptr).as_ptr())
}
pub fn get(&self) -> Option<&T> {
if self.initialized() {
Some(unsafe { self.get_unchecked() })
} else {
None
}
}
pub fn set(&self, value: T) -> Result<(), SetOnceError<T>> {
if self.initialized() {
return Err(SetOnceError(value));
}
let guard = self.notify.lock_waiter_list();
if self.initialized() {
return Err(SetOnceError(value));
}
unsafe {
self.value.with_mut(|ptr| (*ptr).as_mut_ptr().write(value));
}
self.value_set.store(true, Ordering::Release);
guard.notify_waiters();
Ok(())
}
pub fn into_inner(self) -> Option<T> {
let value_set = self.value_set.load(Ordering::Relaxed);
if value_set {
self.value_set.store(false, Ordering::Relaxed);
Some(unsafe { self.value.with_mut(|ptr| ptr::read(ptr).assume_init()) })
} else {
None
}
}
pub async fn wait(&self) -> &T {
loop {
if let Some(val) = self.get() {
return val;
}
let notify_fut = self.notify.notified();
pin!(notify_fut);
poll_fn(|cx| {
let ret = notify_fut.as_mut().poll(cx);
if self.value_set.load(Ordering::Relaxed) {
return Poll::Ready(());
}
ret
})
.await;
}
}
}
unsafe impl<T: Sync + Send> Sync for SetOnce<T> {}
unsafe impl<T: Send> Send for SetOnce<T> {}
#[derive(Debug, PartialEq, Eq)]
pub struct SetOnceError<T>(pub T);
impl<T> fmt::Display for SetOnceError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SetOnceError")
}
}
impl<T: fmt::Debug> Error for SetOnceError<T> {}