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(
53        state: Arc<TaskState<R, E>>,
54        receiver: Receiver<TaskResult<R, E>>,
55    ) -> Self {
56        Self { state, receiver }
57    }
58
59    /// Returns the identifier assigned to this task.
60    ///
61    /// # Returns
62    ///
63    /// The task id stored in the shared task state.
64    #[inline]
65    pub fn task_id(&self) -> TaskId {
66        self.state.task_id
67    }
68
69    /// Marks the task accepted and emits the accepted hook once.
70    #[inline]
71    pub(crate) fn accept(&self) {
72        let _accepted_now = self.state.accept();
73    }
74
75    /// Waits for the task to finish and returns its final result.
76    ///
77    /// This method blocks the current thread until a result is available.
78    ///
79    /// # Returns
80    ///
81    /// `Ok(R)` if the task succeeds. If the accepted task returns `Err(E)`,
82    /// panics, is cancelled, or loses its completion endpoint before producing
83    /// a value, the corresponding [`crate::TaskExecutionError`] is returned.
84    #[inline]
85    pub fn get(self) -> TaskResult<R, E> {
86        self.receiver
87            .recv()
88            .unwrap_or(Err(TaskExecutionError::Dropped))
89    }
90
91    /// Attempts to retrieve the final result without blocking.
92    ///
93    /// # Returns
94    ///
95    /// [`TryGet::Ready`] with the final result when available, otherwise
96    /// [`TryGet::Pending`] containing this handle.
97    #[inline]
98    pub fn try_get(self) -> TryGet<Self, R, E> {
99        let Self { state, receiver } = self;
100        match receiver.try_recv() {
101            Ok(result) => TryGet::Ready(result),
102            Err(TryRecvError::Empty) => TryGet::Pending(Self { state, receiver }),
103            Err(TryRecvError::Disconnected) => TryGet::Ready(Err(TaskExecutionError::Dropped)),
104        }
105    }
106
107    /// Returns whether the task has installed a terminal state.
108    ///
109    /// # Returns
110    ///
111    /// `true` after the task succeeds, fails, panics, is cancelled, or loses
112    /// its completion endpoint. The final result send may still be racing with
113    /// this status observation.
114    #[inline]
115    pub fn is_done(&self) -> bool {
116        self.state.status().is_done()
117    }
118}
119
120impl<R, E> TaskResultHandle<R, E> for TaskHandle<R, E>
121where
122    R: Send,
123    E: Send,
124{
125    /// Returns whether the tracked task state is terminal.
126    #[inline]
127    fn is_done(&self) -> bool {
128        Self::is_done(self)
129    }
130
131    /// Blocks until the result channel yields a task result.
132    #[inline]
133    fn get(self) -> TaskResult<R, E> {
134        Self::get(self)
135    }
136
137    /// Attempts to read the result channel without blocking.
138    #[inline]
139    fn try_get(self) -> TryGet<Self, R, E> {
140        Self::try_get(self)
141    }
142}
143
144impl<R, E> IntoFuture for TaskHandle<R, E> {
145    type Output = TaskResult<R, E>;
146    type IntoFuture = TaskHandleFuture<R, E>;
147
148    /// Converts this handle into a future resolving to the task result.
149    #[inline]
150    fn into_future(self) -> Self::IntoFuture {
151        TaskHandleFuture::new(IntoFuture::into_future(self.receiver))
152    }
153}