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};
7
8/// Owns a fiber until completion, then releases it and remains terminal.
9#[pin_project::pin_project]
10#[repr(transparent)]
11#[must_use = "a task does nothing unless it is driven"]
12pub struct Task<F> {
13    #[pin]
14    state: Option<F>,
15}
16
17impl<F> Task<F> {
18    pub const fn new(fiber: F) -> Self {
19        Self { state: Some(fiber) }
20    }
21    pub(crate) fn poll_with<'d>(
22        self: Pin<&mut Self>,
23        poll: impl FnOnce(Pin<&mut F>) -> PollResult<F::Output>,
24    ) -> PollResult<F::Output>
25    where
26        F: abi::Fiber<'d>,
27    {
28        let mut state = self.project().state;
29        let Some(fiber) = state.as_mut().as_pin_mut() else {
30            return PollResult::Polled(Poll::Pending);
31        };
32        let result = poll(fiber);
33        if matches!(result, PollResult::Polled(Poll::Ready(_))) {
34            state.set(None);
35        }
36        result
37    }
38}
39
40impl<'d, F: abi::Fiber<'d>> abi::Fiber<'d> for Task<F> {
41    type Output = F::Output;
42    fn poll(call: PollCall<'_, '_, 'd, Self>) -> Poll<Self::Output> {
43        let (this, context) = call.into_parts();
44        match this.poll_with(|fiber| {
45            // SAFETY: this call owns the sole initial descent into its live slot.
46            PollResult::Polled(unsafe { context::raw::Live::new(fiber).delegate(context) })
47        }) {
48            PollResult::Polled(poll) => poll,
49            PollResult::Exhausted => Poll::Pending,
50        }
51    }
52}