Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it valid to wake a Rust future while it's being polled?

I'd like to be able to sleep my future for a single "frame" so that other work can happen. Is this a valid implementation of this idea?

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

struct Yield {
    yielded: bool,
}

impl Future for Yield {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<()> {
        if self.yielded {
            Poll::Ready(())
        } else {
            self.yielded = true;

            // This is the part I'm concerned about
            ctx.waker().wake_by_ref();

            Poll::Pending
        }
    }
}

Specifically, my concern is that the context won't "notice" the wake_by_ref call if it's made before the poll returns Pending. Does the interface contract of poll make any guarantees about this task being immediately re-polled when executed in this way?

like image 467
Lucretiel Avatar asked Jan 29 '20 19:01

Lucretiel


1 Answers

TL;DR: Your code is valid.

Based on the contract for the waker, it has to poll your future one more time. Otherwise, it is possible to have a race condition between the Future::poll call and the counterpart of the future which actually does some work.

Let's take a look at an example:

impl Future for Foo {
    type Output = ();
    fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<()> {
        let result = communicate_with_worker(ctx); // returns false

        // <-- Time point (1)

        return match result {
            true => Poll::Pending,
            false => Poll::Ready(()),
        };
    }
}

At time point (1), the future has decided that it is not ready, but it's possible that the polling thread is paused here and the worker thread was scheduled and finished its work.

The worker thread will then call the waker and request the future be polled again. If the waker decided to not poll the future again since it's polling the future right now, then the waker will never receive a wake up request again.

This means that the waker may discard wake up requests which come before poll was called, but it's not allowed to discard wake up requests which came during the future's poll call.


The only question I have: why would you like to reschedule polling for one more frame?

Since your actual work has to be done in a separate thread (not inside fn poll) then it doesn't make any sense to reschedule polling.

like image 176
MaxV Avatar answered Oct 21 '22 20:10

MaxV