Skip to main content

moirai_async/executor/
handle.rs

1use crate::executor::result_slot::AsyncResultSlot;
2use moirai_core::TaskId;
3use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6use std::task::{Context, Poll};
7
8/// A handle to an async task that can be awaited.
9pub struct AsyncHandle<T> {
10    pub(super) task_id: TaskId,
11    pub(super) result_slot: Arc<AsyncResultSlot<T>>,
12}
13
14impl<T> AsyncHandle<T> {
15    /// Return the executor-assigned task identifier.
16    #[must_use]
17    pub fn id(&self) -> TaskId {
18        self.task_id
19    }
20}
21
22impl<T> Future for AsyncHandle<T> {
23    type Output = T;
24
25    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
26        if let Some(value) = self.result_slot.try_take_ready() {
27            return Poll::Ready(value);
28        }
29
30        // SAFETY: `poll` holds `Pin<&mut Self>`, and the handle is the slot's only
31        // consumer (the executor side only completes), so no other registration
32        // can run.
33        unsafe { self.result_slot.register_waker(cx.waker()) };
34
35        // Re-check is mandatory, not defensive: a `complete` that raced in on the
36        // slot's PENDING->WRITING path does not wake (no waker was registered when
37        // it ran), so this second take is what closes the lost-wakeup window. See
38        // the `result_slot` module docs.
39
40        self.result_slot
41            .try_take_ready()
42            .map_or(Poll::Pending, Poll::Ready)
43    }
44}