1use std::sync::mpsc::{self, Receiver, Sender};
12use std::sync::{Arc, Mutex};
13use std::thread::{self, JoinHandle};
14use std::time::{Duration, Instant};
15
16use crate::error::{IoError, Result};
17
18#[derive(Debug, Clone)]
20pub struct ThreadPoolConfig {
21 pub io_threads: usize,
23 pub cpu_threads: usize,
25 pub max_queue_size: usize,
27 pub keep_alive: Duration,
29 pub work_stealing: bool,
31}
32
33impl Default for ThreadPoolConfig {
34 fn default() -> Self {
35 let available_cores = thread::available_parallelism()
36 .map(|n| n.get())
37 .unwrap_or(4);
38
39 Self {
40 io_threads: available_cores / 2,
41 cpu_threads: available_cores / 2,
42 max_queue_size: 1000,
43 keep_alive: Duration::from_secs(60),
44 work_stealing: true,
45 }
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq)]
51pub enum WorkType {
52 IO,
54 CPU,
56}
57
58pub struct WorkItem {
60 pub work_type: WorkType,
62 pub task: Box<dyn FnOnce() -> Result<()> + Send>,
64 pub task_id: Option<u64>,
66}
67
68#[derive(Debug, Clone, Default)]
70pub struct ThreadPoolStats {
71 pub tasks_submitted: u64,
73 pub tasks_completed: u64,
75 pub tasks_failed: u64,
77 pub total_execution_time_ms: f64,
79 pub avg_execution_time_ms: f64,
81 pub current_queue_size: usize,
83 pub max_queue_size_reached: usize,
85 pub active_threads: usize,
87}
88
89pub struct ThreadPool {
91 io_workers: Vec<Worker>,
93 cpu_workers: Vec<Worker>,
95 io_sender: Sender<WorkItem>,
97 cpu_sender: Sender<WorkItem>,
99 #[allow(dead_code)]
101 config: ThreadPoolConfig,
102 stats: Arc<Mutex<ThreadPoolStats>>,
104 shutdown: Arc<Mutex<bool>>,
106}
107
108struct Worker {
110 #[allow(dead_code)]
111 id: usize,
112 thread: Option<JoinHandle<()>>,
113}
114
115impl ThreadPool {
116 pub fn new(config: ThreadPoolConfig) -> Self {
118 let (io_sender, io_receiver) = mpsc::channel();
119 let (cpu_sender, cpu_receiver) = mpsc::channel();
120
121 let stats = Arc::new(Mutex::new(ThreadPoolStats::default()));
122 let shutdown = Arc::new(Mutex::new(false));
123
124 let io_receiver = Arc::new(Mutex::new(io_receiver));
126 let mut io_workers = Vec::with_capacity(config.io_threads);
127
128 for id in 0..config.io_threads {
129 let receiver = Arc::clone(&io_receiver);
130 let stats_clone = Arc::clone(&stats);
131 let shutdown_clone = Arc::clone(&shutdown);
132
133 let thread = thread::spawn(move || {
134 Self::worker_loop(id, receiver, stats_clone, shutdown_clone, WorkType::IO)
135 });
136
137 io_workers.push(Worker {
138 id,
139 thread: Some(thread),
140 });
141 }
142
143 let cpu_receiver = Arc::new(Mutex::new(cpu_receiver));
145 let mut cpu_workers = Vec::with_capacity(config.cpu_threads);
146
147 for id in 0..config.cpu_threads {
148 let receiver = Arc::clone(&cpu_receiver);
149 let stats_clone = Arc::clone(&stats);
150 let shutdown_clone = Arc::clone(&shutdown);
151
152 let thread = thread::spawn(move || {
153 Self::worker_loop(id, receiver, stats_clone, shutdown_clone, WorkType::CPU)
154 });
155
156 cpu_workers.push(Worker {
157 id,
158 thread: Some(thread),
159 });
160 }
161
162 Self {
163 io_workers,
164 cpu_workers,
165 io_sender,
166 cpu_sender,
167 config,
168 stats,
169 shutdown,
170 }
171 }
172
173 pub fn submit<F>(&self, worktype: WorkType, task: F) -> Result<()>
175 where
176 F: FnOnce() -> Result<()> + Send + 'static,
177 {
178 let work_item = WorkItem {
179 work_type: worktype,
180 task: Box::new(task),
181 task_id: None,
182 };
183
184 {
186 let mut stats = self.stats.lock().expect("Operation failed");
187 stats.tasks_submitted += 1;
188 }
189
190 match worktype {
192 WorkType::IO => {
193 self.io_sender.send(work_item).map_err(|_| {
194 IoError::Other("Failed to submit I/O task: thread pool shut down".to_string())
195 })?;
196 }
197 WorkType::CPU => {
198 self.cpu_sender.send(work_item).map_err(|_| {
199 IoError::Other("Failed to submit CPU task: thread pool shut down".to_string())
200 })?;
201 }
202 }
203
204 Ok(())
205 }
206
207 pub fn submit_batch<F>(&self, worktype: WorkType, tasks: Vec<F>) -> Result<()>
209 where
210 F: FnOnce() -> Result<()> + Send + 'static,
211 {
212 for task in tasks {
213 self.submit(worktype, task)?;
214 }
215 Ok(())
216 }
217
218 pub fn parallel_map<T, F, R>(
220 &self,
221 items: Vec<T>,
222 _work_type: WorkType,
223 func: F,
224 ) -> Result<Vec<R>>
225 where
226 T: Send + 'static,
227 F: Fn(T) -> R + Send + Sync + 'static,
228 R: Send + 'static + std::fmt::Debug,
229 {
230 use std::sync::mpsc;
231
232 let func = Arc::new(func);
233 let (sender, receiver) = mpsc::channel();
234 let mut handles = Vec::new();
235 let num_items = items.len();
236
237 for (index, item) in items.into_iter().enumerate() {
238 let func_clone = Arc::clone(&func);
239 let sender_clone = sender.clone();
240
241 let handle = thread::spawn(move || {
242 let result = func_clone(item);
243 let _ = sender_clone.send((index, result));
244 });
245
246 handles.push(handle);
247 }
248
249 drop(sender);
251
252 let mut results: Vec<Option<R>> = (0..num_items).map(|_| None).collect();
254 for _ in 0..num_items {
255 match receiver.recv() {
256 Ok((index, result)) => {
257 results[index] = Some(result);
258 }
259 Err(_) => {
260 return Err(IoError::Other(
261 "Failed to receive result from worker thread".to_string(),
262 ))
263 }
264 }
265 }
266
267 for handle in handles {
269 handle
270 .join()
271 .map_err(|_| IoError::Other("Thread panicked".to_string()))?;
272 }
273
274 let final_results: Result<Vec<R>> = results
276 .into_iter()
277 .enumerate()
278 .map(|(i, opt)| {
279 opt.ok_or_else(|| IoError::Other(format!("Missing result for item {}", i)))
280 })
281 .collect();
282
283 final_results
284 }
285
286 pub fn get_stats(&self) -> ThreadPoolStats {
288 self.stats.lock().expect("Operation failed").clone()
289 }
290
291 pub fn pending_tasks(&self) -> usize {
293 0
296 }
297
298 pub fn wait_for_completion(&self) -> Result<()> {
300 thread::sleep(Duration::from_millis(100));
303 Ok(())
304 }
305
306 pub fn shutdown(mut self) -> Result<()> {
308 {
310 let mut shutdown = self.shutdown.lock().expect("Operation failed");
311 *shutdown = true;
312 }
313
314 drop(self.io_sender);
316 drop(self.cpu_sender);
317
318 for worker in &mut self.io_workers {
320 if let Some(thread) = worker.thread.take() {
321 thread
322 .join()
323 .map_err(|_| IoError::Other("Failed to join I/O worker thread".to_string()))?;
324 }
325 }
326
327 for worker in &mut self.cpu_workers {
329 if let Some(thread) = worker.thread.take() {
330 thread
331 .join()
332 .map_err(|_| IoError::Other("Failed to join CPU worker thread".to_string()))?;
333 }
334 }
335
336 Ok(())
337 }
338
339 fn worker_loop(
341 id: usize,
342 receiver: Arc<Mutex<Receiver<WorkItem>>>,
343 stats: Arc<Mutex<ThreadPoolStats>>,
344 shutdown: Arc<Mutex<bool>>,
345 worker_type: WorkType,
346 ) {
347 loop {
348 if *shutdown.lock().expect("Operation failed") {
350 break;
351 }
352
353 let work_item = {
355 let receiver = receiver.lock().expect("Operation failed");
356 receiver.recv_timeout(Duration::from_millis(100))
357 };
358
359 match work_item {
360 Ok(item) => {
361 let start_time = Instant::now();
362
363 let result = (item.task)();
365
366 let execution_time = start_time.elapsed().as_millis() as f64;
367
368 {
370 let mut stats_guard = stats.lock().expect("Operation failed");
371 match result {
372 Ok(_) => {
373 stats_guard.tasks_completed += 1;
374 }
375 Err(_) => {
376 stats_guard.tasks_failed += 1;
377 }
378 }
379 stats_guard.total_execution_time_ms += execution_time;
380
381 let total_tasks = stats_guard.tasks_completed + stats_guard.tasks_failed;
383 if total_tasks > 0 {
384 stats_guard.avg_execution_time_ms =
385 stats_guard.total_execution_time_ms / total_tasks as f64;
386 }
387 }
388 }
389 Err(mpsc::RecvTimeoutError::Timeout) => {
390 continue;
392 }
393 Err(mpsc::RecvTimeoutError::Disconnected) => {
394 break;
396 }
397 }
398 }
399
400 println!("Worker {id} ({worker_type:?}) shutting down");
401 }
402}
403
404static GLOBAL_THREAD_POOL: std::sync::OnceLock<ThreadPool> = std::sync::OnceLock::new();
406
407#[allow(dead_code)]
409pub fn init_global_thread_pool(config: ThreadPoolConfig) {
410 let _ = GLOBAL_THREAD_POOL.set(ThreadPool::new(config));
411}
412
413#[allow(dead_code)]
415pub fn global_thread_pool() -> &'static ThreadPool {
416 GLOBAL_THREAD_POOL.get_or_init(|| ThreadPool::new(ThreadPoolConfig::default()))
417}
418
419#[allow(dead_code)]
421pub fn execute<F>(work_type: WorkType, task: F) -> Result<()>
422where
423 F: FnOnce() -> Result<()> + Send + 'static,
424{
425 global_thread_pool().submit(work_type, task)
426}
427
428#[allow(dead_code)]
430pub fn optimal_config() -> ThreadPoolConfig {
431 let available_cores = thread::available_parallelism()
432 .map(|n| n.get())
433 .unwrap_or(4);
434
435 let io_threads = if available_cores <= 2 {
437 1
438 } else if available_cores <= 4 {
439 2
440 } else {
441 available_cores / 2
442 };
443
444 let cpu_threads = available_cores - io_threads;
445
446 ThreadPoolConfig {
447 io_threads,
448 cpu_threads,
449 max_queue_size: available_cores * 100, keep_alive: Duration::from_secs(30),
451 work_stealing: available_cores > 2,
452 }
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
458 use std::sync::atomic::{AtomicUsize, Ordering};
459
460 #[test]
461 fn test_thread_pool_creation() {
462 let config = ThreadPoolConfig::default();
463 let pool = ThreadPool::new(config.clone());
464
465 assert_eq!(pool.io_workers.len(), config.io_threads);
466 assert_eq!(pool.cpu_workers.len(), config.cpu_threads);
467 }
468
469 #[test]
470 fn test_task_submission() {
471 let pool = ThreadPool::new(ThreadPoolConfig::default());
472 let counter = Arc::new(AtomicUsize::new(0));
473 let counter_clone = Arc::clone(&counter);
474
475 let result = pool.submit(WorkType::CPU, move || {
476 counter_clone.fetch_add(1, Ordering::SeqCst);
477 Ok(())
478 });
479
480 assert!(result.is_ok());
481
482 thread::sleep(Duration::from_millis(100));
484
485 let stats = pool.get_stats();
486 assert!(stats.tasks_submitted > 0);
487 }
488
489 #[test]
490 fn test_batch_submission() {
491 let pool = ThreadPool::new(ThreadPoolConfig::default());
492 let counter = Arc::new(AtomicUsize::new(0));
493
494 let tasks: Vec<_> = (0..10)
495 .map(|_| {
496 let counter_clone = Arc::clone(&counter);
497 move || {
498 counter_clone.fetch_add(1, Ordering::SeqCst);
499 Ok(())
500 }
501 })
502 .collect();
503
504 let result = pool.submit_batch(WorkType::CPU, tasks);
505 assert!(result.is_ok());
506
507 thread::sleep(Duration::from_millis(200));
509
510 let stats = pool.get_stats();
511 assert_eq!(stats.tasks_submitted, 10);
512 }
513
514 #[test]
515 fn test_optimal_config() {
516 let config = optimal_config();
517 assert!(config.io_threads > 0);
518 assert!(config.cpu_threads > 0);
519 assert!(config.max_queue_size > 0);
520 }
521
522 #[test]
523 fn test_global_thread_pool() {
524 let result = execute(WorkType::CPU, || {
526 println!("Global thread pool test");
527 Ok(())
528 });
529
530 assert!(result.is_ok());
531 }
532}