// inside Future#poll
if !w.will_wake(waker) {
*w = waker.clone()
}
... where w is a waiter set or waiter field store a waker.
I can hardly understand how it can happen since it looks like a future gets polled by different context. How can it happen in the real world? And if we just drop the previous waker, how can we ensure that there is no dangling waiter?
Remember that futures are not just owned by executors, but are also owned by other futures. Those futures may poll the child future for a while and then hand off ownership elsewhere if they wish to.
“Different runtimes” isn’t really the thing. The choice of Waker is up to the owner of the future, which may not be a “runtime”. That said, here is something short to demonstrate the possibility:
use futures::task::noop_waker_ref;
use std::future::Future as _;
use std::task;
async fn foo() {
// some work here
}
fn bar() {
let mut f = Box::pin(foo());
match f
.as_mut()
.poll(&mut task::Context::from_waker(noop_waker_ref()))
{
task::Poll::Ready(value) => {
// do something with the value that is immediately available
}
task::Poll::Pending => {
let handle = tokio::spawn(f);
// do something with the handle for later
}
}
}
This might be done in order to more efficiently handle the case where foo is immediately ready, in some particular way. (It would not be a good idea to have different behavior other than scheduling.)
In this case, the foo future is first polled with the noop waker and then later polled with a Tokio task waker.