use std::{
sync::{
Arc, Barrier,
atomic::{AtomicBool, AtomicU64, Ordering},
},
thread::{self, JoinHandle},
time::{Duration, Instant},
};
use crate::cpus::Cpus;
pub type Round = Box<dyn FnMut() + Send>;
pub struct Crew {
cpus: Cpus,
stop: Arc<AtomicBool>,
start: Arc<Barrier>,
end: Arc<Barrier>,
threads: Vec<JoinHandle<()>>,
}
impl Crew {
pub fn spawn(rounds: impl IntoIterator<Item = Round>) -> Self {
let rounds: Vec<_> = rounds.into_iter().collect();
let count = rounds.len();
let cpus = Cpus::allowed();
let stop = Arc::new(AtomicBool::new(false));
let start = Arc::new(Barrier::new(count + 1));
let end = Arc::new(Barrier::new(count + 1));
let arrivals = Arc::new(AtomicU64::new(0));
let threads = rounds
.into_iter()
.enumerate()
.map(|(index, mut round)| {
let cpu = cpus.nth(index);
let stop = stop.clone();
let start = start.clone();
let end = end.clone();
let arrivals = arrivals.clone();
thread::spawn(move || {
let _pinned = cpu.pin();
let mut started = 0;
loop {
start.wait();
if stop.load(Ordering::Acquire) {
return;
}
started += 1;
arrivals.fetch_add(1, Ordering::AcqRel);
while arrivals.load(Ordering::Acquire) < started * count as u64 {
thread::yield_now();
}
round();
end.wait();
}
})
})
.collect();
Self {
cpus,
stop,
start,
end,
threads,
}
}
pub fn time(&self, iters: u64) -> Duration {
let _pinned = self.cpus.nth(self.threads.len()).pin();
let mut total = Duration::ZERO;
for _ in 0..iters {
let started = Instant::now();
self.start.wait();
self.end.wait();
total += started.elapsed();
}
total
}
pub fn join(self) {
self.stop.store(true, Ordering::Release);
self.start.wait();
for thread in self.threads {
thread.join().unwrap();
}
}
}