qubit_executor/service/executor_service.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 qubit_function::{
11 Callable,
12 Runnable,
13};
14
15use crate::task::spi::{
16 TaskResultHandle,
17 TrackedTaskHandle,
18};
19
20use super::{
21 ExecutorServiceLifecycle,
22 StopReport,
23 SubmissionError,
24};
25
26/// Managed task service with submission and lifecycle control.
27///
28/// `ExecutorService` is intentionally separate from
29/// [`Executor`](crate::Executor). An executor describes an
30/// execution strategy; an executor service accepts tasks into a managed service
31/// that may queue, schedule, assign workers, and track lifecycle.
32///
33/// `submit` and `submit_callable` return `Result` values whose outer `Ok`
34/// means only that the service accepted the task. It does **not** mean the task
35/// has started or succeeded. `submit` is fire-and-forget; callable and tracked
36/// variants return handles for observing the final task result.
37///
38/// ## Lifecycle
39///
40/// A service starts in [`ExecutorServiceLifecycle::Running`]. While running,
41/// submissions may be accepted. Calling [`shutdown`](Self::shutdown) starts an
42/// orderly shutdown and moves the service toward
43/// [`ExecutorServiceLifecycle::ShuttingDown`]: later submissions are rejected,
44/// while work accepted before shutdown is allowed to finish normally. Calling
45/// [`stop`](Self::stop) starts an abrupt stop and moves the service toward
46/// [`ExecutorServiceLifecycle::Stopping`]: later submissions are rejected and
47/// the implementation attempts to cancel or abort accepted work that can still
48/// be stopped.
49///
50/// `shutdown` and `stop` are both terminal admission decisions; neither allows
51/// the service to become running again. The difference is how accepted work is
52/// treated. `shutdown` preserves accepted work, including queued or scheduled
53/// work, unless a concrete service documents a stronger policy. `stop` is a
54/// best-effort interruption request for queued, scheduled, unstarted, or
55/// runtime-abortable work. Work already running in ordinary Rust code, blocking
56/// calls, or OS threads may not be forcibly interrupted, so termination can
57/// still wait for that work to return.
58///
59/// A service reaches [`ExecutorServiceLifecycle::Terminated`] after shutdown or
60/// stop has been requested and no accepted work remains active. Accepted work
61/// may have completed normally, failed, panicked, been cancelled, or been
62/// dropped by its runner endpoint, or been aborted according to the concrete
63/// service's capabilities.
64pub trait ExecutorService: Send + Sync {
65 /// Result handle returned for an accepted callable task.
66 type ResultHandle<R, E>: TaskResultHandle<R, E>
67 where
68 R: Send + 'static,
69 E: Send + 'static;
70
71 /// Tracked handle returned for accepted tasks that expose status.
72 type TrackedHandle<R, E>: TrackedTaskHandle<R, E>
73 where
74 R: Send + 'static,
75 E: Send + 'static;
76
77 /// Submits a runnable task to this service.
78 ///
79 /// # Parameters
80 ///
81 /// * `task` - A fallible background action with no business return value.
82 ///
83 /// # Returns
84 ///
85 /// `Ok(())` if the service accepts the task. This only reports acceptance;
86 /// it does not report task start or task success. Returns
87 /// `Err(SubmissionError)` if the service refuses the task before
88 /// accepting it.
89 ///
90 /// # Errors
91 ///
92 /// Returns [`SubmissionError`] when the service refuses the task before
93 /// accepting it.
94 fn submit<T, E>(&self, task: T) -> Result<(), SubmissionError>
95 where
96 T: Runnable<E> + Send + 'static,
97 E: Send + 'static;
98
99 /// Submits a callable task to this service.
100 ///
101 /// # Parameters
102 ///
103 /// * `task` - A fallible computation whose success value should be captured
104 /// in the returned handle.
105 ///
106 /// # Returns
107 ///
108 /// `Ok(handle)` if the service accepts the task. This only reports
109 /// acceptance; task success, task failure, panic, or cancellation must be
110 /// observed through the returned handle. Returns `Err(SubmissionError)` if
111 /// the service refuses the task before accepting it.
112 ///
113 /// # Errors
114 ///
115 /// Returns [`SubmissionError`] when the service refuses the task before
116 /// accepting it.
117 fn submit_callable<C, R, E>(
118 &self,
119 task: C,
120 ) -> Result<Self::ResultHandle<R, E>, SubmissionError>
121 where
122 C: Callable<R, E> + Send + 'static,
123 R: Send + 'static,
124 E: Send + 'static;
125
126 /// Submits a runnable task and returns a tracked handle.
127 ///
128 /// # Parameters
129 ///
130 /// * `task` - A fallible background action with no business return value.
131 ///
132 /// # Returns
133 ///
134 /// `Ok(handle)` if the service accepts the task. The handle exposes status,
135 /// pre-start cancellation, and final unit result retrieval.
136 ///
137 /// # Errors
138 ///
139 /// Returns [`SubmissionError`] when the service refuses the task before
140 /// accepting it.
141 #[inline]
142 fn submit_tracked<T, E>(&self, task: T) -> Result<Self::TrackedHandle<(), E>, SubmissionError>
143 where
144 T: Runnable<E> + Send + 'static,
145 E: Send + 'static,
146 {
147 let mut task = task;
148 self.submit_tracked_callable(move || task.run())
149 }
150
151 /// Submits a callable task and returns a tracked handle.
152 ///
153 /// # Parameters
154 ///
155 /// * `task` - A fallible computation whose success value should be captured
156 /// in the returned handle.
157 ///
158 /// # Returns
159 ///
160 /// `Ok(handle)` if the service accepts the task. The handle exposes status,
161 /// pre-start cancellation, and final result retrieval.
162 ///
163 /// # Errors
164 ///
165 /// Returns [`SubmissionError`] when the service refuses the task before
166 /// accepting it.
167 fn submit_tracked_callable<C, R, E>(
168 &self,
169 task: C,
170 ) -> Result<Self::TrackedHandle<R, E>, SubmissionError>
171 where
172 C: Callable<R, E> + Send + 'static,
173 R: Send + 'static,
174 E: Send + 'static;
175
176 /// Initiates an orderly shutdown.
177 ///
178 /// After shutdown starts, the service rejects new submissions and enters
179 /// the [`ExecutorServiceLifecycle::ShuttingDown`] path. Already accepted
180 /// work is allowed to complete normally, including work that is queued,
181 /// scheduled, or running, unless the concrete service documents a stronger
182 /// cancellation policy.
183 ///
184 /// This method is an admission gate change, not a wait operation. Use
185 /// [`wait_termination`](Self::wait_termination) to block until all accepted
186 /// work has completed or the service has otherwise terminated.
187 fn shutdown(&self);
188
189 /// Attempts to stop accepting new tasks and stop accepted work immediately.
190 ///
191 /// After stop starts, the service rejects new submissions and enters the
192 /// [`ExecutorServiceLifecycle::Stopping`] path. The implementation should
193 /// cancel queued, scheduled, or unstarted work where possible, and abort
194 /// runtime-managed work where its runtime provides an abort mechanism.
195 ///
196 /// `stop` is best effort. It cannot promise to interrupt arbitrary Rust
197 /// code, blocking calls, or already-running OS-thread work. Such work may
198 /// continue until it returns, and service termination waits for any
199 /// non-interruptible accepted work that remains active.
200 ///
201 /// # Returns
202 ///
203 /// A count-based stop report describing queued, running, and cancelled work
204 /// observed while handling the request.
205 fn stop(&self) -> StopReport;
206
207 /// Returns the current lifecycle state.
208 ///
209 /// # Returns
210 ///
211 /// The lifecycle state currently observed by this service.
212 fn lifecycle(&self) -> ExecutorServiceLifecycle;
213
214 /// Returns whether the service accepts new tasks.
215 ///
216 /// # Returns
217 ///
218 /// `true` only while the lifecycle is [`ExecutorServiceLifecycle::Running`].
219 #[inline]
220 fn is_running(&self) -> bool {
221 self.lifecycle() == ExecutorServiceLifecycle::Running
222 }
223
224 /// Returns whether graceful shutdown is in progress.
225 ///
226 /// # Returns
227 ///
228 /// `true` only while the lifecycle is
229 /// [`ExecutorServiceLifecycle::ShuttingDown`].
230 #[inline]
231 fn is_shutting_down(&self) -> bool {
232 self.lifecycle() == ExecutorServiceLifecycle::ShuttingDown
233 }
234
235 /// Returns whether abrupt stop is in progress.
236 ///
237 /// # Returns
238 ///
239 /// `true` only while the lifecycle is [`ExecutorServiceLifecycle::Stopping`].
240 #[inline]
241 fn is_stopping(&self) -> bool {
242 self.lifecycle() == ExecutorServiceLifecycle::Stopping
243 }
244
245 /// Returns whether this service is not running.
246 ///
247 /// # Returns
248 ///
249 /// `true` once the service has started graceful shutdown, abrupt stop, or has
250 /// already terminated.
251 #[inline]
252 fn is_not_running(&self) -> bool {
253 self.lifecycle() != ExecutorServiceLifecycle::Running
254 }
255
256 /// Returns whether the service has terminated.
257 ///
258 /// # Returns
259 ///
260 /// `true` only after shutdown or stop has been requested and all accepted
261 /// tasks have completed or been cancelled.
262 #[inline]
263 fn is_terminated(&self) -> bool {
264 self.lifecycle() == ExecutorServiceLifecycle::Terminated
265 }
266
267 /// Blocks the current thread until the service has terminated.
268 ///
269 /// This method is a synchronous, blocking wait. It returns only after
270 /// [`shutdown`](Self::shutdown) or [`stop`](Self::stop) has been requested
271 /// and no accepted tasks remain active. If it is called while the service is
272 /// still [`ExecutorServiceLifecycle::Running`] and no other thread requests
273 /// shutdown or stop, it may block forever.
274 ///
275 /// Implementations must not present this method as an asynchronous or
276 /// non-blocking operation.
277 fn wait_termination(&self);
278}