use core::{
fmt,
future::Future,
pin::Pin,
ptr::NonNull,
task::{Context, Poll},
};
use pin_utils::pin_mut;
#[cfg(test)]
mod tests;
pub trait EarlyDrop {
#[inline]
unsafe fn early_drop(self: Pin<&Self>) {}
}
#[pin_project::pin_project]
pub struct EarlyDropGuard<T: EarlyDrop> {
#[pin]
holder: Option<Holder<T>>,
storage: Option<NonNull<T>>,
}
unsafe impl<T: EarlyDrop + Send> Send for EarlyDropGuard<T> {}
unsafe impl<T: EarlyDrop + Sync> Sync for EarlyDropGuard<T> {}
type Holder<T: EarlyDrop> = impl Future<Output = !>;
impl<T: EarlyDrop> EarlyDropGuard<T> {
pub const fn new() -> Self {
Self {
holder: None,
storage: None,
}
}
#[inline]
pub fn get(&self) -> Option<Pin<&T>> {
Self::get_inner(self.storage)
}
#[inline]
fn get_inner<'a>(storage: Option<NonNull<T>>) -> Option<Pin<&'a T>> {
storage.map(|storage| {
unsafe { Pin::new_unchecked(storage.as_ref()) }
})
}
pub fn get_or_insert_with(self: Pin<&mut Self>, init: impl FnOnce() -> T) -> Pin<&T> {
let mut this = self.project();
if let Some(inner) = Self::get_inner(*this.storage) {
return inner;
}
let p_storage = &mut *this.storage as *mut Option<NonNull<T>>;
assert!(this.holder.is_none());
#[inline]
fn make_holder<T: EarlyDrop>(p_storage: *mut Option<NonNull<T>>, init: T) -> Holder<T> {
async move {
let inner = init;
pin_mut!(inner);
let guard = DoEarlyDropOnDrop(Pin::as_ref(&inner));
unsafe { *p_storage = Some(NonNull::from(&*guard.0)) };
loop {
core::future::pending::<!>().await;
}
}
}
this.holder.set(Some(make_holder(p_storage, init())));
let mut holder = this.holder.as_pin_mut().unwrap();
loop {
match Pin::as_mut(&mut holder)
.poll(&mut Context::from_waker(&futures::task::noop_waker()))
{
Poll::Ready(never) => match never {},
Poll::Pending => {}
}
if let Some(inner) = Self::get_inner(*this.storage) {
return inner;
}
}
}
pub fn get_or_insert_default(self: Pin<&mut Self>) -> Pin<&T>
where
T: Default,
{
self.get_or_insert_with(T::default)
}
}
struct DoEarlyDropOnDrop<'a, T: EarlyDrop>(Pin<&'a T>);
impl<T: EarlyDrop> Drop for DoEarlyDropOnDrop<'_, T> {
#[inline]
fn drop(&mut self) {
unsafe { self.0.early_drop() };
}
}
impl<T: EarlyDrop + fmt::Debug> fmt::Debug for EarlyDropGuard<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(inner) = self.get() {
fmt::Debug::fmt(&inner, f)
} else {
f.write_str("< uninitialized >")
}
}
}