qubit_executor/schedule/scheduled_task_handle.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::{
11 future::IntoFuture,
12 sync::{
13 Arc,
14 atomic::{
15 AtomicBool,
16 Ordering,
17 },
18 },
19};
20
21use crate::{
22 CancelResult,
23 TaskResult,
24 TaskStatus,
25 TrackedTask,
26 TryGet,
27 hook::TaskId,
28 task::{
29 TaskHandleFuture,
30 spi::{
31 TaskResultHandle,
32 TrackedTaskHandle,
33 },
34 },
35};
36
37/// Tracked handle for a task accepted by a scheduled executor service.
38///
39/// The handle delegates result and status observation to the standard
40/// [`TrackedTask`] while also waking the scheduler when pre-start cancellation
41/// wins. This prevents a cancelled timer entry from keeping the single scheduler
42/// thread asleep until the original deadline.
43pub struct ScheduledTaskHandle<R, E> {
44 /// Standard tracked task handle.
45 inner: TrackedTask<R, E>,
46 /// Shared marker observed by the scheduler heap.
47 cancellation_marker: Arc<AtomicBool>,
48 /// Callback invoked after this handle cancels the pending task.
49 on_cancelled: Arc<dyn Fn() + Send + Sync + 'static>,
50}
51
52impl<R, E> ScheduledTaskHandle<R, E> {
53 /// Creates a scheduled task handle.
54 ///
55 /// # Parameters
56 ///
57 /// * `inner` - Standard tracked task handle.
58 /// * `cancellation_marker` - Shared marker observed by the scheduler heap.
59 /// * `on_cancelled` - Callback invoked when this handle cancels the task.
60 ///
61 /// # Returns
62 ///
63 /// A scheduled task handle.
64 pub(crate) const fn new(
65 inner: TrackedTask<R, E>,
66 cancellation_marker: Arc<AtomicBool>,
67 on_cancelled: Arc<dyn Fn() + Send + Sync + 'static>,
68 ) -> Self {
69 Self {
70 inner,
71 cancellation_marker,
72 on_cancelled,
73 }
74 }
75
76 /// Waits for the task to finish and returns its final result.
77 ///
78 /// # Returns
79 ///
80 /// The final task result.
81 #[inline]
82 pub fn get(self) -> TaskResult<R, E>
83 where
84 R: Send,
85 E: Send,
86 {
87 <Self as TaskResultHandle<R, E>>::get(self)
88 }
89
90 /// Attempts to retrieve the final result without blocking.
91 ///
92 /// # Returns
93 ///
94 /// A ready result or the pending scheduled handle.
95 #[inline]
96 pub fn try_get(self) -> TryGet<Self, R, E>
97 where
98 R: Send,
99 E: Send,
100 {
101 <Self as TaskResultHandle<R, E>>::try_get(self)
102 }
103
104 /// Returns whether the tracked task has installed a terminal state.
105 ///
106 /// # Returns
107 ///
108 /// `true` after the task succeeds, fails, panics, is cancelled, or loses its
109 /// completion endpoint.
110 #[inline]
111 pub fn is_done(&self) -> bool
112 where
113 R: Send,
114 E: Send,
115 {
116 <Self as TaskResultHandle<R, E>>::is_done(self)
117 }
118
119 /// Returns the currently observed task status.
120 ///
121 /// # Returns
122 ///
123 /// The current task status.
124 #[inline]
125 pub fn status(&self) -> TaskStatus {
126 self.inner.status()
127 }
128
129 /// Returns the identifier assigned to this task.
130 ///
131 /// # Returns
132 ///
133 /// The task id stored in the shared task state.
134 #[inline]
135 pub fn task_id(&self) -> TaskId {
136 self.inner.task_id()
137 }
138
139 /// Attempts to cancel this task before it starts.
140 ///
141 /// # Returns
142 ///
143 /// The observed cancellation outcome.
144 #[inline]
145 pub fn cancel(&self) -> CancelResult {
146 self.cancel_inner()
147 }
148
149 /// Performs cancellation and wakes the scheduler if this handle won.
150 ///
151 /// # Returns
152 ///
153 /// The observed cancellation outcome.
154 fn cancel_inner(&self) -> CancelResult {
155 let result = self.inner.cancel();
156 if result == CancelResult::Cancelled {
157 self.cancellation_marker.store(true, Ordering::Release);
158 (self.on_cancelled)();
159 }
160 result
161 }
162}
163
164impl<R, E> TaskResultHandle<R, E> for ScheduledTaskHandle<R, E>
165where
166 R: Send,
167 E: Send,
168{
169 /// Returns whether the tracked state is terminal.
170 #[inline]
171 fn is_done(&self) -> bool {
172 self.inner.is_done()
173 }
174
175 /// Blocks until the underlying result handle yields a result.
176 #[inline]
177 fn get(self) -> TaskResult<R, E> {
178 self.inner.get()
179 }
180
181 /// Attempts to retrieve the underlying result without blocking.
182 #[inline]
183 fn try_get(self) -> TryGet<Self, R, E> {
184 let Self {
185 inner,
186 cancellation_marker,
187 on_cancelled,
188 } = self;
189 match inner.try_get() {
190 TryGet::Ready(result) => TryGet::Ready(result),
191 TryGet::Pending(inner) => TryGet::Pending(Self {
192 inner,
193 cancellation_marker,
194 on_cancelled,
195 }),
196 }
197 }
198}
199
200impl<R, E> TrackedTaskHandle<R, E> for ScheduledTaskHandle<R, E>
201where
202 R: Send,
203 E: Send,
204{
205 /// Returns the currently observed task status.
206 #[inline]
207 fn status(&self) -> TaskStatus {
208 self.inner.status()
209 }
210
211 /// Attempts to publish a cancellation result while the task is pending.
212 #[inline]
213 fn cancel(&self) -> CancelResult {
214 self.cancel_inner()
215 }
216}
217
218impl<R, E> IntoFuture for ScheduledTaskHandle<R, E> {
219 type Output = TaskResult<R, E>;
220 type IntoFuture = TaskHandleFuture<R, E>;
221
222 /// Converts this scheduled handle into a future resolving to the task result.
223 #[inline]
224 fn into_future(self) -> Self::IntoFuture {
225 self.inner.into_future()
226 }
227}