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}