use core::future::Future;
use core::{fmt, task, time, mem};
use core::pin::Pin;
use crate::timer::Timer;
use crate::timer::Platform as PlatformTimer;
#[must_use = "Timed does nothing unless polled"]
pub enum Timed<F, T=PlatformTimer> {
#[doc(hidden)]
Ongoing(T, F, time::Duration),
#[doc(hidden)]
Stopped,
}
impl<F: Future + Unpin> Timed<F> {
#[inline]
pub fn platform_new(inner: F, timeout: time::Duration) -> Self {
Timed::<F, PlatformTimer>::new(inner, timeout)
}
}
impl<F: Future> Timed<F> {
#[inline]
pub unsafe fn platform_new_unchecked(inner: F, timeout: time::Duration) -> Self {
Timed::<F, PlatformTimer>::new_unchecked(inner, timeout)
}
}
impl<F: Future + Unpin, T: Timer> Timed<F, T> {
pub fn new(inner: F, timeout: time::Duration) -> Self {
Timed::Ongoing(T::new(timeout), inner, timeout)
}
}
impl<F: Future, T: Timer> Timed<F, T> {
pub unsafe fn new_unchecked(inner: F, timeout: time::Duration) -> Self {
Timed::Ongoing(T::new(timeout), inner, timeout)
}
}
impl<F: Future, T: Timer> Future for Timed<F, T> {
type Output = Result<F::Output, Expired<F, T>>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
let mut state = Timed::Stopped;
let mut this = unsafe { self.get_unchecked_mut() };
mem::swap(&mut state, &mut this);
match state {
Timed::Ongoing(mut timer, mut future, timeout) => {
match Future::poll(unsafe { Pin::new_unchecked(&mut future) }, ctx) {
task::Poll::Pending => (),
task::Poll::Ready(result) => return task::Poll::Ready(Ok(result)),
}
match Future::poll(Pin::new(&mut timer), ctx) {
task::Poll::Pending => (),
task::Poll::Ready(_) => return task::Poll::Ready(Err(Expired {
inner: Timed::Ongoing(timer, future, timeout),
})),
}
*this = Timed::Ongoing(timer, future, timeout);
task::Poll::Pending
},
Timed::Stopped => task::Poll::Pending,
}
}
}
impl<F: Future + Unpin, T: Timer> Unpin for Timed<F, T> {}
pub struct Expired<F, T> {
inner: Timed<F, T>,
}
impl<F: Future, T: Timer> Expired<F, T> {
pub fn into_inner(self) -> F {
match self.inner {
Timed::Ongoing(_, fut, _) => fut,
_ => unreach!(),
}
}
}
impl<F: Future, T: Timer> Future for Expired<F, T> {
type Output = Timed<F, T>;
fn poll(self: Pin<&mut Self>, _: &mut task::Context) -> task::Poll<Self::Output> {
let mut state = Timed::Stopped;
let this = unsafe { self.get_unchecked_mut() };
mem::swap(&mut this.inner, &mut state);
match state {
Timed::Ongoing(mut timer, future, timeout) => {
timer.restart(timeout);
task::Poll::Ready(Timed::Ongoing(timer, future, timeout))
},
_ => task::Poll::Pending,
}
}
}
impl<F: Future + Unpin, T: Timer> Unpin for Expired<F, T> {}
#[cfg(feature = "std")]
impl<F, T: Timer> crate::std::error::Error for Expired<F, T> {}
impl<F, T: Timer> fmt::Debug for Expired<F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self)
}
}
impl<F, T: Timer> fmt::Display for Expired<F, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner {
Timed::Stopped => write!(f, "Future is being re-tried."),
Timed::Ongoing(_, _, timeout) => match timeout.as_secs() {
0 => write!(f, "Future expired in {} ms", timeout.as_millis()),
secs => write!(f, "Future expired in {} seconds and {} ms", secs, timeout.subsec_millis()),
},
}
}
}