Skip to main content

qubit_rayon_executor/
rayon_executor_service_builder.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10use std::{
11    sync::Arc,
12    thread,
13};
14
15use rayon::ThreadPoolBuilder as RayonThreadPoolBuilder;
16
17use crate::{
18    rayon_executor_service::RayonExecutorService,
19    rayon_executor_service_build_error::RayonExecutorServiceBuildError,
20    rayon_executor_service_state::RayonExecutorServiceState,
21};
22
23/// Default thread name prefix used by [`RayonExecutorServiceBuilder`].
24const DEFAULT_THREAD_NAME_PREFIX: &str = "qubit-rayon-executor";
25
26/// Builder for [`RayonExecutorService`].
27///
28/// The default builder uses the available CPU parallelism and names workers
29/// with the `qubit-rayon-executor` prefix.
30#[derive(Debug, Clone)]
31pub struct RayonExecutorServiceBuilder {
32    /// Number of Rayon worker threads to create.
33    num_threads: usize,
34    /// Prefix used when naming Rayon worker threads.
35    thread_name_prefix: String,
36    /// Optional worker stack size in bytes.
37    stack_size: Option<usize>,
38}
39
40impl RayonExecutorServiceBuilder {
41    /// Sets the number of Rayon worker threads.
42    ///
43    /// # Parameters
44    ///
45    /// * `num_threads` - Number of Rayon worker threads.
46    ///
47    /// # Returns
48    ///
49    /// This builder for fluent configuration.
50    #[inline]
51    pub fn num_threads(mut self, num_threads: usize) -> Self {
52        self.num_threads = num_threads;
53        self
54    }
55
56    /// Sets the Rayon worker-thread name prefix.
57    ///
58    /// # Parameters
59    ///
60    /// * `prefix` - Prefix appended with the worker index.
61    ///
62    /// # Returns
63    ///
64    /// This builder for fluent configuration.
65    #[inline]
66    pub fn thread_name_prefix(mut self, prefix: &str) -> Self {
67        self.thread_name_prefix = prefix.to_owned();
68        self
69    }
70
71    /// Sets the Rayon worker-thread stack size.
72    ///
73    /// # Parameters
74    ///
75    /// * `stack_size` - Stack size in bytes for each Rayon worker.
76    ///
77    /// # Returns
78    ///
79    /// This builder for fluent configuration.
80    #[inline]
81    pub fn stack_size(mut self, stack_size: usize) -> Self {
82        self.stack_size = Some(stack_size);
83        self
84    }
85
86    /// Builds the configured Rayon executor service.
87    ///
88    /// # Returns
89    ///
90    /// `Ok(RayonExecutorService)` if the Rayon thread pool is created
91    /// successfully.
92    ///
93    /// # Errors
94    ///
95    /// Returns [`RayonExecutorServiceBuildError`] if the thread count or stack
96    /// size is zero, or if Rayon rejects the thread-pool configuration.
97    pub fn build(self) -> Result<RayonExecutorService, RayonExecutorServiceBuildError> {
98        if self.num_threads == 0 {
99            return Err(RayonExecutorServiceBuildError::ZeroThreadCount);
100        }
101        if self.stack_size == Some(0) {
102            return Err(RayonExecutorServiceBuildError::ZeroStackSize);
103        }
104        let prefix = self.thread_name_prefix;
105        let mut builder = RayonThreadPoolBuilder::new()
106            .num_threads(self.num_threads)
107            .thread_name(move |index| format!("{prefix}-{index}"));
108        if let Some(stack_size) = self.stack_size {
109            builder = builder.stack_size(stack_size);
110        }
111        let pool = Arc::new(builder.build()?);
112        Ok(RayonExecutorService {
113            pool,
114            state: Arc::new(RayonExecutorServiceState::new()),
115        })
116    }
117}
118
119impl Default for RayonExecutorServiceBuilder {
120    /// Creates a builder with CPU-parallelism defaults.
121    ///
122    /// # Returns
123    ///
124    /// A builder configured for the detected CPU parallelism.
125    fn default() -> Self {
126        Self {
127            num_threads: default_rayon_thread_count(),
128            thread_name_prefix: DEFAULT_THREAD_NAME_PREFIX.to_owned(),
129            stack_size: None,
130        }
131    }
132}
133
134/// Returns the default Rayon thread count for new builders.
135///
136/// # Returns
137///
138/// The available CPU parallelism, or `1` if it cannot be detected.
139fn default_rayon_thread_count() -> usize {
140    thread::available_parallelism()
141        .map(usize::from)
142        .unwrap_or(1)
143}