Skip to main content

orx_parallel/pools/
pool.rs

1#[cfg(any(feature = "std", feature = "rayon-core"))]
2use crate::NumThreads;
3#[cfg(feature = "std")]
4use crate::pools::pool_impl::{BasicPool, OncePool};
5use crate::pools::{DefaultPool, global_pool};
6
7/// Factory for creating thread pools with different characteristics.
8///
9/// `Pool` provides builder methods to create various types of thread pools that can be used
10/// for parallel computations. Each pool type has different properties regarding thread lifecycle
11/// and persistence.
12///
13/// > **Note:** `Pool` is a convenience factory for thread pools provided or adapted by this crate.
14/// > You can also implement [`ThreadPool`](crate::ThreadPool) yourself and pass it directly
15/// > to `.pool(...)` or to runner constructors that accept any thread pool implementing the trait.
16///
17/// # Thread Count Configuration
18///
19/// When creating a pool, the thread count is determined by combining:
20///
21/// 1. **Requested count** - Passed to factory methods
22/// 2. **Environment limit** - `ORX_NUM_THREADS` if set
23/// 3. **System availability** - Number of logical CPUs available
24///
25/// The pool will use the minimum of these constraints.
26///
27/// # Examples
28///
29/// ```ignore
30/// use orx_parallel::*;
31///
32/// // Create a OncePool with auto-detection (subject to ORX_NUM_THREADS)
33/// let pool = Pool::once(NumThreads::Auto);
34///
35/// // Create a OncePool capped at 4 threads
36/// let pool = Pool::once(4);  // Converted from usize via From impl
37///
38/// // Create a persistent BasicPool with 8 threads
39/// let pool = Pool::basic(8);
40///
41/// // Create a Rayon pool (requires rayon-core feature)
42/// let pool = Pool::rayon(NumThreads::Auto)?;
43/// ```
44///
45/// # Pool Types
46///
47/// - **OncePool** (with `transient-pool` feature) - Spawns threads only when needed, releases after computation
48/// - **BasicPool** (default) - Maintains persistent workers across multiple computations
49/// - **Rayon** - Uses the Rayon parallel runtime (external crate)
50///
51/// See the [`thread_usage.md`](https://github.com/orxfun/orx-parallel/blob/main/docs/thread_usage.md) documentation for complete details.
52pub struct Pool;
53
54impl Pool {
55    /// Returns the default global thread pool.
56    ///
57    /// This exposes the thread pool's functionality directly, allowing convenient
58    /// **ad-hoc** parallel computation, on top of the parallel iterators of this
59    /// crate. Note, however, that such ad-hoc parallelization does not benefit from
60    /// the input concurrent iterator and parallel runner strategy optimizations that
61    /// parallel iterators build on. Therefore, it is best suited for a handful of
62    /// large enough, independent tasks rather than for computations with numerous
63    /// small tasks.
64    ///
65    /// There are two ways to use it:
66    ///
67    /// * Manually via [`scope`] and [`run`]:
68    ///
69    /// ```rust
70    /// use orx_parallel::*;
71    ///
72    /// Pool::global().scope(|s| {
73    ///     s.run(|| println!("task A"));
74    ///     s.run(|| println!("task B"));
75    /// });
76    /// ```
77    ///
78    /// * Or via [`tasks!`] and [`run_all`], which builds a statically typed queue of
79    ///   tasks to be run in parallel:
80    ///
81    /// ```rust
82    /// use orx_parallel::*;
83    /// use std::sync::Mutex;
84    ///
85    /// let numbers = [4, 8, 15, 16, 23, 42];
86    ///
87    /// let sum = Mutex::new(0);
88    /// let max = Mutex::new(i32::MIN);
89    /// let all_positive = Mutex::new(false);
90    ///
91    /// let tasks = tasks![
92    ///     || *sum.lock().unwrap() = numbers.iter().sum(),
93    ///     || *max.lock().unwrap() = numbers.iter().copied().max().unwrap(),
94    ///     || *all_positive.lock().unwrap() = numbers.iter().all(|&x| x > 0),
95    /// ];
96    ///
97    /// Pool::global().run_all(tasks);
98    ///
99    /// println!(
100    ///     "sum={}, max={}, all_positive={}",
101    ///     sum.into_inner().unwrap(),
102    ///     max.into_inner().unwrap(),
103    ///     all_positive.into_inner().unwrap(),
104    /// );
105    /// ```
106    ///
107    /// [`scope`]: crate::ThreadPool::scope
108    /// [`run`]: crate::Scope::run
109    /// [`run_all`]: crate::ThreadPool::run_all
110    /// [`tasks!`]: crate::tasks
111    pub fn global() -> DefaultPool {
112        global_pool()
113    }
114
115    /// Creates a lightweight on-demand pool with the specified thread configuration.
116    ///
117    /// A `OncePool` is a lightweight virtual pool that spawns worker threads just before
118    /// a computation starts and releases them immediately after. This reduces overhead when
119    /// a persistent thread pool isn't needed.
120    ///
121    /// # Thread Count Decision
122    ///
123    /// The actual thread count is determined by:
124    /// - The `num_threads` parameter
125    /// - The `ORX_NUM_THREADS` environment variable (if set)
126    /// - The number of available system CPU cores
127    ///
128    /// The minimum of these constraints will be used.
129    ///
130    /// # Parameters
131    ///
132    /// - `num_threads` - Either:
133    ///   - `0` or `NumThreads::Auto` - Use all available threads (respecting constraints)
134    ///   - `n > 0` or `NumThreads::Max(n)` - Cap at `n` threads (respecting constraints)
135    ///
136    /// # Examples
137    ///
138    /// ```ignore
139    /// use orx_parallel::*;
140    ///
141    /// // Auto-detect threads
142    /// let pool = Pool::once(NumThreads::Auto);
143    ///
144    /// // Cap at 4 threads
145    /// let pool = Pool::once(4);
146    ///
147    /// // Same as above (usize converts via From impl)
148    /// let pool = Pool::once(NumThreads::Max(std::num::NonZeroUsize::new(4).unwrap()));
149    /// ```
150    ///
151    /// # Default Behavior
152    ///
153    /// This is available when the `transient-pool` feature is enabled.
154    /// Applications can explicitly create an `OncePool` to configure custom thread settings
155    /// for on-demand thread spawning and cleanup.
156    #[cfg(feature = "std")]
157    pub fn once(num_threads: impl Into<NumThreads>) -> OncePool {
158        OncePool::new(num_threads)
159    }
160
161    /// Creates a [`BasicPool`] with the specified thread configuration.
162    ///
163    /// A `BasicPool` maintains persistent worker threads that remain alive across
164    /// multiple parallel computations. This is more efficient than `OncePool` when
165    /// running many parallel operations sequentially.
166    ///
167    /// # Thread Count Decision
168    ///
169    /// Thread count is determined the same way as [`Self::once`]:
170    /// - The `num_threads` parameter
171    /// - The `ORX_NUM_THREADS` environment variable (if set)
172    /// - Available system CPU cores
173    ///
174    /// The minimum of these constraints will be used.
175    ///
176    /// # Parameters
177    ///
178    /// - `num_threads` - Configuration as described in [`Self::once`]
179    ///
180    /// # Examples
181    ///
182    /// ```ignore
183    /// use orx_parallel::*;
184    ///
185    /// // Create and reuse a persistent pool
186    /// let pool = Pool::basic(8);
187    ///
188    /// for data in datasets {
189    ///     let result = data.into_par()
190    ///         .map(|x| process(x))
191    ///         .pool(pool)
192    ///         .collect();
193    /// }
194    /// ```
195    ///
196    /// # Benefits Over OncePool
197    ///
198    /// - Worker threads persist between computations
199    /// - Avoids overhead of repeated thread spawning
200    /// - Ideal for applications with many parallel tasks
201    #[cfg(feature = "std")]
202    pub fn basic(num_threads: impl Into<NumThreads>) -> BasicPool {
203        BasicPool::new(num_threads)
204    }
205
206    /// Creates a Rayon [`ThreadPool`](https://docs.rs/rayon-core/latest/rayon_core/struct.ThreadPool.html).
207    ///
208    /// This method integrates with the Rayon parallel runtime. Rayon pools can be used
209    /// with orx-parallel parallel iterators through the `.pool()` method.
210    ///
211    /// # Thread Count Decision
212    ///
213    /// Rayon's thread count is determined similarly to other pools:
214    /// - When `num_threads` is `0` or `NumThreads::Auto`:
215    ///   - Rayon uses `RAYON_NUM_THREADS` environment variable if set
216    ///   - Otherwise uses the number of logical CPUs
217    /// - When `num_threads` is `n > 0` or `NumThreads::Max(n)`:
218    ///   - Rayon will start at most `n` threads
219    ///
220    /// Note: `ORX_NUM_THREADS` is not automatically applied to Rayon pools.
221    /// See Rayon documentation for its configuration options.
222    ///
223    /// # Parameters
224    ///
225    /// - `num_threads` - Configuration for the Rayon thread pool
226    ///
227    /// # Returns
228    ///
229    /// - `Ok(ThreadPool)` - Successfully created Rayon pool
230    /// - `Err(ThreadPoolBuildError)` - Failed to create pool (e.g., invalid configuration)
231    ///
232    /// # Examples
233    ///
234    /// ```ignore
235    /// use orx_parallel::*;
236    ///
237    /// // Create a Rayon pool with automatic thread detection
238    /// let pool = Pool::rayon(NumThreads::Auto)?;
239    ///
240    /// // Create a Rayon pool capped at 4 threads
241    /// let pool = Pool::rayon(4)?;
242    ///
243    /// let result = (0..1000)
244    ///     .into_par()
245    ///     .map(|x| x * 2)
246    ///     .pool(pool)
247    ///     .collect();
248    /// ```
249    ///
250    /// # Features
251    ///
252    /// Requires the `rayon-core` feature to be enabled.
253    ///
254    /// [`ThreadPool`]: https://docs.rs/rayon-core/latest/rayon_core/struct.ThreadPool.html
255    #[cfg(feature = "rayon-core")]
256    pub fn rayon(
257        num_threads: impl Into<NumThreads>,
258    ) -> Result<rayon_core::ThreadPool, rayon_core::ThreadPoolBuildError> {
259        let num_threads = match num_threads.into() {
260            NumThreads::Auto => 0,
261            NumThreads::Max(nt) => nt.into(),
262        };
263        rayon_core::ThreadPoolBuilder::new()
264            .num_threads(num_threads)
265            .build()
266    }
267}