use std::cell::{Cell, UnsafeCell};
use std::future::Future;
use std::marker::PhantomData;
use std::rc::Rc;
use super::Notify;
#[derive(Clone, Copy, Eq, PartialEq)]
enum State {
Empty,
Initializing,
Ready,
}
pub struct OnceCell<T> {
state: Cell<State>,
notify: Notify,
_not_send_sync: PhantomData<Rc<()>>,
value: UnsafeCell<Option<T>>,
}
impl<T> OnceCell<T> {
pub fn new() -> Self {
Self {
state: Cell::new(State::Empty),
notify: Notify::new(),
_not_send_sync: PhantomData,
value: UnsafeCell::new(None),
}
}
pub fn get(&self) -> Option<&T> {
if self.state.get() != State::Ready {
return None;
}
unsafe { (&*self.value.get()).as_ref() }
}
pub async fn get_or_init<F, Fut>(&self, f: F) -> &T
where
F: FnOnce() -> Fut,
Fut: Future<Output = T>,
{
let mut initializer = Some(f);
loop {
match self.state.get() {
State::Ready => return self.ready_value(),
State::Empty => {
self.state.set(State::Initializing);
let mut guard = InitGuard {
cell: self,
armed: true,
};
let f = initializer
.take()
.expect("initializer is available on the empty path");
let value = f().await;
unsafe {
*self.value.get() = Some(value);
}
self.state.set(State::Ready);
guard.armed = false;
self.notify.notify_waiters();
return self.ready_value();
}
State::Initializing => {
self.notify.notified().await;
}
}
}
}
fn ready_value(&self) -> &T {
self.get()
.expect("OnceCell ready state must contain an initialized value")
}
}
struct InitGuard<'a, T> {
cell: &'a OnceCell<T>,
armed: bool,
}
impl<T> Drop for InitGuard<'_, T> {
fn drop(&mut self) {
if self.armed {
self.cell.state.set(State::Empty);
self.cell.notify.notify_waiters();
}
}
}
impl<T> Default for OnceCell<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::{Cell, RefCell};
use crate::{run, spawn, yield_now};
#[test]
fn fast_path_initializes_and_returns_value() {
let cell = Rc::new(OnceCell::new());
let observed = Rc::new(Cell::new(0));
spawn({
let cell = Rc::clone(&cell);
let observed = Rc::clone(&observed);
async move {
let value = cell.get_or_init(|| async { 42 }).await;
observed.set(*value);
}
});
run();
assert_eq!(observed.get(), 42);
assert_eq!(cell.get(), Some(&42));
}
#[test]
fn racing_callers_run_one_initializer() {
let cell = Rc::new(OnceCell::new());
let init_count = Rc::new(Cell::new(0));
let observed = Rc::new(RefCell::new(Vec::new()));
for _ in 0..2 {
spawn({
let cell = Rc::clone(&cell);
let init_count = Rc::clone(&init_count);
let observed = Rc::clone(&observed);
async move {
let value = cell
.get_or_init(|| {
let init_count = Rc::clone(&init_count);
async move {
init_count.set(init_count.get() + 1);
yield_now().await;
99
}
})
.await;
observed.borrow_mut().push(*value);
}
});
}
run();
assert_eq!(init_count.get(), 1);
assert_eq!(&*observed.borrow(), &[99, 99]);
}
#[test]
fn cancelled_initializer_lets_next_caller_initialize() {
let cell = Rc::new(OnceCell::new());
let observed = Rc::new(Cell::new(0));
let initializing = spawn({
let cell = Rc::clone(&cell);
async move {
cell.get_or_init(|| async {
std::future::pending::<()>().await;
1
})
.await;
}
});
spawn({
let cell = Rc::clone(&cell);
let observed = Rc::clone(&observed);
async move {
yield_now().await;
initializing.abort();
let value = cell.get_or_init(|| async { 42 }).await;
observed.set(*value);
}
});
run();
assert_eq!(observed.get(), 42);
assert_eq!(cell.get(), Some(&42));
}
}