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}