use crate::{Clock, Signal};
use std::sync::{Arc, Mutex, MutexGuard, PoisonError, Weak};
use std::time::{Duration, Instant};
#[cfg_attr(docsrs, doc(cfg(feature = "test-clock")))]
impl Clock {
pub fn paused() -> Self {
let now = Instant::now();
Self {
paused: Some(Arc::new(Paused {
start: now,
state: Mutex::new(PausedState {
now,
signals: Vec::new(),
}),
})),
}
}
pub fn advance(&self, by: Duration) {
self.advance_with(|now| {
now.checked_add(by)
.expect("clock advance overflows Instant")
});
}
pub fn advance_to(&self, target: Instant) {
self.advance_with(|now| {
assert!(target >= now, "clock cannot go backwards");
target
});
}
fn advance_with(&self, next: impl FnOnce(Instant) -> Instant) {
let Some(paused) = &self.paused else {
panic!("real clock cannot be advanced");
};
let mut state = paused.lock();
let now = next(state.now);
if now == state.now {
return;
}
state.now = now;
let mut signals = Vec::with_capacity(state.signals.len());
state.signals.retain(|signal| match signal.upgrade() {
Some(signal) => {
signals.push(signal);
true
}
None => false,
});
drop(state);
for signal in signals {
signal.notify_all();
}
}
}
impl PartialEq for Clock {
fn eq(&self, other: &Self) -> bool {
match (&self.paused, &other.paused) {
(None, None) => true,
(Some(ours), Some(theirs)) => Arc::ptr_eq(ours, theirs),
_ => false,
}
}
}
impl Eq for Clock {}
pub(crate) struct Paused {
start: Instant,
state: Mutex<PausedState>,
}
struct PausedState {
now: Instant,
signals: Vec<Weak<Signal>>,
}
impl Paused {
pub(crate) fn now(&self) -> Instant {
self.lock().now
}
pub(crate) fn advanced(&self) -> Duration {
self.lock().now - self.start
}
pub(crate) fn register(&self, signal: &Arc<Signal>) {
let mut state = self.lock();
if state.signals.len() == state.signals.capacity() {
state.signals.retain(|signal| signal.strong_count() > 0);
}
state.signals.push(Arc::downgrade(signal));
}
fn lock(&self) -> MutexGuard<'_, PausedState> {
self.state.lock().unwrap_or_else(PoisonError::into_inner)
}
}