moirai_core/task/future.rs
1use core::future::Future;
2use core::pin::Pin;
3
4use super::id_and_context::TaskContext;
5use super::traits::Task;
6
7/// A future adapter that executes a [`Task`] on first poll.
8///
9/// # Execution semantics
10///
11/// The wrapped task runs **synchronously inside the first `poll` call** on the
12/// polling thread — this adapter does not offload work to an executor, does
13/// not yield mid-task, and never returns `Poll::Pending`. It exists so a
14/// synchronous task can be awaited from async code; the `.await` completes in
15/// one poll, blocking the async worker for the task's full duration. Offload
16/// long-running tasks to a blocking pool instead of awaiting them directly on
17/// an async executor.
18///
19/// # Fused
20///
21/// Like [`core::future::Ready`] and other std-convention one-shot futures,
22/// polling again after completion is a contract violation and panics.
23#[allow(clippy::module_name_repetitions)]
24pub struct TaskFuture<T> {
25 task: Option<T>,
26 context: TaskContext,
27}
28
29impl<T> TaskFuture<T>
30where
31 T: Task,
32{
33 /// Create a new task future.
34 pub fn new(task: T, context: TaskContext) -> Self {
35 Self {
36 task: Some(task),
37 context,
38 }
39 }
40
41 /// Get the task context.
42 pub fn context(&self) -> &TaskContext {
43 &self.context
44 }
45}
46
47impl<T> Future for TaskFuture<T>
48where
49 T: Task + Unpin,
50{
51 type Output = T::Output;
52
53 /// Executes the task synchronously and returns `Poll::Ready` on the first
54 /// call; see the type-level docs for the blocking semantics.
55 ///
56 /// # Panics
57 /// Panics if polled again after it has returned `Poll::Ready` (fused
58 /// one-shot contract; a completed future has no result to hand out and no
59 /// waker-based path by which `Pending` could ever resolve).
60 fn poll(
61 self: Pin<&mut Self>,
62 _cx: &mut core::task::Context<'_>,
63 ) -> core::task::Poll<Self::Output> {
64 let task = self
65 .get_mut()
66 .task
67 .take()
68 .expect("TaskFuture polled after completion");
69 core::task::Poll::Ready(task.execute())
70 }
71}