Skip to main content

dope_fiber/abi/
task.rs

1use core::{pin::Pin, task::Poll};
2
3use crate::{
4    abi,
5    context::{self, PollCall, PollResult},
6    task::storage::slots::{self, Action},
7};
8
9/// Owns a fiber until completion, then releases it and remains terminal.
10#[pin_project::pin_project]
11#[repr(transparent)]
12#[must_use = "a task does nothing unless it is driven"]
13pub struct Task<F> {
14    #[pin]
15    state: slots::raw::Entry<F, ()>,
16}
17
18impl<F> Task<F> {
19    pub const fn new(fiber: F) -> Self {
20        Self {
21            state: slots::raw::Entry::live(fiber),
22        }
23    }
24    pub(crate) fn poll_with<'d>(
25        self: Pin<&mut Self>,
26        poll: impl FnOnce(Pin<&mut F>) -> PollResult<F::Output>,
27    ) -> PollResult<F::Output>
28    where
29        F: abi::Fiber<'d>,
30    {
31        match self
32            .project()
33            .state
34            .visit((), |fiber, ()| match poll(fiber) {
35                result @ PollResult::Polled(Poll::Ready(_)) => Action::Vacate(result),
36                result => Action::Keep(result),
37            }) {
38            slots::Phase::Live(result) => result,
39            slots::Phase::Vacant(()) | slots::Phase::Done(()) => PollResult::Polled(Poll::Pending),
40        }
41    }
42}
43
44impl<'d, F: abi::Fiber<'d>> abi::Fiber<'d> for Task<F> {
45    type Output = F::Output;
46    fn poll(call: PollCall<'_, '_, 'd, Self>) -> Poll<Self::Output> {
47        let (this, context) = call.into_parts();
48        match this.poll_with(|fiber| {
49            // SAFETY: this call owns the sole initial descent into its live slot.
50            PollResult::Polled(unsafe { context::raw::Live::new(fiber).delegate(context) })
51        }) {
52            PollResult::Polled(poll) => poll,
53            PollResult::Exhausted => Poll::Pending,
54        }
55    }
56}