use std::collections::BTreeMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc;
use std::thread;
use super::scratch::Scratch;
pub(super) fn each<T: Send>(
scratches: &[Scratch],
n: usize,
work: impl Fn(&Scratch, usize) -> anyhow::Result<T> + Sync,
mut done: impl FnMut(T),
) -> anyhow::Result<()> {
let next = AtomicUsize::new(0);
thread::scope(|s| {
let (tx, rx) = mpsc::channel();
for scratch in scratches {
let (tx, next, work) = (tx.clone(), &next, &work);
s.spawn(move || {
loop {
let i = next.fetch_add(1, Ordering::SeqCst);
if i >= n {
break;
}
if tx.send((i, work(scratch, i))).is_err() {
break;
}
}
});
}
drop(tx);
let mut waiting = BTreeMap::new();
let mut reported = 0;
for (i, result) in rx {
waiting.insert(i, result?);
while let Some(t) = waiting.remove(&reported) {
done(t);
reported += 1;
}
}
Ok(())
})
}