use std::sync::OnceLock;
pub fn get_or_compute<T>(cell: &OnceLock<T>, init: impl FnOnce() -> T) -> &T {
if let Some(value) = cell.get() {
return value;
}
let computed = init();
cell.get_or_init(|| computed)
}
#[cfg(test)]
mod tests {
use super::get_or_compute;
use rayon::prelude::*;
use std::sync::{OnceLock, mpsc};
use std::time::Duration;
#[test]
fn a_job_asking_for_a_value_its_own_worker_is_computing_gets_the_stored_value() {
let (sender, receiver) = mpsc::channel();
std::thread::spawn(move || {
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.expect("a thread pool");
let cell = OnceLock::new();
let values: Vec<usize> = pool.install(|| {
(0..2)
.into_par_iter()
.with_max_len(1)
.map(|_| {
let value: *const u32 = get_or_compute(&cell, || {
rayon::yield_now();
7_u32
});
value as usize
})
.collect()
});
sender.send((values, cell.get().copied())).expect("the test is waiting");
});
let (addresses, stored) = receiver
.recv_timeout(Duration::from_secs(30))
.expect("both jobs got the value");
assert_eq!(stored, Some(7));
assert_eq!(addresses[0], addresses[1]);
}
}