use std::{
future::Future,
pin::{pin, Pin},
task::{Context, Poll},
};
use futures::task;
pub fn yield_once() -> YieldOnce {
YieldOnce::default()
}
#[derive(Default)]
pub struct YieldOnce {
yielded: bool,
}
impl Future for YieldOnce {
type Output = ();
fn poll(mut self: Pin<&mut Self>, context: &mut Context) -> Poll<Self::Output> {
let mut this = self.as_mut();
if this.yielded {
Poll::Ready(())
} else {
this.yielded = true;
context.waker().wake_by_ref();
Poll::Pending
}
}
}
pub trait BlockingWait {
type Output;
fn blocking_wait(self) -> Self::Output;
}
impl<AnyFuture> BlockingWait for AnyFuture
where
AnyFuture: Future,
{
type Output = AnyFuture::Output;
fn blocking_wait(mut self) -> Self::Output {
let waker = task::noop_waker();
let mut task_context = Context::from_waker(&waker);
let mut future = pin!(self);
loop {
match future.as_mut().poll(&mut task_context) {
Poll::Pending => continue,
Poll::Ready(output) => return output,
}
}
}
}
#[cfg(test)]
mod tests {
use std::task::{Context, Poll};
use futures::{future::poll_fn, task::noop_waker, FutureExt as _};
use super::{yield_once, BlockingWait};
#[test]
#[expect(clippy::bool_assert_comparison)]
fn yield_once_returns_pending_only_on_first_call() {
let mut future = yield_once();
let waker = noop_waker();
let mut context = Context::from_waker(&waker);
assert_eq!(future.yielded, false);
assert!(future.poll_unpin(&mut context).is_pending());
assert_eq!(future.yielded, true);
assert!(future.poll_unpin(&mut context).is_ready());
assert_eq!(future.yielded, true);
}
#[test]
fn blocking_wait_blocks_until_future_is_ready() {
let mut remaining_polls = 100;
let future = poll_fn(|_context| {
if remaining_polls == 0 {
Poll::Ready(())
} else {
remaining_polls -= 1;
Poll::Pending
}
});
future.blocking_wait();
assert_eq!(remaining_polls, 0);
}
}