use std::{fmt::Display, sync::Arc, time::Duration};
use crossbeam_channel::bounded;
use crate::{task::TaskListeners, RejectedTaskHandler, ThreadPool};
#[derive(Debug)]
pub struct TPBuilderError {
msg: String,
}
impl std::error::Error for TPBuilderError {}
impl Display for TPBuilderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.msg)
}
}
macro_rules! tp_builder_error {
($($arg:tt)*) => {
TPBuilderError { msg: format!($($arg)*) }
};
}
pub struct ThreadPoolBuilder {
channel_capacity: usize,
max_pool_size: usize,
core_pool_size: usize,
keep_alive_time: Duration,
rejected_task_handler: RejectedTaskHandler,
task_lisenters: TaskListeners,
}
impl Default for ThreadPoolBuilder {
fn default() -> Self {
Self {
channel_capacity: 50,
max_pool_size: num_cpus::get_physical(),
core_pool_size: usize::max(1, num_cpus::get_physical() / 2),
keep_alive_time: Duration::from_secs(1),
rejected_task_handler: RejectedTaskHandler::Abort,
task_lisenters: TaskListeners {
before_execute: Box::new(|_| {}),
after_execute: Box::new(|_| {}),
},
}
}
}
impl ThreadPoolBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn set_channel_capacity(mut self, capacity: usize) -> Self {
self.channel_capacity = capacity;
self
}
#[must_use]
pub fn set_max_pool_size(mut self, size: usize) -> Self {
self.max_pool_size = size;
self
}
#[must_use]
pub fn set_core_pool_size(mut self, size: usize) -> Self {
self.core_pool_size = size;
self
}
#[must_use]
pub fn set_keep_alive_time(mut self, time_ms: Duration) -> Self {
self.keep_alive_time = time_ms;
self
}
#[must_use]
pub fn set_rejected_handler(mut self, handler: RejectedTaskHandler) -> Self {
self.rejected_task_handler = handler;
self
}
#[must_use]
pub fn set_lisenter_before_execute<F>(mut self, executor: F) -> Self
where
F: Fn(usize) + Send + Sync + 'static,
{
self.task_lisenters.before_execute = Box::new(executor);
self
}
#[must_use]
pub fn set_lisenter_after_execute<F>(mut self, executor: F) -> Self
where
F: Fn(usize) + Send + Sync + 'static,
{
self.task_lisenters.after_execute = Box::new(executor);
self
}
pub fn build(self) -> Result<ThreadPool, TPBuilderError> {
self.check_arguments()?;
let (sender, reciver) = bounded(self.channel_capacity);
Ok(ThreadPool {
sender: Some(sender),
reciver,
task_lisenters: Arc::new(self.task_lisenters),
core_workers: Vec::with_capacity(self.core_pool_size),
workers: Vec::with_capacity(self.max_pool_size - self.core_pool_size),
core_pool_size: self.core_pool_size,
max_pool_size: self.max_pool_size,
keep_alive_time: self.keep_alive_time,
rejected_task_handler: self.rejected_task_handler,
next_task_id: 0,
})
}
fn check_arguments(&self) -> Result<(), TPBuilderError> {
if self.channel_capacity < self.max_pool_size {
return Err(tp_builder_error!(
"Invalid arguments: max_tasks({}) < max_pool_size({}).",
self.channel_capacity,
self.max_pool_size
));
}
if self.max_pool_size < self.core_pool_size {
return Err(tp_builder_error!(
"Invalid arguments: max_pool_size({}) < core_pool_size({}).",
self.max_pool_size,
self.core_pool_size
));
}
Ok(())
}
}