Skip to main content

qubit_executor/task/
task_slot.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::sync::Arc;
11
12use qubit_function::Callable;
13
14use super::{
15    TaskResult,
16    task_runner::TaskRunner,
17    task_state::TaskState,
18};
19
20/// Runner-side slot for one task submission.
21///
22/// This low-level endpoint is exposed so custom executor services built on top
23/// of `qubit-executor` can wire their own scheduling while still returning the
24/// standard [`crate::TaskHandle`]. Executor implementations should call
25/// [`Self::accept`] only after submission succeeds; this arms lifecycle hook
26/// reporting for later start and finish events. Normal callers should use
27/// [`crate::TaskHandle`] and executor/service submission methods instead.
28pub struct TaskSlot<R, E> {
29    /// Shared state updated by this completion endpoint.
30    pub(crate) state: Arc<TaskState<R, E>>,
31}
32
33impl<R, E> TaskSlot<R, E> {
34    /// Marks this runner endpoint as accepted and arms lifecycle hook reporting.
35    ///
36    /// Calling this method emits `on_accepted` before any later `on_started` or
37    /// `on_finished` event for the same task. Executor implementations must call
38    /// it only after submission has succeeded. Dropping a slot before acceptance
39    /// still releases result waiters with `Dropped`, but does not emit lifecycle
40    /// hook events for a task that was rejected before acceptance.
41    #[inline]
42    pub fn accept(&self) {
43        let _accepted_now = self.state.accept();
44    }
45
46    /// Marks the task as started if it was not cancelled first.
47    ///
48    /// # Returns
49    ///
50    /// `true` if the runner should execute the task, or `false` if the task was
51    /// already completed through cancellation.
52    pub(crate) fn start(&self) -> bool {
53        self.state.try_start(self.state.is_accepted())
54    }
55
56    /// Completes the task with its final result.
57    ///
58    /// If another path has already completed the task, this result is ignored.
59    ///
60    /// # Parameters
61    ///
62    /// * `result` - Final task result to publish if the task is not already
63    ///   completed.
64    #[inline]
65    pub(crate) fn complete(&self, result: TaskResult<R, E>) {
66        let _completed = self.state.try_complete(result, self.state.is_accepted());
67    }
68
69    /// Starts the task and completes it with a lazily produced result.
70    ///
71    /// The supplied closure is executed only if this completion endpoint wins
72    /// the start race. If the handle was cancelled first, the closure is not
73    /// called and the existing cancellation result is preserved.
74    ///
75    /// # Parameters
76    ///
77    /// * `task` - Closure that runs the accepted task and returns its final
78    ///   result.
79    ///
80    /// # Returns
81    ///
82    /// `true` if the closure was executed and its result was published, or
83    /// `false` if the task had already been completed by cancellation.
84    #[inline]
85    pub(crate) fn start_and_complete<F>(&self, task: F) -> bool
86    where
87        F: FnOnce() -> TaskResult<R, E>,
88    {
89        if !self.start() {
90            return false;
91        }
92        self.complete(task());
93        true
94    }
95
96    /// Starts this slot and runs a callable to completion.
97    ///
98    /// # Parameters
99    ///
100    /// * `task` - Callable to run if the task has not been cancelled.
101    ///
102    /// # Returns
103    ///
104    /// `true` if the callable ran and published a result, or `false` if the
105    /// task had already been cancelled.
106    #[inline]
107    pub fn run<C>(self, task: C) -> bool
108    where
109        C: Callable<R, E>,
110    {
111        self.start_and_complete(|| TaskRunner::new(task).call())
112    }
113}
114
115impl<R, E> Drop for TaskSlot<R, E> {
116    /// Publishes a dropped-result error when the runner endpoint is abandoned.
117    #[inline]
118    fn drop(&mut self) {
119        let _ignored = self.state.try_drop_unfinished(self.state.is_accepted());
120    }
121}