use crate::{
queue::Queue,
scope::{Scope, ScopeGuard},
task_batch::TaskBatch,
worker::spawn_worker,
};
use std::{
num::NonZeroUsize,
sync::Arc,
thread::{self, JoinHandle},
};
pub struct ZeroPool {
queue: Arc<Queue>,
workers: Box<[JoinHandle<()>]>,
}
impl ZeroPool {
#[must_use]
pub fn new() -> Self {
let worker_count = thread::available_parallelism().unwrap_or(NonZeroUsize::MIN);
Self::with_workers(worker_count)
}
#[must_use]
pub fn with_workers(worker_count: NonZeroUsize) -> Self {
let worker_count = worker_count.get();
let queue = Arc::new(Queue::new(worker_count));
let workers = (0..worker_count)
.map(|id| {
let handle = spawn_worker(id, queue.clone());
queue.register_thread(id, handle.thread().clone());
handle
})
.collect();
ZeroPool { queue, workers }
}
#[inline]
pub fn scope<'env, F, R>(&'env self, f: F) -> R
where
F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> R,
{
let scope = Scope::new(&self.queue);
let _guard = ScopeGuard(&scope);
f(&scope)
}
#[inline]
pub fn run<T>(&self, task_fn: fn(&T), params: &[T]) {
self.scope(|s| s.run(task_fn, params));
}
#[inline]
pub unsafe fn run_detached<T>(&self, task_fn: fn(&T), params: *const [T]) {
if !params.is_empty() {
let batch = unsafe { TaskBatch::new(task_fn, params, None) };
self.queue.enqueue(batch, params.len());
}
}
}
impl Default for ZeroPool {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Drop for ZeroPool {
fn drop(&mut self) {
self.queue.shutdown();
for handle in std::mem::take(&mut self.workers) {
let _ = handle.join();
}
}
}