use crate::{
rt::sleep::{AsyncSleep, Sleep},
time::TimeSource,
};
use std::{
future::IntoFuture,
ops::{Deref, DerefMut},
sync::{Arc, Mutex},
time::{Duration, SystemTime},
};
use tokio::sync::oneshot::Sender;
#[derive(Debug)]
struct QueuedSleep {
presents_at: Duration,
notify: Option<Sender<()>>,
}
#[derive(Default, Debug)]
struct Inner {
sleeps: Vec<QueuedSleep>,
now: Duration,
}
impl Inner {
fn push(&mut self, sleep: QueuedSleep) {
self.sleeps.push(sleep);
self.sleeps.sort_by_key(|s| s.presents_at);
}
fn next_presenting(&mut self, time: Duration) -> Option<QueuedSleep> {
if self
.sleeps
.first()
.map(|f| f.presents_at <= time)
.unwrap_or(false)
{
Some(self.sleeps.remove(0))
} else {
None
}
}
}
#[derive(Clone, Default, Debug)]
struct SharedInner {
inner: Arc<Mutex<Inner>>,
}
impl SharedInner {
fn get(&self) -> impl Deref<Target = Inner> + '_ {
self.inner.lock().unwrap()
}
fn get_mut(&self) -> impl DerefMut<Target = Inner> + '_ {
self.inner.lock().unwrap()
}
}
#[derive(Clone, Debug)]
pub struct TickAdvanceSleep {
inner: SharedInner,
}
impl AsyncSleep for TickAdvanceSleep {
fn sleep(&self, duration: Duration) -> Sleep {
let (tx, rx) = tokio::sync::oneshot::channel::<()>();
let mut inner = self.inner.get_mut();
let now = inner.now;
inner.push(QueuedSleep {
presents_at: now + duration,
notify: Some(tx),
});
Sleep::new(async move {
let _ = rx.into_future().await;
})
}
}
#[derive(Clone, Debug)]
pub struct TickAdvanceTime {
inner: SharedInner,
}
impl TickAdvanceTime {
pub async fn tick(&self, duration: Duration) {
let time = self.inner.get().now + duration;
loop {
let Some(mut presenting) = self.inner.get_mut().next_presenting(time) else {
break;
};
self.inner.get_mut().now = presenting.presents_at;
let _ = presenting.notify.take().unwrap().send(());
tokio::task::yield_now().await;
}
self.inner.get_mut().now = time;
}
}
impl TimeSource for TickAdvanceTime {
fn now(&self) -> SystemTime {
SystemTime::UNIX_EPOCH + self.inner.get().now
}
}
pub fn tick_advance_time_and_sleep() -> (TickAdvanceTime, TickAdvanceSleep) {
let inner = SharedInner::default();
(
TickAdvanceTime {
inner: inner.clone(),
},
TickAdvanceSleep {
inner: inner.clone(),
},
)
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::FutureExt;
#[tokio::test]
async fn tick_advances() {
let (time, sleep) = tick_advance_time_and_sleep();
assert_eq!(SystemTime::UNIX_EPOCH, time.now());
time.tick(Duration::from_secs(1)).await;
assert_eq!(SystemTime::UNIX_EPOCH + Duration::from_secs(1), time.now());
let sleeps = vec![
tokio::spawn(sleep.sleep(Duration::from_millis(500))),
tokio::spawn(sleep.sleep(Duration::from_secs(1))),
tokio::spawn(sleep.sleep(Duration::from_secs(2))),
tokio::spawn(sleep.sleep(Duration::from_secs(3))),
tokio::spawn(sleep.sleep(Duration::from_secs(4))),
];
tokio::task::yield_now().await;
for sleep in &sleeps {
assert!(!sleep.is_finished());
}
time.tick(Duration::from_secs(1)).await;
assert_eq!(SystemTime::UNIX_EPOCH + Duration::from_secs(2), time.now());
assert!(sleeps[0].is_finished());
assert!(sleeps[1].is_finished());
assert!(!sleeps[2].is_finished());
assert!(!sleeps[3].is_finished());
assert!(!sleeps[4].is_finished());
time.tick(Duration::from_secs(2)).await;
assert_eq!(SystemTime::UNIX_EPOCH + Duration::from_secs(4), time.now());
assert!(sleeps[2].is_finished());
assert!(sleeps[3].is_finished());
assert!(!sleeps[4].is_finished());
time.tick(Duration::from_secs(1)).await;
assert_eq!(SystemTime::UNIX_EPOCH + Duration::from_secs(5), time.now());
assert!(sleeps[4].is_finished());
}
#[tokio::test]
async fn sleep_leading_to_sleep() {
let (time, sleep) = tick_advance_time_and_sleep();
let task = tokio::spawn(async move {
sleep.sleep(Duration::from_secs(1)).await;
sleep.sleep(Duration::from_secs(2)).await;
sleep.sleep(Duration::from_secs(3)).await;
});
tokio::task::yield_now().await;
assert!(!task.is_finished());
assert_eq!(SystemTime::UNIX_EPOCH, time.now());
time.tick(Duration::from_secs(6)).await;
assert_eq!(SystemTime::UNIX_EPOCH + Duration::from_secs(6), time.now());
task.await.unwrap();
}
#[tokio::test]
async fn racing_sleeps() {
let (time, sleep) = tick_advance_time_and_sleep();
let task = tokio::spawn(async move {
let sleep1 = sleep.sleep(Duration::from_secs(1)).then({
let sleep = sleep.clone();
move |_| async move {
sleep.sleep(Duration::from_secs(1)).await;
}
});
let sleep2 = sleep.sleep(Duration::from_secs(3));
tokio::select! {
_ = sleep1 => { }
_ = sleep2 => { panic!("sleep2 should not complete before sleep1") }
}
});
tokio::task::yield_now().await;
assert!(!task.is_finished());
assert_eq!(SystemTime::UNIX_EPOCH, time.now());
time.tick(Duration::from_secs(6)).await;
assert_eq!(SystemTime::UNIX_EPOCH + Duration::from_secs(6), time.now());
task.await.unwrap();
}
}