Search code examples
rustfuturesleep

How to sleep in Future::poll()?


I want to sleep 1 second in poll(), but it gets stuck in Pending. From what I understand, I pass it &mut cx and it should wake up the current task after 1 second.

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::{Duration};

struct MyDelay {}

impl Future for MyDelay {
    type Output = ();
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        println!("poll");
        let sleep = tokio::time::sleep(Duration::from_secs(1));
        tokio::pin!(sleep);
        sleep.poll(cx)
    }
}

#[tokio::main]
async fn main() {
    let delay = MyDelay{};
    let a = delay.await;
    dbg!(a);
}

Play url: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ed6c09df100f1140ddc85b2ae600ab93


Solution

  • Each time you poll you create a new Sleep future, and it starts from scratch.

    Instead, you should store the Sleep inside your future. It is easy with pin-project-lite:

    pin_project_lite::pin_project! {
        struct MyDelay {
            #[pin]
            sleep: tokio::time::Sleep,
        }
    }
    
    impl MyDelay {
        fn new() -> Self {
            Self {
                sleep: tokio::time::sleep(Duration::from_secs(1)),
            }
        }
    }
    
    impl Future for MyDelay {
        type Output = ();
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            println!("poll");
            let this = self.project();
            this.sleep.poll(cx)
        }
    }
    

    Playground.

    Note that the number of times this will be polled is not guaranteed, and may not be constant.