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