Skip to main content

qubit_executor/task/
running_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 a task that has crossed into the running state.
21///
22/// This endpoint is returned by
23/// [`TaskSlot::try_start`](crate::task::spi::TaskSlot::try_start) after the
24/// pending to running transition succeeds. It lets schedulers claim a task
25/// under their own queue locks before removing the task from their pending
26/// structures. Dropping a running slot before completion reports
27/// [`crate::TaskExecutionError::Dropped`].
28pub struct RunningTaskSlot<R, E> {
29    /// Shared state updated by this running endpoint.
30    state: Option<Arc<TaskState<R, E>>>,
31}
32
33impl<R, E> RunningTaskSlot<R, E> {
34    /// Creates a running task slot from already-started task state.
35    ///
36    /// # Parameters
37    ///
38    /// * `state` - Shared task state that has already moved to running.
39    ///
40    /// # Returns
41    ///
42    /// A running endpoint that must complete or drop the task.
43    #[inline]
44    pub(crate) fn new(state: Arc<TaskState<R, E>>) -> Self {
45        Self { state: Some(state) }
46    }
47
48    /// Returns the shared state owned by this running slot.
49    ///
50    /// # Returns
51    ///
52    /// A reference to the task state. The state is present until this running
53    /// endpoint publishes a terminal completion result.
54    #[inline]
55    fn state(&self) -> &TaskState<R, E> {
56        self.state
57            .as_deref()
58            .expect("running task slot state should be present")
59    }
60
61    /// Completes the running task with its final result.
62    ///
63    /// Cancellation is a pre-start decision and should be reported through
64    /// [`TaskSlot::cancel_unstarted`](crate::task::spi::TaskSlot::cancel_unstarted).
65    /// Passing a cancellation or dropped result does not complete the running
66    /// task; the slot is then dropped and callers observe
67    /// [`crate::TaskExecutionError::Dropped`].
68    ///
69    /// # Parameters
70    ///
71    /// * `result` - Final task result to publish for this running task.
72    ///
73    /// # Returns
74    ///
75    /// `true` if the result was published, or `false` if the state was no
76    /// longer running or the result does not represent normal task completion.
77    #[inline]
78    pub fn complete(mut self, result: TaskResult<R, E>) -> bool {
79        let completed = self
80            .state()
81            .try_complete(result, self.state().is_accepted());
82        if completed {
83            self.state.take();
84        }
85        completed
86    }
87
88    /// Runs a callable and publishes its final result through this running slot.
89    ///
90    /// The callable is always executed because the pending-to-running
91    /// transition has already succeeded. Task failures and panics are converted
92    /// through [`TaskRunner`].
93    ///
94    /// # Parameters
95    ///
96    /// * `task` - Callable to execute for the already-started task.
97    ///
98    /// # Returns
99    ///
100    /// `true` if the callable result was published, or `false` if completion
101    /// was no longer possible.
102    #[inline]
103    pub fn run<C>(self, task: C) -> bool
104    where
105        C: Callable<R, E>,
106    {
107        self.complete(TaskRunner::new(task).call())
108    }
109}
110
111impl<R, E> Drop for RunningTaskSlot<R, E> {
112    /// Publishes a dropped-result error when the running endpoint is abandoned.
113    #[inline]
114    fn drop(&mut self) {
115        if let Some(state) = &self.state {
116            let _ignored = state.try_drop_unfinished(state.is_accepted());
117        }
118    }
119}