Skip to main content

qubit_executor/task/
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 oneshot::{
11    Receiver,
12    TryRecvError,
13};
14use std::{
15    future::IntoFuture,
16    sync::Arc,
17};
18
19use super::{
20    TaskExecutionError,
21    TaskResult,
22    task_handle_future::TaskHandleFuture,
23    task_result_handle::TaskResultHandle,
24    task_state::TaskState,
25    try_get::TryGet,
26};
27use crate::hook::TaskId;
28
29/// Lightweight result handle for a submitted callable task.
30///
31/// `TaskHandle` owns the receiving endpoint for exactly one task result. It can
32/// block through [`Self::get`], poll non-blockingly through [`Self::try_get`],
33/// or be awaited by value.
34pub struct TaskHandle<R, E> {
35    /// Shared task state used for status and cancellation.
36    pub(crate) state: Arc<TaskState<R, E>>,
37    /// One-shot receiver for the final task result.
38    receiver: Receiver<TaskResult<R, E>>,
39}
40
41impl<R, E> TaskHandle<R, E> {
42    /// Creates a task handle from a one-shot result receiver.
43    ///
44    /// # Parameters
45    ///
46    /// * `receiver` - Receiver that yields the final task result.
47    ///
48    /// # Returns
49    ///
50    /// A task result handle.
51    #[inline]
52    pub(crate) const fn new(state: Arc<TaskState<R, E>>, receiver: Receiver<TaskResult<R, E>>) -> Self {
53        Self { state, receiver }
54    }
55
56    /// Returns the identifier assigned to this task.
57    ///
58    /// # Returns
59    ///
60    /// The task id stored in the shared task state.
61    #[inline]
62    pub fn task_id(&self) -> TaskId {
63        self.state.task_id
64    }
65
66    /// Marks the task accepted and emits the accepted hook once.
67    #[inline]
68    pub(crate) fn accept(&self) {
69        let _accepted_now = self.state.accept();
70    }
71
72    /// Waits for the task to finish and returns its final result.
73    ///
74    /// This method blocks the current thread until a result is available.
75    ///
76    /// # Returns
77    ///
78    /// `Ok(R)` if the task succeeds. If the accepted task returns `Err(E)`,
79    /// panics, is cancelled, or loses its completion endpoint before producing
80    /// a value, the corresponding [`crate::TaskExecutionError`] is returned.
81    #[inline]
82    pub fn get(self) -> TaskResult<R, E> {
83        self.receiver.recv().unwrap_or(Err(TaskExecutionError::Dropped))
84    }
85
86    /// Attempts to retrieve the final result without blocking.
87    ///
88    /// # Returns
89    ///
90    /// [`TryGet::Ready`] with the final result when available, otherwise
91    /// [`TryGet::Pending`] containing this handle.
92    #[inline]
93    pub fn try_get(self) -> TryGet<Self, R, E> {
94        let Self { state, receiver } = self;
95        match receiver.try_recv() {
96            Ok(result) => TryGet::Ready(result),
97            Err(TryRecvError::Empty) => TryGet::Pending(Self { state, receiver }),
98            Err(TryRecvError::Disconnected) => TryGet::Ready(Err(TaskExecutionError::Dropped)),
99        }
100    }
101
102    /// Returns whether the task has installed a terminal state.
103    ///
104    /// # Returns
105    ///
106    /// `true` after the task succeeds, fails, panics, is cancelled, or loses
107    /// its completion endpoint. The final result send may still be racing with
108    /// this status observation.
109    #[inline]
110    pub fn is_done(&self) -> bool {
111        self.state.status().is_done()
112    }
113}
114
115impl<R, E> TaskResultHandle<R, E> for TaskHandle<R, E>
116where
117    R: Send,
118    E: Send,
119{
120    /// Returns whether the tracked task state is terminal.
121    #[inline]
122    fn is_done(&self) -> bool {
123        Self::is_done(self)
124    }
125
126    /// Blocks until the result channel yields a task result.
127    #[inline]
128    fn get(self) -> TaskResult<R, E> {
129        Self::get(self)
130    }
131
132    /// Attempts to read the result channel without blocking.
133    #[inline]
134    fn try_get(self) -> TryGet<Self, R, E> {
135        Self::try_get(self)
136    }
137}
138
139impl<R, E> IntoFuture for TaskHandle<R, E> {
140    type Output = TaskResult<R, E>;
141    type IntoFuture = TaskHandleFuture<R, E>;
142
143    /// Converts this handle into a future resolving to the task result.
144    #[inline]
145    fn into_future(self) -> Self::IntoFuture {
146        TaskHandleFuture::new(IntoFuture::into_future(self.receiver))
147    }
148}