Typically, Rust compiles asynchronous functions into state machines. However, each time this state machine is polled, it needs to be invoked from the top level sequentially, which results in poor performance in certain continuous polling implementations of async functions.
(This might be considered an anti-pattern, but can be useful in some embedded development scenarios.)
For example:
pub async fn next_poll() {
let mut once = true;
core::future::poll_fn(|cx| {
if core::mem::take(&mut once) {
cx.waker().wake_by_ref();
Poll::Pending
} else {
Poll::Ready(())
}
})
.await
}
There are some attempts, Such as: https://internals.rust-lang.org/t/pre-rfc-cps-transform-for-generators/7120
Can we enable the async runtime to have a fast-path for advancing the state machine's leaf nodes after encountering Pending
by erasing the return value?
use std::pin::Pin;
use std::task::{Context, Poll};
pub trait Block {
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool;
}
pub struct FutureBlock<F: Future> {
future: F,
ready: Option<F::Output>,
}
impl<F: Future> FutureBlock<F> {
pub fn future(self: Pin<&mut Self>) -> Pin<&mut F> {
unsafe { self.map_unchecked_mut(|s| &mut s.future) }
}
pub fn ready(self: Pin<&mut Self>) -> &mut Option<F::Output> {
unsafe { &mut self.get_unchecked_mut().ready }
}
}
impl<F: Future> Block for FutureBlock<F> {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool {
if self.ready.is_some() {
true
} else {
unsafe {
match self.as_mut().future().poll(cx) {
Poll::Ready(ready) => {
*self.ready() = Some(ready);
true
}
Poll::Pending => false,
}
}
}
}
}
impl<F: Future> Future for FutureBlock<F> {
type Output = F::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(ready) = self.as_mut().ready().take() {
Poll::Ready(ready)
} else {
self.future().poll(cx)
}
}
fn get_block(self: Pin<&mut Self>) -> Option<Pin<&mut dyn Block>> {
Some(self)
}
}
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
fn get_block(self: Pin<&mut Self>) -> Option<Pin<&mut dyn Block>> {
None
}
}
impl<P> Future for Pin<P>
where
P: std::ops::DerefMut<Target: std::future::Future>,
{
type Output = <<P as std::ops::Deref>::Target as std::future::Future>::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
<P::Target as std::future::Future>::poll(self.as_deref_mut(), cx)
}
fn get_block(self: Pin<&mut Self>) -> Option<Pin<&mut dyn Block>> {
None
}
}