#![forbid(unsafe_op_in_unsafe_fn)]
#![warn(
missing_docs,
rust_2018_idioms,
clippy::cargo,
clippy::semicolon_if_nothing_returned
)]
#![cfg_attr(feature = "nightly", feature(doc_cfg, impl_trait_in_assoc_type))]
#![doc = include_str!("../README.md")]
use std::{
cell::UnsafeCell,
fmt,
future::Future,
mem::ManuallyDrop,
ops::Drop,
panic::{RefUnwindSafe, UnwindSafe},
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
};
use tokio::sync::{Semaphore, SemaphorePermit};
enum LazyUninitData<Fut, F> {
Function(ManuallyDrop<F>),
Future(ManuallyDrop<Fut>),
}
union LazyData<T, F, Fut> {
init: ManuallyDrop<Option<T>>,
uninit: ManuallyDrop<LazyUninitData<F, Fut>>,
}
pub struct Lazy<T, Fut = Pin<Box<dyn Future<Output = T> + Send>>, F = fn() -> Fut> {
value_set: AtomicBool,
value: UnsafeCell<LazyData<T, Fut, F>>,
semaphore: Semaphore,
}
impl<T, Fut, F> Lazy<T, Fut, F> {
#[must_use]
#[inline]
pub const fn new(init: F) -> Self {
Self {
value_set: AtomicBool::new(false),
value: UnsafeCell::new(LazyData {
uninit: ManuallyDrop::new(LazyUninitData::Function(ManuallyDrop::new(init))),
}),
semaphore: Semaphore::const_new(1),
}
}
#[deprecated(note = "use `new` instead")]
#[doc(hidden)]
#[must_use]
#[inline]
pub const fn const_new(init: F) -> Self {
Self::new(init)
}
#[must_use]
#[inline]
fn initialized(&self) -> bool {
self.value_set.load(Ordering::Acquire)
}
#[must_use]
#[inline]
fn initialized_mut(&mut self) -> bool {
*self.value_set.get_mut()
}
#[must_use]
#[inline]
fn initialized_pin_mut(self: Pin<&mut Self>) -> bool {
unsafe { *self.get_unchecked_mut().value_set.get_mut() }
}
#[must_use]
#[inline]
unsafe fn get_unchecked(&self) -> Option<&T> {
unsafe { (*self.value.get()).init.as_ref() }
}
#[must_use]
#[inline]
unsafe fn get_unchecked_mut(&mut self) -> Option<&mut T> {
unsafe { self.value.get_mut().init.as_mut() }
}
#[must_use]
#[inline]
unsafe fn get_unchecked_pin(self: Pin<&Self>) -> Option<Pin<&T>> {
unsafe {
(*self.value.get())
.init
.as_ref()
.map(|r| Pin::new_unchecked(r))
}
}
#[must_use]
#[inline]
unsafe fn get_unchecked_pin_mut(self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
#[allow(clippy::needless_borrow)]
unsafe {
self.get_unchecked_mut()
.value
.get_mut()
.init
.as_mut()
.map(|r| Pin::new_unchecked(r))
}
}
#[must_use]
#[inline]
pub fn get(&self) -> Option<&T> {
if self.initialized() {
unsafe { self.get_unchecked() }
} else {
None
}
}
#[must_use]
#[inline]
pub fn get_mut(&mut self) -> Option<&mut T> {
if self.initialized_mut() {
unsafe { self.get_unchecked_mut() }
} else {
None
}
}
#[must_use]
#[inline]
pub fn get_pin(self: Pin<&Self>) -> Option<Pin<&T>> {
if self.initialized() {
unsafe { self.get_unchecked_pin() }
} else {
None
}
}
#[must_use]
#[inline]
pub fn get_pin_mut(mut self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
if self.as_mut().initialized_pin_mut() {
unsafe { self.get_unchecked_pin_mut() }
} else {
None
}
}
}
impl<T, Fut, F> Lazy<T, Fut, F>
where
Fut: Future<Output = T> + Unpin,
F: FnOnce() -> Fut,
{
#[inline]
pub async fn force(&self) -> &T {
if !self.initialized() {
unsafe {
Pin::new_unchecked(self).initialize().await;
}
}
(unsafe { self.get_unchecked() })
.unwrap_or_else(|| panic!("Lazy instance has previously been poisoned"))
}
#[inline]
pub async fn force_mut(&mut self) -> &mut T {
if !self.initialized_mut() {
unsafe {
Pin::new_unchecked(&*self).initialize().await;
}
}
(unsafe { self.get_unchecked_mut() })
.unwrap_or_else(|| panic!("Lazy instance has previously been poisoned"))
}
}
impl<T, Fut, F> Lazy<T, Fut, F>
where
Fut: Future<Output = T>,
F: FnOnce() -> Fut,
{
#[cold]
async fn initialize(self: Pin<&Self>) {
match self.semaphore.acquire().await {
Ok(permit) => {
debug_assert!(!self.initialized());
enum InitializationState<T> {
Initializing,
Initialized(ManuallyDrop<Option<T>>),
}
struct InitializeOnDrop<'a, 'permit, T, Fut, F> {
lazy: Pin<&'a Lazy<T, Fut, F>>,
value: InitializationState<T>,
permit: ManuallyDrop<SemaphorePermit<'permit>>,
}
impl<T, Fut, F> Drop for InitializeOnDrop<'_, '_, T, Fut, F> {
fn drop(&mut self) {
match self.value {
InitializationState::Initializing => {
unsafe {
drop(ManuallyDrop::take(&mut self.permit));
}
}
InitializationState::Initialized(ref mut value) => {
unsafe {
(*self.lazy.value.get()).init =
ManuallyDrop::new(ManuallyDrop::take(value));
};
self.lazy.value_set.store(true, Ordering::Release);
self.lazy.semaphore.close();
}
}
}
}
#[allow(clippy::let_and_return)]
let uninit_data: &mut ManuallyDrop<LazyUninitData<Fut, F>> = unsafe {
let ptr = self.value.get();
let mut_ref = &mut (*ptr).uninit;
mut_ref
};
let mut initialize_on_drop = InitializeOnDrop {
lazy: self,
value: InitializationState::Initialized(ManuallyDrop::new(None)),
permit: ManuallyDrop::new(permit),
};
struct Wrapper<Fut>(*mut Fut);
unsafe impl<Fut> Send for Wrapper<Fut> {}
unsafe impl<Fut> Sync for Wrapper<Fut> {}
let fut_ptr: Wrapper<Fut> = {
let mut_ptr: *mut ManuallyDrop<Fut> = match &mut **uninit_data {
LazyUninitData::Function(f) => {
let f = unsafe { ManuallyDrop::take(f) };
let fut = f();
**uninit_data = LazyUninitData::Future(ManuallyDrop::new(fut));
match &mut **uninit_data {
LazyUninitData::Future(fut) => fut,
_ => unreachable!("We just set this to LazyUninitData::Future"),
}
}
LazyUninitData::Future(fut) => fut,
};
Wrapper(mut_ptr.cast())
};
let fut: Pin<&mut Fut> = unsafe { Pin::new_unchecked(&mut *fut_ptr.0) };
initialize_on_drop.value = InitializationState::Initializing;
let result = fut.await;
initialize_on_drop.value =
InitializationState::Initialized(ManuallyDrop::new(Some(result)));
unsafe { core::ptr::drop_in_place(fut_ptr.0) }
drop(initialize_on_drop);
}
Err(_) => {
debug_assert!(self.initialized());
}
}
}
#[inline]
pub async fn force_pin(self: Pin<&Self>) -> Pin<&T> {
if !self.initialized() {
self.initialize().await;
}
(unsafe { self.get_unchecked_pin() })
.unwrap_or_else(|| panic!("Lazy instance has previously been poisoned"))
}
#[inline]
pub async fn force_pin_mut(mut self: Pin<&mut Self>) -> Pin<&mut T> {
if !self.as_mut().initialized_pin_mut() {
self.as_ref().initialize().await;
}
(unsafe { self.get_unchecked_pin_mut() })
.unwrap_or_else(|| panic!("Lazy instance has previously been poisoned"))
}
}
impl<T, Fut, F> Drop for Lazy<T, Fut, F> {
#[inline]
fn drop(&mut self) {
if self.initialized_mut() {
unsafe { ManuallyDrop::drop(&mut self.value.get_mut().init) };
} else {
unsafe {
match &mut *self.value.get_mut().uninit {
LazyUninitData::Function(f) => ManuallyDrop::drop(f),
LazyUninitData::Future(fut) => ManuallyDrop::drop(fut),
}
};
}
}
}
impl<T: fmt::Debug, Fut, F> fmt::Debug for Lazy<T, Fut, F> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.get() {
Some(v) => f.debug_tuple("Lazy").field(v).finish(),
None => f.write_str("Lazy(Uninit)"),
}
}
}
unsafe impl<T: Send, Fut: Send, F: Send> Send for Lazy<T, Fut, F> {}
unsafe impl<T: Send + Sync, Fut: Send, F: Send> Sync for Lazy<T, Fut, F> {}
impl<T: UnwindSafe, Fut: UnwindSafe, F: UnwindSafe> UnwindSafe for Lazy<T, Fut, F> {}
impl<T: UnwindSafe + RefUnwindSafe, Fut: UnwindSafe, F: UnwindSafe> RefUnwindSafe
for Lazy<T, F, Fut>
{
}
impl<T: Unpin, Fut: Unpin, F> Unpin for Lazy<T, Fut, F> {}
#[cfg(feature = "nightly")]
#[doc(cfg(feature = "nightly"))]
impl<'a, T, Fut, F> std::future::IntoFuture for &'a Lazy<T, Fut, F>
where
Fut: Future<Output = T> + Unpin,
F: FnOnce() -> Fut,
{
type Output = &'a T;
type IntoFuture = impl Future<Output = Self::Output>;
#[inline]
fn into_future(self) -> Self::IntoFuture {
self.force()
}
}
#[cfg(feature = "nightly")]
#[doc(cfg(feature = "nightly"))]
impl<'a, T, Fut, F> std::future::IntoFuture for &'a mut Lazy<T, Fut, F>
where
Fut: Future<Output = T> + Unpin,
F: FnOnce() -> Fut,
{
type Output = &'a mut T;
type IntoFuture = impl Future<Output = Self::Output>;
#[inline]
fn into_future(self) -> Self::IntoFuture {
self.force_mut()
}
}
#[cfg(feature = "nightly")]
#[doc(cfg(feature = "nightly"))]
impl<'a, T, Fut, F> std::future::IntoFuture for Pin<&'a Lazy<T, Fut, F>>
where
Fut: Future<Output = T>,
F: FnOnce() -> Fut,
{
type Output = Pin<&'a T>;
type IntoFuture = impl Future<Output = Self::Output>;
#[inline]
fn into_future(self) -> Self::IntoFuture {
self.force_pin()
}
}
#[cfg(feature = "nightly")]
#[doc(cfg(feature = "nightly"))]
impl<'a, T, Fut, F> std::future::IntoFuture for Pin<&'a mut Lazy<T, Fut, F>>
where
Fut: Future<Output = T>,
F: FnOnce() -> Fut,
{
type Output = Pin<&'a mut T>;
type IntoFuture = impl Future<Output = Self::Output>;
#[inline]
fn into_future(self) -> Self::IntoFuture {
self.force_pin_mut()
}
}