use std::{collections::VecDeque, thread};
pub trait Worker: Send + 'static {
fn exec(&mut self);
}
impl<T: Fn() + Send + Sync + 'static> Worker for T {
fn exec(&mut self) {
self()
}
}
pub struct ThreadPool {
pool: Vec<thread::JoinHandle<()>>,
queue: VecDeque<Box<dyn Worker>>,
pool_size: usize,
}
impl ThreadPool {
pub fn new(size: usize) -> Self {
Self {
pool: Vec::with_capacity(size),
queue: VecDeque::new(),
pool_size: size,
}
}
pub fn enqueue(&mut self, worker: impl Worker) {
self.queue.push_back(Box::new(worker));
}
pub fn update_threads(&mut self) -> bool {
for i in self
.pool
.iter()
.enumerate()
.filter(|(_, t)| t.is_finished())
.map(|(i, _)| i)
.collect::<Vec<_>>()
{
self.pool.swap_remove(i);
}
for _ in 0..self.queue.len() {
#[expect(
clippy::collapsible_if,
reason = "We don't want to take ownership of the worker until we know we can append a worker"
)]
if self.pool.len() < self.pool_size {
if let Some(mut w) = self.queue.pop_back() {
self.pool.push(thread::spawn(move || {
w.exec();
}));
}
}
}
self.pool.is_empty()
}
pub const fn capacity(&self) -> usize {
self.pool_size
}
pub fn queued_count(&self) -> usize {
self.queue.len()
}
pub const fn active_count(&self) -> usize {
self.pool.len()
}
pub fn is_finished(&self) -> bool {
self.queued_count() == 0 && self.active_count() == 0
}
pub fn run_all(&mut self) {
while !self.update_threads() {}
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::*;
#[test]
fn thread_pool() {
let mut pool = ThreadPool::new(8);
for i in 0..pool.capacity() {
let v = (i + 1) as u64;
pool.enqueue(move || {
thread::sleep(Duration::from_secs(v));
println!("Task finished! {v}")
});
}
while !pool.update_threads() {}
}
}