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}