use std::{
future::{Future, poll_fn},
io::ErrorKind,
pin::pin,
task::Poll,
time::Duration,
};
use super::{Runtime, traits};
use crate::{Error, Result};
impl Runtime {
pub(crate) async fn sleep(&self, duration: Duration) {
match self {
#[cfg(feature = "default-rt")]
Self::ZRuntime(runtime) => traits::Runtime::sleep(runtime, duration).await,
#[cfg(feature = "tokio")]
Self::Tokio(runtime) => traits::Runtime::sleep(runtime, duration).await,
Self::External(runtime) => traits::Runtime::sleep(runtime, duration).await,
}
}
pub(crate) async fn timeout<F, T>(&self, fut: F, duration: Duration) -> Result<T>
where
F: Future<Output = Result<T>>,
{
let mut fut = pin!(fut);
let mut sleep = pin!(self.sleep(duration));
poll_fn(|cx| {
if let Poll::Ready(output) = fut.as_mut().poll(cx) {
return Poll::Ready(output);
}
sleep.as_mut().poll(cx).map(|()| {
Err(Error::from(std::io::Error::new(
ErrorKind::TimedOut,
"timed out",
)))
})
})
.await
}
}
#[cfg(all(test, feature = "tokio"))]
mod tests {
use std::pin::pin;
use futures_util::poll;
use ntest::timeout;
use super::*;
use crate::runtime::Tokio;
#[tokio::test(start_paused = true)]
#[timeout(15000)]
async fn a_tokio_timeout_expires_on_the_tokio_clock() {
tokio::time::advance(Duration::from_secs(60)).await;
let runtime = Runtime::Tokio(Tokio::current().expect("a Tokio runtime is current"));
let mut timing_out =
pin!(runtime.timeout(std::future::pending::<Result<()>>(), Duration::from_secs(1)));
assert!(poll!(timing_out.as_mut()).is_pending());
tokio::time::advance(Duration::from_millis(500)).await;
assert!(poll!(timing_out.as_mut()).is_pending());
tokio::time::advance(Duration::from_millis(600)).await;
let error = timing_out.await.unwrap_err();
assert!(
matches!(&error, Error::InputOutput(e) if e.kind() == ErrorKind::TimedOut),
"got {error:?}",
);
}
}