#![warn(missing_docs)]
#![crate_name = "async_oncecell"]
use std::{
cell::UnsafeCell,
convert::Infallible,
fmt,
future::Future,
pin::Pin,
sync::atomic::{AtomicBool, Ordering},
};
use futures::lock::Mutex;
pub struct OnceCell<T> {
lock: Mutex<()>,
initialized: AtomicBool,
inner: UnsafeCell<Option<T>>,
}
unsafe impl<T: Sync + Send> Sync for OnceCell<T> {}
unsafe impl<T: Send> Send for OnceCell<T> {}
impl<T> OnceCell<T> {
pub fn new() -> Self {
Self {
lock: Mutex::new(()),
initialized: AtomicBool::new(false),
inner: UnsafeCell::new(None),
}
}
pub async fn get_or_init<F>(&self, f: F) -> &T
where
F: Future<Output = T>,
{
match self
.get_or_try_init(async { Ok::<_, Infallible>(f.await) })
.await
{
Ok(res) => res,
Err(_) => unreachable!(),
}
}
pub async fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
where
F: Future<Output = Result<T, E>>,
{
if !self.initialized.load(Ordering::Acquire) {
self.set(f).await?;
}
Ok(self.get().unwrap())
}
pub fn get(&self) -> Option<&T> {
unsafe { &*self.inner.get() }.as_ref()
}
async fn set<F, E>(&self, f: F) -> Result<(), E>
where
F: Future<Output = Result<T, E>>,
{
let _guard = self.lock.lock().await;
if !self.initialized.load(Ordering::Acquire) {
match f.await {
Ok(v) => {
unsafe {
*self.inner.get() = Some(v);
};
self.initialized.store(true, Ordering::Release);
Ok(())
}
Err(e) => Err(e),
}
} else {
Ok(())
}
}
pub fn initialized(&self) -> bool {
self.initialized.load(Ordering::Acquire)
}
}
impl<T> Default for OnceCell<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: fmt::Debug> fmt::Debug for OnceCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OnceCell").field(&self.get()).finish()
}
}
impl<T: PartialEq> PartialEq for OnceCell<T> {
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl<T: Eq> Eq for OnceCell<T> {}
pub struct Lazy<T, F = Pin<Box<dyn Future<Output = T> + Send>>> {
cell: OnceCell<T>,
f: Mutex<Option<F>>,
}
impl<T> Lazy<T, Pin<Box<dyn Future<Output = T> + Send>>> {
pub fn new(f: impl Future<Output = T> + 'static + Send) -> Self {
Self {
cell: OnceCell::new(),
f: Mutex::new(Some(Box::pin(f))),
}
}
}
impl<T> Lazy<T, Pin<Box<dyn Future<Output = T> + Send>>> {
pub async fn get(&self) -> &T {
self.cell
.get_or_init(async { self.f.lock().await.take().unwrap().await })
.await
}
}
impl<T: fmt::Debug, F> fmt::Debug for Lazy<T, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Lazy").field(&self.cell.get()).finish()
}
}
#[cfg(test)]
mod test {
use std::sync::Arc;
use super::*;
#[tokio::test]
async fn test_once_cell() {
let cell: OnceCell<i32> = OnceCell::new();
assert_eq!(cell.get(), None);
let v = cell.get_or_init(async { 0 }).await;
assert_eq!(v, &0);
assert_eq!(cell.get(), Some(&0));
}
#[tokio::test]
async fn test_once_cell_across_threads() {
let cell: Arc<OnceCell<i32>> = Arc::new(OnceCell::new());
let cell_clone1 = cell.clone();
let handler = tokio::spawn(async move {
cell_clone1.get_or_init(async { 0 }).await;
});
assert!(handler.await.is_ok());
assert_eq!(cell.get(), Some(&0));
}
#[tokio::test]
async fn test_lazy() {
let lazy = Lazy::new(async { 0 });
assert_eq!(lazy.get().await, &0);
assert_eq!(lazy.get().await, &0);
}
#[tokio::test]
async fn test_lazy_multi_threaded() {
let t = 5;
let lazy = Arc::new(Lazy::new(async move { t }));
let lazy_clone = lazy.clone();
let handle = tokio::spawn(async move {
assert_eq!(lazy_clone.get().await, &t);
});
assert!(handle.await.is_ok());
assert_eq!(lazy.get().await, &t);
}
#[tokio::test]
async fn test_lazy_struct() {
struct Test {
lazy: Lazy<i32>,
}
let data = Test {
lazy: Lazy::new(async { 0 }),
};
assert_eq!(data.lazy.get().await, &0);
}
}