qubit_rayon_executor/
rayon_executor_service.rs1use std::sync::{
11 Arc,
12 Mutex,
13};
14
15use qubit_function::{
16 Callable,
17 Runnable,
18};
19use rayon::ThreadPool as RayonThreadPool;
20
21use qubit_executor::{
22 TaskHandle,
23 task::spi::{
24 TaskEndpointPair,
25 TaskRunner,
26 TaskSlot,
27 },
28};
29
30use qubit_executor::service::{
31 ExecutorService,
32 ExecutorServiceLifecycle,
33 StopReport,
34 SubmissionError,
35};
36
37use crate::{
38 pending_cancel::PendingCancel,
39 rayon_executor_service_build_error::RayonExecutorServiceBuildError,
40 rayon_executor_service_builder::RayonExecutorServiceBuilder,
41 rayon_executor_service_state::RayonExecutorServiceState,
42 rayon_task_handle::RayonTaskHandle,
43};
44
45#[derive(Clone)]
51pub struct RayonExecutorService {
52 pub(crate) pool: Arc<RayonThreadPool>,
54 pub(crate) state: Arc<RayonExecutorServiceState>,
56}
57
58impl RayonExecutorService {
59 #[inline]
71 pub fn new() -> Result<Self, RayonExecutorServiceBuildError> {
72 Self::builder().build()
73 }
74
75 #[inline]
81 pub fn builder() -> RayonExecutorServiceBuilder {
82 RayonExecutorServiceBuilder::default()
83 }
84
85 fn submit_callable_with<C, R, E, H, F>(
103 &self,
104 task: C,
105 split: F,
106 ) -> Result<(H, usize, PendingCancel), SubmissionError>
107 where
108 C: Callable<R, E> + Send + 'static,
109 R: Send + 'static,
110 E: Send + 'static,
111 F: FnOnce(TaskEndpointPair<R, E>) -> (H, TaskSlot<R, E>),
112 {
113 let submission_guard = self.state.lock_submission();
114 if self.state.is_not_running() {
115 return Err(SubmissionError::Shutdown);
116 }
117 let task_id = self.state.next_task_id();
118 self.state.on_task_accepted();
119 let (handle, completion) = split(TaskEndpointPair::new());
120 completion.accept();
121 let completion = Arc::new(Mutex::new(Some(completion)));
122 let completion_for_cancel = Arc::clone(&completion);
123 let cancel: PendingCancel = Arc::new(move || {
124 let completion = completion_for_cancel
125 .lock()
126 .unwrap_or_else(std::sync::PoisonError::into_inner)
127 .take();
128 completion.is_some_and(|completion| completion.cancel_unstarted())
129 });
130 self.state.register_pending_task(task_id, Arc::clone(&cancel));
131 drop(submission_guard);
132
133 let completion_for_run = completion;
134 let state_for_run = Arc::clone(&self.state);
135 self.pool.spawn_fifo(move || {
136 let mut running_completion = None;
137 if !state_for_run.start_pending_task(task_id, || {
138 let mut completion = completion_for_run
139 .lock()
140 .unwrap_or_else(std::sync::PoisonError::into_inner);
141 let Some(task_completion) = completion.take() else {
142 return false;
143 };
144 match task_completion.try_start() {
145 Ok(running) => {
146 running_completion = Some(running);
147 true
148 }
149 Err(task_completion) => {
150 *completion = Some(task_completion);
151 false
152 }
153 }
154 }) {
155 return;
156 }
157 let running_completion = running_completion.expect("claimed pending task should own a running slot");
158 TaskRunner::new(task).run_started(running_completion);
159 state_for_run.on_task_completed();
160 });
161 Ok((handle, task_id, cancel))
162 }
163}
164
165impl ExecutorService for RayonExecutorService {
166 type ResultHandle<R, E>
167 = TaskHandle<R, E>
168 where
169 R: Send + 'static,
170 E: Send + 'static;
171
172 type TrackedHandle<R, E>
173 = RayonTaskHandle<R, E>
174 where
175 R: Send + 'static,
176 E: Send + 'static;
177
178 fn submit<T, E>(&self, task: T) -> Result<(), SubmissionError>
180 where
181 T: Runnable<E> + Send + 'static,
182 E: Send + 'static,
183 {
184 let submission_guard = self.state.lock_submission();
185 if self.state.is_not_running() {
186 return Err(SubmissionError::Shutdown);
187 }
188 let task_id = self.state.next_task_id();
189 self.state.on_task_accepted();
190 let cancel: PendingCancel = Arc::new(|| true);
191 self.state.register_pending_task(task_id, Arc::clone(&cancel));
192 drop(submission_guard);
193
194 let state_for_run = Arc::clone(&self.state);
195 self.pool.spawn_fifo(move || {
196 if !state_for_run.start_pending_task(task_id, || true) {
197 return;
198 }
199 let mut task = task;
200 let _ignored = TaskRunner::new(move || task.run()).call::<(), E>();
201 state_for_run.on_task_completed();
202 });
203 Ok(())
204 }
205
206 fn submit_callable<C, R, E>(&self, task: C) -> Result<Self::ResultHandle<R, E>, SubmissionError>
221 where
222 C: Callable<R, E> + Send + 'static,
223 R: Send + 'static,
224 E: Send + 'static,
225 {
226 let (handle, _, _) = self.submit_callable_with(task, TaskEndpointPair::into_parts)?;
227 Ok(handle)
228 }
229
230 fn submit_tracked_callable<C, R, E>(&self, task: C) -> Result<Self::TrackedHandle<R, E>, SubmissionError>
232 where
233 C: Callable<R, E> + Send + 'static,
234 R: Send + 'static,
235 E: Send + 'static,
236 {
237 let (handle, task_id, cancel) = self.submit_callable_with(task, TaskEndpointPair::into_tracked_parts)?;
238 Ok(RayonTaskHandle::new(handle, task_id, Arc::clone(&self.state), cancel))
239 }
240
241 fn shutdown(&self) {
245 let _guard = self.state.lock_submission();
246 self.state.shutdown();
247 self.state.notify_if_terminated();
248 }
249
250 fn stop(&self) -> StopReport {
262 let _guard = self.state.lock_submission();
263 self.state.stop();
264 self.state.cancel_pending_tasks_for_stop()
265 }
266
267 fn lifecycle(&self) -> ExecutorServiceLifecycle {
269 self.state.lifecycle()
270 }
271
272 fn is_not_running(&self) -> bool {
274 self.state.is_not_running()
275 }
276
277 fn is_terminated(&self) -> bool {
279 self.lifecycle() == ExecutorServiceLifecycle::Terminated
280 }
281
282 fn wait_termination(&self) {
284 self.state.wait_for_termination();
285 }
286}