86 lines
2.8 KiB
Rust
86 lines
2.8 KiB
Rust
use std::{
|
|
future::Future,
|
|
pin::Pin,
|
|
sync::{Arc, Mutex},
|
|
task::{Context, Poll, Waker},
|
|
thread,
|
|
time::Duration,
|
|
sync::mpsc::{sync_channel, Receiver, SyncSender},
|
|
};
|
|
use futures::{
|
|
future::{BoxFuture, FutureExt},
|
|
task::{waker_ref, ArcWake},
|
|
};
|
|
|
|
pub struct TimerFuture {
|
|
shared_state: Arc<Mutex<SharedState>>,
|
|
}
|
|
|
|
/// Shared state between the future and the waiting thread
|
|
struct SharedState {
|
|
/// If the sleep time has elapsed
|
|
completed: bool,
|
|
|
|
/// The waker for the task that `TimerFuture` is running on.
|
|
/// The thread can use this after setting `completed = true` to tell
|
|
/// `TimerFuture`'s task to wake up, see that `completed = true`, and
|
|
/// move forward.
|
|
waker: Option<Waker>,
|
|
}
|
|
|
|
impl Future for TimerFuture {
|
|
type Output = ();
|
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
// Look at the shared state to see if the timer has already completed.
|
|
let mut shared_state = self.shared_state.lock().unwrap();
|
|
if shared_state.completed {
|
|
Poll::Ready(())
|
|
} else {
|
|
// Set waker so that the thread can wake up the current task
|
|
// when the timer has completed, ensuring that the future is polled
|
|
// again and sees that `completed = true`.
|
|
//
|
|
// It's tempting to do this once rather than repeatedly cloning
|
|
// the waker each time. However, the `TimerFuture` can move between
|
|
// tasks on the executor, which could cause a stale waker pointing
|
|
// to the wrong task, preventing `TimerFuture` from waking up
|
|
// correctly.
|
|
//
|
|
// N.B. it's possible to check for this using the `Waker::will_wake`
|
|
// function, but we omit that here to keep things simple.
|
|
shared_state.waker = Some(cx.waker().clone());
|
|
Poll::Pending
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TimerFuture {
|
|
/// Create a new `TimerFuture` which will complete after the provided
|
|
/// timeout.
|
|
pub fn new(duration: Duration) -> Self {
|
|
let shared_state = Arc::new(Mutex::new(SharedState {
|
|
completed: false,
|
|
waker: None,
|
|
}));
|
|
|
|
// Spawn the new thread
|
|
let thread_shared_state = shared_state.clone();
|
|
thread::spawn(move || {
|
|
thread::sleep(duration);
|
|
let mut shared_state = thread_shared_state.lock().unwrap();
|
|
// Signal that the timer has completed and wake up the last
|
|
// task on which the future was polled, if one exists.
|
|
shared_state.completed = true;
|
|
if let Some(waker) = shared_state.waker.take() {
|
|
waker.wake()
|
|
}
|
|
});
|
|
|
|
TimerFuture { shared_state }
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
println!("Hello, world!");
|
|
}
|