use std::{
future::Future,
panic::Location,
pin::Pin,
sync::Arc,
task::{Context, Poll, Waker},
};
use pin_project_lite::pin_project;
use super::system::{self, credit, gate::TaskGate};
pin_project! {
pub struct Participating<F> {
#[pin]
fut: F,
gate: Arc<TaskGate>,
}
impl<F> PinnedDrop for Participating<F> {
fn drop(this: Pin<&mut Self>) {
this.gate.on_drop();
}
}
}
impl<F: Future> Future for Participating<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
let this = self.project();
this.gate.store_runtime_waker(cx.waker());
if !this.gate.try_enter_poll() {
return Poll::Pending;
}
let gate_waker = Waker::from(Arc::clone(this.gate));
let mut gate_cx = Context::from_waker(&gate_waker);
let outcome = {
let _poll_guard = credit::AsyncPollGuard::enter(this.gate.id(), this.gate.loc());
this.fut.poll(&mut gate_cx)
};
match outcome {
Poll::Ready(out) => {
this.gate.complete();
Poll::Ready(out)
}
Poll::Pending => {
this.gate.park();
Poll::Pending
}
}
}
}
pub fn participate<F: Future>(fut: F, loc: &'static Location<'static>) -> Participating<F> {
let id = system::async_acquire(loc);
Participating {
fut,
gate: TaskGate::new(id, loc),
}
}