Skip to main content

qubit_execution_services/
execution_services_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 ******************************************************************************/
10//! Builder for the execution-services facade.
11
12use std::{
13    thread,
14    time::Duration,
15};
16
17use super::{
18    BlockingExecutorService,
19    BlockingExecutorServiceBuilder,
20    ExecutionServices,
21    ExecutionServicesBuildError,
22    RayonExecutorService,
23    RayonExecutorServiceBuilder,
24    TokioBlockingExecutorService,
25    TokioIoExecutorService,
26};
27
28/// Builder for [`ExecutionServices`].
29///
30/// The builder exposes blocking-pool options by delegating to
31/// [`BlockingExecutorServiceBuilder`] and CPU-pool options by delegating to
32/// [`RayonExecutorServiceBuilder`]. Tokio-backed domains are created with their
33/// default constructors because they do not currently expose custom builders.
34#[derive(Debug, Clone)]
35pub struct ExecutionServicesBuilder {
36    /// Builder for the blocking executor domain.
37    blocking: BlockingExecutorServiceBuilder,
38    /// Builder for the CPU executor domain.
39    cpu: RayonExecutorServiceBuilder,
40}
41
42impl ExecutionServicesBuilder {
43    /// Sets both the blocking core and maximum pool sizes to the same value.
44    ///
45    /// # Parameters
46    ///
47    /// * `pool_size` - Pool size applied as both core and maximum limits.
48    ///
49    /// # Returns
50    ///
51    /// This builder for fluent configuration.
52    #[inline]
53    pub fn blocking_pool_size(mut self, pool_size: usize) -> Self {
54        self.blocking = self.blocking.pool_size(pool_size);
55        self
56    }
57
58    /// Sets the blocking core pool size.
59    ///
60    /// # Parameters
61    ///
62    /// * `core_pool_size` - Core pool size for the blocking domain.
63    ///
64    /// # Returns
65    ///
66    /// This builder for fluent configuration.
67    #[inline]
68    pub fn blocking_core_pool_size(mut self, core_pool_size: usize) -> Self {
69        self.blocking = self.blocking.core_pool_size(core_pool_size);
70        self
71    }
72
73    /// Sets the blocking maximum pool size.
74    ///
75    /// # Parameters
76    ///
77    /// * `maximum_pool_size` - Maximum pool size for the blocking domain.
78    ///
79    /// # Returns
80    ///
81    /// This builder for fluent configuration.
82    #[inline]
83    pub fn blocking_maximum_pool_size(mut self, maximum_pool_size: usize) -> Self {
84        self.blocking = self.blocking.maximum_pool_size(maximum_pool_size);
85        self
86    }
87
88    /// Sets a bounded queue capacity for the blocking domain.
89    ///
90    /// # Parameters
91    ///
92    /// * `capacity` - Maximum number of queued blocking tasks.
93    ///
94    /// # Returns
95    ///
96    /// This builder for fluent configuration.
97    #[inline]
98    pub fn blocking_queue_capacity(mut self, capacity: usize) -> Self {
99        self.blocking = self.blocking.queue_capacity(capacity);
100        self
101    }
102
103    /// Configures the blocking domain to use an unbounded queue.
104    ///
105    /// # Returns
106    ///
107    /// This builder for fluent configuration.
108    #[inline]
109    pub fn blocking_unbounded_queue(mut self) -> Self {
110        self.blocking = self.blocking.unbounded_queue();
111        self
112    }
113
114    /// Sets the blocking worker-thread name prefix.
115    ///
116    /// # Parameters
117    ///
118    /// * `prefix` - Prefix appended with the worker index.
119    ///
120    /// # Returns
121    ///
122    /// This builder for fluent configuration.
123    #[inline]
124    pub fn blocking_thread_name_prefix(mut self, prefix: &str) -> Self {
125        self.blocking = self.blocking.thread_name_prefix(prefix);
126        self
127    }
128
129    /// Sets the blocking worker-thread stack size.
130    ///
131    /// # Parameters
132    ///
133    /// * `stack_size` - Stack size in bytes for each blocking worker.
134    ///
135    /// # Returns
136    ///
137    /// This builder for fluent configuration.
138    #[inline]
139    pub fn blocking_stack_size(mut self, stack_size: usize) -> Self {
140        self.blocking = self.blocking.stack_size(stack_size);
141        self
142    }
143
144    /// Sets the blocking worker keep-alive timeout.
145    ///
146    /// # Parameters
147    ///
148    /// * `keep_alive` - Idle timeout for blocking workers allowed to retire.
149    ///
150    /// # Returns
151    ///
152    /// This builder for fluent configuration.
153    #[inline]
154    pub fn blocking_keep_alive(mut self, keep_alive: Duration) -> Self {
155        self.blocking = self.blocking.keep_alive(keep_alive);
156        self
157    }
158
159    /// Allows blocking core workers to retire after keep-alive timeout.
160    ///
161    /// # Parameters
162    ///
163    /// * `allow` - Whether idle blocking core workers may time out.
164    ///
165    /// # Returns
166    ///
167    /// This builder for fluent configuration.
168    #[inline]
169    pub fn blocking_allow_core_thread_timeout(mut self, allow: bool) -> Self {
170        self.blocking = self.blocking.allow_core_thread_timeout(allow);
171        self
172    }
173
174    /// Starts all blocking core workers during build.
175    ///
176    /// # Returns
177    ///
178    /// This builder for fluent configuration.
179    #[inline]
180    pub fn blocking_prestart_core_threads(mut self) -> Self {
181        self.blocking = self.blocking.prestart_core_threads();
182        self
183    }
184
185    /// Sets the number of Rayon worker threads in the CPU domain.
186    ///
187    /// # Parameters
188    ///
189    /// * `num_threads` - Number of Rayon worker threads.
190    ///
191    /// # Returns
192    ///
193    /// This builder for fluent configuration.
194    #[inline]
195    pub fn cpu_threads(mut self, num_threads: usize) -> Self {
196        self.cpu = self.cpu.num_threads(num_threads);
197        self
198    }
199
200    /// Sets the Rayon worker-thread name prefix in the CPU domain.
201    ///
202    /// # Parameters
203    ///
204    /// * `prefix` - Prefix appended with the worker index.
205    ///
206    /// # Returns
207    ///
208    /// This builder for fluent configuration.
209    #[inline]
210    pub fn cpu_thread_name_prefix(mut self, prefix: &str) -> Self {
211        self.cpu = self.cpu.thread_name_prefix(prefix);
212        self
213    }
214
215    /// Sets the Rayon worker-thread stack size in the CPU domain.
216    ///
217    /// # Parameters
218    ///
219    /// * `stack_size` - Stack size in bytes for each Rayon worker.
220    ///
221    /// # Returns
222    ///
223    /// This builder for fluent configuration.
224    #[inline]
225    pub fn cpu_stack_size(mut self, stack_size: usize) -> Self {
226        self.cpu = self.cpu.stack_size(stack_size);
227        self
228    }
229
230    /// Builds the configured execution-services facade.
231    ///
232    /// # Returns
233    ///
234    /// `Ok(ExecutionServices)` if the blocking and CPU domains build
235    /// successfully.
236    ///
237    /// # Errors
238    ///
239    /// Returns [`ExecutionServicesBuildError`] if either the blocking or CPU
240    /// domain rejects its builder configuration.
241    pub fn build(self) -> Result<ExecutionServices, ExecutionServicesBuildError> {
242        let blocking = self
243            .blocking
244            .build()
245            .map_err(|source| ExecutionServicesBuildError::Blocking { source })?;
246        let cpu = self
247            .cpu
248            .build()
249            .map_err(|source| ExecutionServicesBuildError::Cpu { source })?;
250        let tokio_blocking = TokioBlockingExecutorService::new();
251        let io = TokioIoExecutorService::new();
252        Ok(ExecutionServices::from_parts(
253            blocking,
254            cpu,
255            tokio_blocking,
256            io,
257        ))
258    }
259}
260
261impl Default for ExecutionServicesBuilder {
262    /// Creates a builder with CPU-parallelism defaults.
263    ///
264    /// # Returns
265    ///
266    /// A builder configured with available parallelism for both blocking and
267    /// CPU domains.
268    fn default() -> Self {
269        let pool_size = default_pool_size();
270        Self {
271            blocking: BlockingExecutorService::builder().pool_size(pool_size),
272            cpu: RayonExecutorService::builder().num_threads(pool_size),
273        }
274    }
275}
276
277/// Returns the default pool size for blocking and CPU domains.
278///
279/// # Returns
280///
281/// The available CPU parallelism, or `1` if it cannot be detected.
282fn default_pool_size() -> usize {
283    thread::available_parallelism()
284        .map(usize::from)
285        .unwrap_or(1)
286}