use std::{fmt, future::Future, time::Duration};
use crate::{
executor::{select2, Either},
reactor::REACTOR,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Elapsed;
impl fmt::Display for Elapsed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("deadline has elapsed")
}
}
impl std::error::Error for Elapsed {}
pub async fn sleep(duration: Duration) {
if duration.is_zero() {
crate::executor::yield_now().await;
return;
}
if let Ok(op) = REACTOR.with(|r| r.borrow_mut().sleep(duration)) {
let _ = op.await;
}
}
pub async fn timeout<F>(duration: Duration, future: F) -> Result<F::Output, Elapsed>
where
F: Future,
{
match select2(future, sleep(duration)).await {
Either::Left(output) => Ok(output),
Either::Right(()) => Err(Elapsed),
}
}
pub struct Interval {
period: Duration,
}
impl Interval {
pub async fn tick(&mut self) {
sleep(self.period).await;
}
pub fn period(&self) -> Duration {
self.period
}
}
pub fn interval(period: Duration) -> Interval {
Interval { period }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::executor::block_on;
use std::time::Instant;
#[test]
fn test_sleep_duration() {
block_on(async {
let start = Instant::now();
sleep(Duration::from_millis(15)).await;
assert!(start.elapsed() >= Duration::from_millis(10));
});
}
#[test]
fn test_timeout_completes_before_deadline() {
block_on(async {
let res = timeout(Duration::from_millis(200), async {
sleep(Duration::from_millis(2)).await;
42
})
.await;
assert_eq!(res, Ok(42));
});
}
#[test]
fn test_timeout_elapsed_drops_inner() {
block_on(async {
let res = timeout(Duration::from_millis(10), async {
sleep(Duration::from_millis(500)).await;
99
})
.await;
assert_eq!(res, Err(Elapsed));
});
}
#[test]
fn test_interval_ticks() {
block_on(async {
let mut iv = interval(Duration::from_millis(5));
let start = Instant::now();
iv.tick().await;
iv.tick().await;
assert!(start.elapsed() >= Duration::from_millis(5));
});
}
}