use std::panic::{AssertUnwindSafe, catch_unwind};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{Sender, channel};
use std::thread::JoinHandle;
pub type Job = Box<dyn FnOnce() + Send + 'static>;
#[derive(Debug)]
pub struct Pool {
senders: Vec<Sender<Job>>,
next: AtomicUsize,
workers: Vec<JoinHandle<()>>,
}
impl Pool {
pub fn new(threads: usize) -> std::io::Result<Self> {
let threads = threads.max(1);
let mut senders = Vec::with_capacity(threads);
let mut workers = Vec::with_capacity(threads);
for index in 0..threads {
let (sender, receiver) = channel::<Job>();
let worker = std::thread::Builder::new()
.name(format!("f-pool-{index:02}"))
.spawn(move || {
while let Ok(job) = receiver.recv() {
let _ = catch_unwind(AssertUnwindSafe(job));
}
})?;
senders.push(sender);
workers.push(worker);
}
Ok(Self {
senders,
next: AtomicUsize::new(0),
workers,
})
}
pub fn submit(&self, job: Job) {
let index = self.next.fetch_add(1, Ordering::Relaxed) % self.senders.len();
let _ = self.senders[index].send(job);
}
}
impl Drop for Pool {
fn drop(&mut self) {
self.senders.clear();
for worker in self.workers.drain(..) {
let _ = worker.join();
}
}
}