zero_pool/pool.rs
1use crate::{
2 TaskFnPointer, TaskItem, TaskParamPointer, queue::Queue, task_future::TaskFuture,
3 uniform_tasks_to_pointers, worker::spawn_worker,
4};
5use std::{sync::Arc, thread::JoinHandle};
6
7pub struct ZeroPool {
8 queue: Arc<Queue>,
9 workers: Vec<JoinHandle<()>>,
10}
11
12impl ZeroPool {
13 /// Creates a new thread pool with worker count equal to available parallelism
14 ///
15 /// Worker count is determined by `std::thread::available_parallelism()`,
16 /// falling back to 1 if unavailable. This is usually the optimal choice
17 /// for CPU-bound workloads.
18 ///
19 /// # Examples
20 ///
21 /// ```rust
22 /// use zero_pool::ZeroPool;
23 /// let pool = ZeroPool::new();
24 /// ```
25 pub fn new() -> Self {
26 let worker_count = std::thread::available_parallelism()
27 .map(std::num::NonZeroUsize::get)
28 .unwrap_or(1);
29 Self::with_workers(worker_count)
30 }
31
32 /// Creates a new thread pool with the specified number of workers
33 ///
34 /// Use this when you need precise control over the worker count,
35 /// for example when coordinating with other thread pools or
36 /// when you know the optimal count for your specific workload.
37 ///
38 /// # Panics
39 ///
40 /// Panics if `worker_count` is 0.
41 ///
42 /// ```rust
43 /// use zero_pool::ZeroPool;
44 /// let pool = ZeroPool::with_workers(4);
45 /// ```
46 pub fn with_workers(worker_count: usize) -> Self {
47 assert!(worker_count > 0, "Must have at least one worker");
48
49 let queue = Arc::new(Queue::new());
50
51 let workers: Vec<JoinHandle<()>> = (0..worker_count)
52 .map(|id| spawn_worker(id, queue.clone()))
53 .collect();
54
55 ZeroPool { queue, workers }
56 }
57
58 /// Submit a single task using raw function and parameter pointers
59 ///
60 /// This is the lowest-level submission method with minimal overhead.
61 /// Most users should prefer the type-safe `submit_task` method.
62 ///
63 /// # Safety
64 ///
65 /// The parameter pointer must remain valid until the returned future completes.
66 /// The caller must ensure the pointer points to the correct parameter type
67 /// expected by the task function.
68 ///
69 /// # Examples
70 ///
71 /// ```rust
72 /// use zero_pool::{ZeroPool, TaskFnPointer, TaskParamPointer, zp_define_task_fn, zp_write};
73 ///
74 /// struct MyTaskParams { value: u64, result: *mut u64 }
75 ///
76 /// zp_define_task_fn!(my_task, MyTaskParams, |params| {
77 /// zp_write!(params.result, params.value * 2);
78 /// });
79 ///
80 /// let pool = ZeroPool::new();
81 /// let mut result = 0u64;
82 /// let task_params = MyTaskParams { value: 42, result: &mut result };
83 /// let future = pool.submit_raw_task(
84 /// my_task as TaskFnPointer,
85 /// &task_params as *const _ as TaskParamPointer
86 /// );
87 /// future.wait();
88 /// assert_eq!(result, 84);
89 /// ```
90 pub fn submit_raw_task(&self, task_fn: TaskFnPointer, params: TaskParamPointer) -> TaskFuture {
91 self.queue.push_single_task(task_fn, params)
92 }
93
94 /// Submit a batch of tasks using raw work items
95 ///
96 /// This method provides optimal performance for pre-converted task batches.
97 /// Use `uniform_tasks_to_pointers` to convert typed tasks efficiently,
98 /// or build work items manually for mixed task types.
99 ///
100 /// # Examples
101 ///
102 /// ```rust
103 /// use zero_pool::{ZeroPool, uniform_tasks_to_pointers, zp_define_task_fn, zp_write};
104 ///
105 /// struct MyTaskParams { value: u64, result: *mut u64 }
106 ///
107 /// zp_define_task_fn!(my_task, MyTaskParams, |params| {
108 /// zp_write!(params.result, params.value * 2);
109 /// });
110 ///
111 /// let pool = ZeroPool::new();
112 /// let mut results = vec![0u64; 2];
113 /// let task_params = vec![
114 /// MyTaskParams { value: 1, result: &mut results[0] },
115 /// MyTaskParams { value: 2, result: &mut results[1] }
116 /// ];
117 /// let tasks = uniform_tasks_to_pointers(my_task, &task_params);
118 /// let future = pool.submit_raw_task_batch(&tasks);
119 /// future.wait();
120 /// assert_eq!(results[0], 2);
121 /// assert_eq!(results[1], 4);
122 /// ```
123 pub fn submit_raw_task_batch(&self, tasks: &[TaskItem]) -> TaskFuture {
124 self.queue.push_task_batch(tasks)
125 }
126
127 /// Submit a single typed task with automatic pointer conversion
128 ///
129 /// This method provides type safety while maintaining performance.
130 /// The parameter struct must remain valid until the future completes.
131 /// This is the recommended method for submitting individual tasks.
132 ///
133 /// # Examples
134 ///
135 /// ```rust
136 /// use zero_pool::{ZeroPool, zp_define_task_fn, zp_write};
137 ///
138 /// struct MyTaskParams { value: u64, result: *mut u64 }
139 ///
140 /// zp_define_task_fn!(my_task_fn, MyTaskParams, |params| {
141 /// zp_write!(params.result, params.value * 2);
142 /// });
143 ///
144 /// let pool = ZeroPool::new();
145 /// let mut result = 0u64;
146 /// let task_params = MyTaskParams { value: 42, result: &mut result };
147 /// let future = pool.submit_task(my_task_fn, &task_params);
148 /// future.wait();
149 /// assert_eq!(result, 84);
150 /// ```
151 pub fn submit_task<T>(&self, task_fn: TaskFnPointer, params: &T) -> TaskFuture {
152 let params_ptr = params as *const T as TaskParamPointer;
153 self.submit_raw_task(task_fn, params_ptr)
154 }
155
156 /// Submit a batch of uniform tasks with automatic pointer conversion
157 ///
158 /// All tasks in the batch must be the same type and use the same task function.
159 /// This method handles the pointer conversion automatically and is the most
160 /// convenient way to submit large batches of similar work.
161 ///
162 /// # Examples
163 ///
164 /// ```rust
165 /// use zero_pool::{ZeroPool, zp_define_task_fn, zp_write};
166 ///
167 /// struct MyTaskParams { value: u64, result: *mut u64 }
168 ///
169 /// zp_define_task_fn!(my_task_fn, MyTaskParams, |params| {
170 /// zp_write!(params.result, params.value * 2);
171 /// });
172 ///
173 /// let pool = ZeroPool::new();
174 /// let mut results = vec![0u64; 1000];
175 /// let task_params: Vec<_> = (0..1000)
176 /// .map(|i| MyTaskParams { value: i as u64, result: &mut results[i] })
177 /// .collect();
178 /// let future = pool.submit_batch_uniform(my_task_fn, &task_params);
179 /// future.wait();
180 /// assert_eq!(results[0], 0);
181 /// assert_eq!(results[1], 2);
182 /// assert_eq!(results[999], 1998);
183 /// ```
184 pub fn submit_batch_uniform<T>(&self, task_fn: TaskFnPointer, params_vec: &[T]) -> TaskFuture {
185 let tasks = uniform_tasks_to_pointers(task_fn, params_vec);
186 self.submit_raw_task_batch(&tasks)
187 }
188}
189
190impl Drop for ZeroPool {
191 fn drop(&mut self) {
192 self.queue.shutdown();
193
194 let workers = std::mem::take(&mut self.workers);
195 for handle in workers {
196 let _ = handle.join();
197 }
198 }
199}