Reputation: 881
I want to add sleep to avoid busy loop in Future::poll(), expect the finally effect is that runtime call poll() every second, the fifth second will return Poll::Ready(()).
According to my understanding, poll() is be called in first, this.sleeper.poll(cx) will return Poll::Pending immediately, cx.waker().wake_by_ref() will make current task to execute again immediately.
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use pin_project_lite::pin_project;
use std::time::{Duration, Instant};
pin_project! {
struct Delay {
when: Instant,
#[pin]
sleeper: tokio::time::Sleep,
}
}
impl Delay {
pub fn new(when: Instant) -> Self {
Self {
when,
sleeper: tokio::time::sleep(Duration::from_secs(1))
}
}
}
impl Future for Delay {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.when < Instant::now() {
Poll::Ready(())
} else {
let this = self.project();
// I want to add sleep to avoid busy loop.
this.sleeper.poll(cx);
// here will make CPU busy loop, uninterrupted call poll()
cx.waker().wake_by_ref();
// expect five "1", but will get many "1"
print!("1");
Poll::Pending
}
}
}
#[tokio::main]
async fn main() {
let when = Instant::now() + Duration::from_secs(5);
let delay1 = Delay::new(when);
delay1.await;
println!("Hello, world!");
}
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=030fbade7a2f1e5df50c4774fc06fdf1
Upvotes: 0
Views: 932
Reputation: 881
finally, I resoved this problem by changing Sleep to Interval and call poll_tick().
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use pin_project_lite::pin_project;
use std::time::{Duration, Instant};
use tokio::time::Sleep;
pin_project! {
struct Delay {
when: Instant,
#[pin]
sleeper: tokio::time::Interval,
}
}
impl Delay {
pub fn new(when: Instant) -> Self {
Self {
when,
sleeper: tokio::time::interval(Duration::from_secs(1))
}
}
}
impl Future for Delay {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
println!("0");
if self.when < Instant::now() {
println!("1");
Poll::Ready(())
} else {
println!("2");
let this = unsafe {self.get_unchecked_mut()};
//let this = self.project();
this.sleeper.poll_tick(cx);
Poll::Pending
}
}
}
#[tokio::main]
async fn main() {
let when = Instant::now() + Duration::from_secs(5);
let delay1 = Delay::new(when);
delay1.await;
}
Upvotes: 0
Reputation: 43743
There are two problems here. First and simplest, when your future is delegating to another future in poll()
, you do not need to call the waker at all — the other future will do that. In this case, the Sleeper
takes care of getting the waker called at the right time, causing your future to be polled again.
Second, you're ignoring the return value of the sleeper.poll()
. If it returns Poll::Ready
, you need to make sure you don't poll it again — either do something else, or replace self.sleeper
with a new sleep()
. Polling a future again after it returned Poll::Ready
is not meaningful, and has consequences left up to the future (often a panic).
Upvotes: 3