In this example:
use std::task::{Context, Poll, Waker};
use std::pin::Pin;
use std::future::Future;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
/// A future that completes after a delay (toy implementation)
struct Delay {
completed: Arc<Mutex<bool>>,
waker_stored: Arc<Mutex<Option<Waker>>>,
duration: Duration,
started: bool,
}
impl Delay {
fn new(duration: Duration) -> Self {
Delay {
completed: Arc::new(Mutex::new(false)),
waker_stored: Arc::new(Mutex::new(None)),
duration,
started: false,
}
}
}
impl Future for Delay {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
// Check if already completed
if *self.completed.lock().unwrap() {
return Poll::Ready(());
}
// Store the waker so the background thread can wake us
*self.waker_stored.lock().unwrap() = Some(cx.waker().clone()); // #1
// Start the background timer on first poll
if !self.started {
self.started = true;
let completed = Arc::clone(&self.completed);
let waker = Arc::clone(&self.waker_stored);
let duration = self.duration;
thread::spawn(move || {
thread::sleep(duration);
*completed.lock().unwrap() = true;
// CRITICAL: wake the executor so it polls us again
if let Some(w) = waker.lock().unwrap().take() { // #2
w.wake(); // "Hey executor, I'm ready — poll me again!"
}
});
}
Poll::Pending // Not done yet
}
}
According to the document of will_wake, the executor can give the new waker such that the saved old waker cannot correctly wake the future. For example, #2 happens-before #1 in the next invocation to poll that passed in a new waker, this would make w.wake() after #2 failure.
For this case, we need to double-check completed at the end of poll.
In this example:
According to the document of will_wake, the executor can give the new
wakersuch that the saved oldwakercannot correctly wake the future. For example,#2happens-before#1in the next invocation topollthat passed in a newwaker, this would makew.wake()after#2failure.For this case, we need to double-check
completedat the end ofpoll.