use std::{
any::Any,
cell::Cell,
future::{Future, poll_fn},
panic::{AssertUnwindSafe, catch_unwind},
sync::{
Arc, Barrier,
atomic::{AtomicBool, AtomicUsize, Ordering},
},
task::Poll,
thread,
};
use futures::executor::block_on;
use ntest::timeout;
use super::{counting_waker, handoff, linger, poll_once, poll_with, ready};
use crate::{lock::OnceCell, tests::yield_now};
#[test]
fn get_finds_no_value_until_one_is_set() {
let cell = OnceCell::new();
assert_eq!(cell.get(), None);
assert_eq!(ready(cell.set(1)), Ok(&1));
assert_eq!(cell.get(), Some(&1));
}
#[test]
fn get_or_init_runs_its_closure_once_and_hands_back_the_same_value() {
let cell = OnceCell::new();
let runs = Cell::new(0);
let first = ready(cell.get_or_init(|| {
runs.set(runs.get() + 1);
async { 1 }
}));
let second = ready(cell.get_or_init(|| {
runs.set(runs.get() + 1);
async { 2 }
}));
assert_eq!((first, second), (&1, &1));
assert!(std::ptr::eq(first, second));
assert_eq!(runs.get(), 1);
}
#[test]
fn a_concurrent_initialiser_waits_for_the_first_and_gets_its_value() {
let cell = OnceCell::new();
let gate = Gate::default();
let second_runs = Cell::new(0);
let mut first = Box::pin(cell.get_or_init(|| gate.hold(1)));
let mut second = Box::pin(cell.get_or_init(|| {
second_runs.set(second_runs.get() + 1);
gate.hold(2)
}));
assert!(poll_once(&mut first).is_pending());
assert!(poll_once(&mut second).is_pending());
assert_eq!(cell.get(), None);
gate.open();
assert_eq!(poll_once(&mut first), Poll::Ready(&1));
assert_eq!(poll_once(&mut second), Poll::Ready(&1));
assert_eq!(second_runs.get(), 0);
}
#[test]
fn a_failing_initialiser_leaves_the_cell_empty_and_the_next_one_runs() {
let cell = OnceCell::<u8>::new();
let failed = ready(cell.get_or_try_init(|| async { Err("failed") }));
assert_eq!(failed, Err("failed"));
assert_eq!(cell.get(), None);
let made = ready(cell.get_or_try_init(|| async { Ok::<_, &str>(1) }));
assert_eq!(made, Ok(&1));
assert_eq!(cell.get(), Some(&1));
}
#[test]
fn an_initialiser_waiting_behind_a_failing_one_runs_its_own_closure() {
let cell = OnceCell::new();
let gate = Gate::default();
let mut failing = Box::pin(cell.get_or_try_init(|| gate.hold(Err("failed"))));
let mut waiting = Box::pin(cell.get_or_init(|| gate.hold(2)));
assert!(poll_once(&mut failing).is_pending());
assert!(poll_once(&mut waiting).is_pending());
gate.open();
assert_eq!(poll_once(&mut failing), Poll::Ready(Err("failed")));
assert_eq!(cell.get(), None);
assert_eq!(poll_once(&mut waiting), Poll::Ready(&2));
}
#[test]
fn a_cancelled_initialiser_lets_the_next_waiting_initialiser_run() {
let cell = OnceCell::new();
let (woken, waker) = counting_waker();
let gate = Gate::default();
let mut cancelled = Box::pin(cell.get_or_init(|| gate.hold(1)));
let mut waiting = Box::pin(cell.get_or_init(|| async { 2 }));
assert!(poll_once(&mut cancelled).is_pending());
assert!(poll_with(&mut waiting, &waker).is_pending());
drop(cancelled);
assert_eq!(woken.load(Ordering::SeqCst), 1);
assert_eq!(cell.get(), None);
assert_eq!(poll_once(&mut waiting), Poll::Ready(&2));
}
#[test]
fn a_panic_in_the_initialiser_leaves_the_cell_empty_and_usable() {
let cell = OnceCell::<u8>::new();
let panicked = catch_unwind(AssertUnwindSafe(|| {
ready(cell.get_or_init(|| -> std::future::Ready<u8> { panic!("the closure panicked") }))
}));
assert_eq!(message(panicked), "the closure panicked");
assert_eq!(cell.get(), None);
let panicked = catch_unwind(AssertUnwindSafe(|| {
ready(cell.get_or_init(panics_in_the_future))
}));
assert_eq!(message(panicked), "the future panicked");
assert_eq!(cell.get(), None);
assert_eq!(ready(cell.get_or_init(|| async { 3 })), &3);
}
#[test]
fn wait_completes_once_another_task_sets_the_value() {
let cell = OnceCell::new();
let mut waiting = Box::pin(cell.wait());
assert!(poll_once(&mut waiting).is_pending());
assert_eq!(ready(cell.set(4)), Ok(&4));
assert_eq!(poll_once(&mut waiting), Poll::Ready(&4));
assert_eq!(ready(cell.wait()), &4);
}
#[test]
fn wait_keeps_waiting_through_initialisers_that_fail_or_are_cancelled() {
let cell = OnceCell::new();
let gate = Gate::default();
let mut waiting = Box::pin(cell.wait());
assert!(poll_once(&mut waiting).is_pending());
let mut cancelled = Box::pin(cell.get_or_init(|| gate.hold(1)));
assert!(poll_once(&mut cancelled).is_pending());
drop(cancelled);
assert!(poll_once(&mut waiting).is_pending());
assert_eq!(cell.get(), None);
let failed = ready(cell.get_or_try_init(|| async { Err("failed") }));
assert_eq!(failed, Err("failed"));
assert!(poll_once(&mut waiting).is_pending());
assert_eq!(ready(cell.get_or_init(|| async { 5 })), &5);
assert_eq!(poll_once(&mut waiting), Poll::Ready(&5));
}
#[test]
fn set_succeeds_on_an_empty_cell_and_fails_on_a_set_one() {
let cell = OnceCell::new();
assert_eq!(ready(cell.set(1)), Ok(&1));
assert_eq!(ready(cell.set(2)), Err(2));
assert_eq!(cell.get(), Some(&1));
}
#[test]
fn set_waits_for_an_initialiser_in_progress_and_fails_when_it_succeeds() {
let cell = OnceCell::new();
let gate = Gate::default();
let mut initialising = Box::pin(cell.get_or_init(|| gate.hold(1)));
let mut setting = Box::pin(cell.set(9));
assert!(poll_once(&mut initialising).is_pending());
assert!(poll_once(&mut setting).is_pending());
gate.open();
assert_eq!(poll_once(&mut initialising), Poll::Ready(&1));
assert_eq!(poll_once(&mut setting), Poll::Ready(Err(9)));
assert_eq!(cell.get(), Some(&1));
}
#[test]
fn set_waits_for_an_initialiser_in_progress_and_succeeds_when_it_fails() {
let cell = OnceCell::new();
let gate = Gate::default();
let mut initialising = Box::pin(cell.get_or_try_init(|| gate.hold(Err("failed"))));
let mut setting = Box::pin(cell.set(9));
assert!(poll_once(&mut initialising).is_pending());
assert!(poll_once(&mut setting).is_pending());
gate.open();
assert_eq!(poll_once(&mut initialising), Poll::Ready(Err("failed")));
assert_eq!(poll_once(&mut setting), Poll::Ready(Ok(&9)));
assert_eq!(cell.get(), Some(&9));
}
#[test]
fn a_cancelled_set_leaves_the_initialiser_alone() {
let cell = OnceCell::new();
let gate = Gate::default();
let mut initialising = Box::pin(cell.get_or_init(|| gate.hold(1)));
let mut setting = Box::pin(cell.set(9));
assert!(poll_once(&mut initialising).is_pending());
assert!(poll_once(&mut setting).is_pending());
drop(setting);
gate.open();
assert_eq!(poll_once(&mut initialising), Poll::Ready(&1));
}
#[test]
fn setting_the_value_wakes_every_task_that_waits_for_it() {
let cell = OnceCell::new();
let (first_woken, first_waker) = counting_waker();
let (second_woken, second_waker) = counting_waker();
let mut first = Box::pin(cell.wait());
let mut second = Box::pin(cell.wait());
assert!(poll_with(&mut first, &first_waker).is_pending());
assert!(poll_with(&mut second, &second_waker).is_pending());
assert_eq!(first_woken.load(Ordering::SeqCst), 0);
assert_eq!(second_woken.load(Ordering::SeqCst), 0);
assert_eq!(ready(cell.set(1)), Ok(&1));
assert_eq!(first_woken.load(Ordering::SeqCst), 1);
assert_eq!(second_woken.load(Ordering::SeqCst), 1);
assert_eq!(poll_with(&mut first, &first_waker), Poll::Ready(&1));
assert_eq!(poll_with(&mut second, &second_waker), Poll::Ready(&1));
}
#[test]
fn initialising_the_cell_wakes_every_task_that_waits_for_it() {
let cell = OnceCell::new();
let (first_woken, first_waker) = counting_waker();
let (second_woken, second_waker) = counting_waker();
let mut first = Box::pin(cell.wait());
let mut second = Box::pin(cell.wait());
assert!(poll_with(&mut first, &first_waker).is_pending());
assert!(poll_with(&mut second, &second_waker).is_pending());
assert_eq!(ready(cell.get_or_init(|| async { 1 })), &1);
assert_eq!(first_woken.load(Ordering::SeqCst), 1);
assert_eq!(second_woken.load(Ordering::SeqCst), 1);
assert_eq!(poll_with(&mut first, &first_waker), Poll::Ready(&1));
assert_eq!(poll_with(&mut second, &second_waker), Poll::Ready(&1));
}
#[test]
fn initialising_the_cell_wakes_every_task_that_waits_to_initialise_it() {
let cell = OnceCell::new();
let gate = Gate::default();
let runs = Cell::new(0);
let (first_woken, first_waker) = counting_waker();
let (second_woken, second_waker) = counting_waker();
let mut initialising = Box::pin(cell.get_or_init(|| gate.hold(1)));
let mut first = Box::pin(cell.get_or_init(|| {
runs.set(runs.get() + 1);
async { 2 }
}));
let mut second = Box::pin(cell.get_or_init(|| {
runs.set(runs.get() + 1);
async { 3 }
}));
assert!(poll_once(&mut initialising).is_pending());
assert!(poll_with(&mut first, &first_waker).is_pending());
assert!(poll_with(&mut second, &second_waker).is_pending());
assert_eq!(first_woken.load(Ordering::SeqCst), 0);
assert_eq!(second_woken.load(Ordering::SeqCst), 0);
gate.open();
assert_eq!(poll_once(&mut initialising), Poll::Ready(&1));
assert_ne!(first_woken.load(Ordering::SeqCst), 0);
assert_ne!(second_woken.load(Ordering::SeqCst), 0);
assert_eq!(poll_with(&mut second, &second_waker), Poll::Ready(&1));
assert_eq!(poll_with(&mut first, &first_waker), Poll::Ready(&1));
assert_eq!(runs.get(), 0);
}
#[test]
fn initialising_the_cell_wakes_a_set_that_waits_behind_an_initialiser() {
let cell = OnceCell::new();
let gate = Gate::default();
let runs = Cell::new(0);
let (setting_woken, setting_waker) = counting_waker();
let mut initialising = Box::pin(cell.get_or_init(|| gate.hold(1)));
let mut queued = Box::pin(cell.get_or_init(|| {
runs.set(runs.get() + 1);
async { 2 }
}));
let mut setting = Box::pin(cell.set(3));
assert!(poll_once(&mut initialising).is_pending());
assert!(poll_once(&mut queued).is_pending());
assert!(poll_with(&mut setting, &setting_waker).is_pending());
assert_eq!(setting_woken.load(Ordering::SeqCst), 0);
gate.open();
assert_eq!(poll_once(&mut initialising), Poll::Ready(&1));
assert_ne!(setting_woken.load(Ordering::SeqCst), 0);
assert_eq!(poll_with(&mut setting, &setting_waker), Poll::Ready(Err(3)));
assert_eq!(poll_once(&mut queued), Poll::Ready(&1));
assert_eq!(runs.get(), 0);
}
#[test]
fn get_mut_take_and_into_inner_reach_the_value_without_a_wait() {
let mut cell = OnceCell::new();
assert_eq!(cell.get_mut(), None);
assert_eq!(cell.take(), None);
assert_eq!(ready(cell.set(vec![1])), Ok(&vec![1]));
cell.get_mut().expect("the cell has a value").push(2);
assert_eq!(cell.get(), Some(&vec![1, 2]));
assert_eq!(cell.take(), Some(vec![1, 2]));
assert_eq!(cell.get(), None);
assert_eq!(ready(cell.set(vec![3])), Ok(&vec![3]));
assert_eq!(cell.into_inner(), Some(vec![3]));
assert_eq!(OnceCell::<u8>::new().into_inner(), None);
}
#[test]
fn a_cell_is_made_from_a_default_or_from_a_value() {
struct NotDefault;
assert!(OnceCell::<NotDefault>::default().get().is_none());
let cell = OnceCell::from(7);
assert_eq!(cell.get(), Some(&7));
assert_eq!(ready(cell.set(8)), Err(8));
assert_eq!(ready(cell.wait()), &7);
}
#[test]
fn a_cell_prints_its_value_or_a_placeholder() {
let cell = OnceCell::new();
assert_eq!(format!("{cell:?}"), "OnceCell(<uninit>)");
assert_eq!(ready(cell.set(1)), Ok(&1));
assert_eq!(format!("{cell:?}"), "OnceCell(1)");
assert_eq!(format!("{cell:#?}"), "OnceCell(\n 1,\n)");
}
#[test]
#[timeout(15000)]
fn threads_initialising_at_once_run_one_initialiser_and_share_its_value() {
let (threads, rounds) = if cfg!(miri) { (3, 5) } else { (6, 300) };
let cells: Arc<Vec<_>> = Arc::new((0..rounds).map(|_| OnceCell::new()).collect());
let runs: Arc<Vec<_>> = Arc::new((0..rounds).map(|_| AtomicUsize::new(0)).collect());
let start = Arc::new(Barrier::new(threads));
let workers: Vec<_> = (0..threads)
.map(|index| {
let cells = cells.clone();
let runs = runs.clone();
let start = start.clone();
thread::spawn(move || {
(0..rounds)
.map(|round| {
start.wait();
*block_on(cells[round].get_or_init(|| async {
runs[round].fetch_add(1, Ordering::SeqCst);
yield_now().await;
index
}))
})
.collect::<Vec<usize>>()
})
})
.collect();
let seen: Vec<_> = workers
.into_iter()
.map(|worker| worker.join().unwrap())
.collect();
for (round, runs) in runs.iter().enumerate() {
assert_eq!(runs.load(Ordering::SeqCst), 1);
let value = seen[0][round];
assert!(value < threads);
assert!(seen.iter().all(|seen| seen[round] == value));
}
}
#[test]
#[timeout(15000)]
fn threads_waiting_for_a_value_are_all_released_by_one_initialiser() {
let (waiters, rounds) = if cfg!(miri) { (3, 5) } else { (6, 300) };
let cells: Arc<Vec<_>> = Arc::new((0..rounds).map(|_| OnceCell::new()).collect());
let start = Arc::new(Barrier::new(waiters + 1));
let waiter_threads: Vec<_> = (0..waiters)
.map(|_| {
let cells = cells.clone();
let start = start.clone();
thread::spawn(move || {
(0..rounds)
.map(|round| {
start.wait();
*block_on(cells[round].wait())
})
.collect::<Vec<usize>>()
})
})
.collect();
for round in 0..rounds {
start.wait();
for _ in 0..round % 64 {
linger();
}
assert_eq!(
*block_on(cells[round].get_or_init(|| async { round + 1 })),
round + 1
);
}
for waiter in waiter_threads {
let seen = waiter.join().unwrap();
assert!(
seen.iter()
.enumerate()
.all(|(round, &value)| value == round + 1)
);
}
}
#[test]
#[timeout(15000)]
fn a_value_set_racing_a_wait_is_not_missed() {
let rounds = if cfg!(miri) { 20 } else { 20_000 };
handoff(
Arc::new(Race {
cells: (0..rounds).map(|_| OnceCell::new()).collect(),
set: AtomicUsize::new(0),
waited: AtomicUsize::new(0),
}),
rounds,
|race, start_waiting| {
let round = race.set.fetch_add(1, Ordering::SeqCst);
start_waiting();
assert_eq!(block_on(race.cells[round].set(round)), Ok(&round));
},
|race| {
let round = race.waited.fetch_add(1, Ordering::SeqCst);
assert_eq!(*block_on(race.cells[round].wait()), round);
},
);
}
fn message(panicked: Result<&u8, Box<dyn Any + Send>>) -> &'static str {
let Err(payload) = panicked else {
panic!("the call was to panic");
};
payload
.downcast_ref::<&'static str>()
.expect("the panic carried a string literal")
}
async fn panics_in_the_future() -> u8 {
panic!("the future panicked");
}
#[derive(Default)]
struct Gate {
open: AtomicBool,
}
impl Gate {
fn hold<T>(&self, value: T) -> impl Future<Output = T> {
let mut value = Some(value);
poll_fn(move |_| {
if !self.open.load(Ordering::SeqCst) {
return Poll::Pending;
}
Poll::Ready(
value
.take()
.expect("the future was polled after it completed"),
)
})
}
fn open(&self) {
self.open.store(true, Ordering::SeqCst);
}
}
struct Race {
cells: Vec<OnceCell<usize>>,
set: AtomicUsize,
waited: AtomicUsize,
}