Skip to main content

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}