use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy)]
pub struct Deadline {
kind: DeadlineKind,
}
impl Deadline {
#[inline]
pub fn once(dur: Duration) -> Self {
Self {
kind: DeadlineKind::once(dur),
}
}
#[inline]
pub fn repeat(dur: Duration) -> Self {
Self {
kind: DeadlineKind::repeat(dur),
}
}
#[inline]
pub fn expired(&mut self) -> bool {
match &mut self.kind {
DeadlineKind::Once(deadline) => deadline.expired(),
DeadlineKind::Repeat(deadline) => deadline.expired(),
}
}
#[inline]
pub fn remaining_duration(&mut self) -> Duration {
match &mut self.kind {
DeadlineKind::Once(deadline) => deadline.remaining_duration(),
DeadlineKind::Repeat(deadline) => deadline.remaining_duration(),
}
}
#[inline]
pub fn wait(&mut self) {
match &mut self.kind {
DeadlineKind::Once(deadline) => deadline.wait(),
DeadlineKind::Repeat(deadline) => deadline.wait(),
}
}
}
#[derive(Clone, Copy)]
enum DeadlineKind {
Once(DeadlineOnce),
Repeat(DeadlineRepeat),
}
impl DeadlineKind {
#[inline]
fn once(dur: Duration) -> Self {
Self::Once(DeadlineOnce::new(dur))
}
#[inline]
fn repeat(dur: Duration) -> Self {
Self::Repeat(DeadlineRepeat::new(dur))
}
}
impl Debug for DeadlineKind {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Once(inner) => write!(f, "{inner:?}"),
Self::Repeat(inner) => write!(f, "{inner:?}"),
}
}
}
#[derive(Debug, Clone, Copy)]
struct DeadlineOnce {
delivery_time: Instant,
}
impl DeadlineOnce {
#[inline]
fn new(dur: Duration) -> Self {
let delivery_time = checked_delivery_time(Instant::now(), dur);
Self { delivery_time }
}
#[inline]
fn expired(&self) -> bool {
self.remaining_duration() == Duration::ZERO
}
#[inline]
fn remaining_duration(&self) -> Duration {
self.delivery_time - Instant::now()
}
#[inline]
fn wait(&self) {
std::thread::sleep(self.remaining_duration())
}
}
#[derive(Debug, Clone, Copy)]
struct DeadlineRepeat {
dur: Duration,
delivery_time: Instant,
}
impl DeadlineRepeat {
#[inline]
fn new(dur: Duration) -> Self {
let delivery_time = checked_delivery_time(Instant::now(), dur);
Self { dur, delivery_time }
}
#[inline]
fn expired(&mut self) -> bool {
self.remaining_duration() == Duration::ZERO
}
#[inline]
fn remaining_duration(&mut self) -> Duration {
let ret = self.delivery_time - Instant::now();
if ret == Duration::ZERO {
self.delivery_time += self.dur;
}
ret
}
#[inline]
fn wait(&mut self) {
std::thread::sleep(self.remaining_duration());
self.delivery_time += self.dur;
}
}
#[inline]
fn checked_delivery_time(instant: Instant, dur: Duration) -> Instant {
const TEN_YEARS: Duration = Duration::from_secs(86400 * 365 * 10);
instant.checked_add(dur).unwrap_or(instant + TEN_YEARS)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn delivery_time() {
let now = Instant::now();
let ret = checked_delivery_time(now, Duration::from_secs(1));
assert_eq!(ret, now + Duration::from_secs(1));
let ret = checked_delivery_time(now, Duration::MAX);
assert_eq!(ret, now + Duration::from_secs(86400 * 365 * 10));
}
#[test]
fn once_expired() {
let mut deadline = Deadline::once(Duration::from_millis(100));
assert!(!deadline.expired());
std::thread::sleep(Duration::from_millis(110));
assert!(deadline.expired());
assert!(deadline.expired());
}
#[test]
fn once_remains() {
let mut deadline = Deadline::once(Duration::from_millis(100));
assert!(deadline.remaining_duration() > Duration::ZERO);
assert!(deadline.remaining_duration() < Duration::from_millis(100));
std::thread::sleep(Duration::from_millis(50));
assert!(deadline.remaining_duration() > Duration::ZERO);
assert!(deadline.remaining_duration() < Duration::from_millis(50));
std::thread::sleep(Duration::from_millis(51));
assert!(deadline.remaining_duration() == Duration::ZERO);
assert!(deadline.remaining_duration() == Duration::ZERO);
}
#[test]
fn once_wait() {
let mut deadline = Deadline::once(Duration::from_millis(100));
let now = Instant::now();
deadline.wait();
assert!(now.elapsed() >= Duration::from_millis(100));
let mut deadline = Deadline::once(Duration::from_millis(100));
let now = Instant::now();
std::thread::sleep(Duration::from_millis(50));
deadline.wait();
let delay = now.elapsed();
assert!(delay >= Duration::from_millis(100));
assert!(delay < Duration::from_millis(110));
let now = Instant::now();
deadline.wait();
let delay = now.elapsed();
assert!(delay < Duration::from_millis(1));
}
#[test]
fn repeat_expired() {
let mut deadline = Deadline::repeat(Duration::from_millis(100));
assert!(!deadline.expired());
std::thread::sleep(Duration::from_millis(110));
assert!(deadline.expired());
assert!(!deadline.expired());
}
#[test]
fn repeat_remains() {
let mut deadline = Deadline::repeat(Duration::from_millis(100));
assert!(deadline.remaining_duration() > Duration::ZERO);
assert!(deadline.remaining_duration() < Duration::from_millis(100));
std::thread::sleep(Duration::from_millis(50));
assert!(deadline.remaining_duration() > Duration::ZERO);
assert!(deadline.remaining_duration() < Duration::from_millis(50));
std::thread::sleep(Duration::from_millis(51));
assert!(deadline.remaining_duration() == Duration::ZERO);
assert!(deadline.remaining_duration() < Duration::from_millis(100));
}
#[test]
fn repeat_wait() {
let mut deadline = Deadline::repeat(Duration::from_millis(100));
let now = Instant::now();
deadline.wait();
assert!(now.elapsed() >= Duration::from_millis(100));
let mut deadline = Deadline::repeat(Duration::from_millis(100));
let now = Instant::now();
std::thread::sleep(Duration::from_millis(50));
deadline.wait();
let delay = now.elapsed();
assert!(delay >= Duration::from_millis(100));
assert!(delay < Duration::from_millis(110));
let now = Instant::now();
deadline.wait();
let delay = now.elapsed();
assert!(delay >= Duration::from_millis(90), "delay = {:?}", delay);
}
}