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 fn sleep(duration: Duration) -> Sleep {
if duration.is_zero() {
return Sleep {
deadline: std::time::Instant::now(),
handle: None,
yield_once: true,
};
}
sleep_until(std::time::Instant::now() + duration)
}
pub fn sleep_until(deadline: std::time::Instant) -> Sleep {
Sleep {
deadline,
handle: None,
yield_once: false,
}
}
pub struct Sleep {
deadline: std::time::Instant,
handle: Option<(std::time::Instant, u64)>,
yield_once: bool,
}
impl Sleep {
pub fn deadline(&self) -> std::time::Instant {
self.deadline
}
pub fn is_elapsed(&self) -> bool {
std::time::Instant::now() >= self.deadline
}
pub fn reset(&mut self, deadline: std::time::Instant) {
if let Some(h) = self.handle.take() {
let _ = REACTOR.try_with(|r| {
if let Ok(mut r) = r.try_borrow_mut() {
r.cancel_timer(h);
}
});
}
self.deadline = deadline;
self.yield_once = false;
}
}
impl Future for Sleep {
type Output = ();
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<()> {
if self.yield_once {
self.yield_once = false;
cx.waker().wake_by_ref();
return std::task::Poll::Pending;
}
if std::time::Instant::now() >= self.deadline {
return std::task::Poll::Ready(());
}
let deadline = self.deadline;
let existing = self.handle;
let handle = REACTOR.with(|r| {
let mut r = r.borrow_mut();
match existing {
Some(h) => {
r.update_timer(h, cx.waker());
h
}
None => r.add_timer(deadline, cx.waker().clone()),
}
});
self.handle = Some(handle);
std::task::Poll::Pending
}
}
impl Drop for Sleep {
fn drop(&mut self) {
if let Some(h) = self.handle {
let _ = REACTOR.try_with(|r| {
if let Ok(mut r) = r.try_borrow_mut() {
r.cancel_timer(h);
}
});
}
}
}
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 async fn timeout_at<F>(deadline: std::time::Instant, future: F) -> Result<F::Output, Elapsed>
where
F: Future,
{
match select2(future, sleep_until(deadline)).await {
Either::Left(output) => Ok(output),
Either::Right(()) => Err(Elapsed),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MissedTickBehavior {
Burst,
Delay,
#[default]
Skip,
}
pub struct Interval {
period: Duration,
next_tick: std::time::Instant,
missed_tick_behavior: MissedTickBehavior,
}
impl Interval {
pub async fn tick(&mut self) {
let now = std::time::Instant::now();
if self.next_tick > now {
sleep_until(self.next_tick).await;
} else {
crate::executor::yield_now().await;
}
let after = std::time::Instant::now();
match self.missed_tick_behavior {
MissedTickBehavior::Burst => {
self.next_tick += self.period;
}
MissedTickBehavior::Delay => {
self.next_tick = after + self.period;
}
MissedTickBehavior::Skip => {
self.next_tick += self.period;
if self.next_tick <= after {
let missed = (after.duration_since(self.next_tick).as_nanos()
/ self.period.as_nanos().max(1)) as u32
+ 1;
self.next_tick += self.period * missed;
}
}
}
}
pub fn reset(&mut self) {
self.next_tick = std::time::Instant::now() + self.period;
}
pub fn period(&self) -> Duration {
self.period
}
pub fn set_missed_tick_behavior(&mut self, behavior: MissedTickBehavior) {
self.missed_tick_behavior = behavior;
}
pub fn missed_tick_behavior(&self) -> MissedTickBehavior {
self.missed_tick_behavior
}
}
pub fn interval(period: Duration) -> Interval {
let period = if period.is_zero() {
Duration::from_nanos(1)
} else {
period
};
interval_at(std::time::Instant::now() + period, period)
}
pub fn interval_at(start: std::time::Instant, period: Duration) -> Interval {
let period = if period.is_zero() {
Duration::from_nanos(1)
} else {
period
};
Interval {
period,
next_tick: start,
missed_tick_behavior: MissedTickBehavior::default(),
}
}
#[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));
});
}
}