use crate::time::{Duration, Instant};
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use gloo_timers::callback::Timeout;
#[derive(Debug)]
struct TimerState {
fired: bool,
waker: Option<std::task::Waker>,
}
#[derive(Debug)]
pub struct Timer {
timer: Option<Timeout>,
duration: Duration,
state: Arc<Mutex<TimerState>>,
}
impl Timer {
pub fn after(duration: Duration) -> Timer {
Timer {
timer: None,
duration,
state: Arc::new(Mutex::new(TimerState {
fired: false,
waker: None,
})),
}
}
pub fn set_after(&mut self, duration: Duration) {
self.duration = duration;
self.timer = None;
let mut state = self.state.lock().unwrap();
state.fired = false;
state.waker = None;
}
pub fn at(instant: Instant) -> Timer {
Timer {
timer: None,
duration: instant.duration_since(*Instant::now()).into(),
state: Arc::new(Mutex::new(TimerState {
fired: false,
waker: None,
})), }
}
}
impl Future for Timer {
type Output = Instant;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let mut state = this.state.lock().unwrap();
if state.fired {
return Poll::Ready(Instant::now());
}
if this.timer.is_none() {
let state_clone = this.state.clone();
let timeout = Timeout::new(this.duration.as_millis() as u32, move || {
let mut state = state_clone.lock().unwrap();
state.fired = true;
if let Some(waker) = state.waker.take() {
waker.wake();
}
});
this.timer = Some(timeout);
}
state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
impl Drop for Timer {
fn drop(&mut self) {
if let Some(timeout) = self.timer.take() {
drop(timeout);
}
}
}