use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
use tokio::sync::oneshot;
#[derive(Clone, Debug, Default)]
pub struct Clock {
inner: Arc<Mutex<ClockState>>,
}
#[derive(Debug, Default)]
struct ClockState {
now_ms: u64,
waiters: Vec<Waiter>,
}
#[derive(Debug)]
struct Waiter {
target_ms: u64,
wake: oneshot::Sender<()>,
}
impl Clock {
pub fn new() -> Self {
Self::default()
}
pub fn now_ms(&self) -> u64 {
self.lock().now_ms
}
pub fn advance_ms(&self, delta_ms: u64) {
let mut state = self.lock();
state.now_ms = state.now_ms.saturating_add(delta_ms);
let now = state.now_ms;
let mut i = 0;
while i < state.waiters.len() {
if state.waiters[i].target_ms <= now {
let waiter = state.waiters.swap_remove(i);
let _ = waiter.wake.send(());
} else {
i += 1;
}
}
}
pub async fn sleep_ms(&self, dur_ms: u64) {
let rx = {
let mut state = self.lock();
let target_ms = state.now_ms.saturating_add(dur_ms);
if state.now_ms >= target_ms {
return;
}
let (tx, rx) = oneshot::channel();
state.waiters.push(Waiter {
target_ms,
wake: tx,
});
rx
};
let _ = rx.await;
}
fn lock(&self) -> MutexGuard<'_, ClockState> {
self.inner.lock().unwrap_or_else(PoisonError::into_inner)
}
}