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}