use std::time::Duration;
use std::time::Instant;
use priority_queue::double_priority_queue::DoublePriorityQueue;
type Index = u64;
pub struct TimerQueue {
timers: DoublePriorityQueue<Index, Instant>,
}
impl TimerQueue {
pub fn new() -> Self {
Self {
timers: DoublePriorityQueue::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
timers: DoublePriorityQueue::with_capacity(capacity),
}
}
pub fn len(&self) -> usize {
self.timers.len()
}
pub fn is_empty(&self) -> bool {
self.timers.is_empty()
}
pub fn add(&mut self, idx: u64, duration: Duration, now: Instant) {
_ = self.timers.push(idx, now + duration);
}
pub fn del(&mut self, idx: &u64) {
_ = self.timers.remove(idx);
}
pub fn time_remaining(&self, now: Instant) -> Option<Duration> {
self.timers.peek_min().map(|(_, expires_at)| {
if now > *expires_at {
return Duration::new(0, 0);
}
*expires_at - now
})
}
pub fn next_expire(&mut self, now: Instant) -> Option<Index> {
if let Some((_, expires_at)) = self.timers.peek_min() {
if *expires_at <= now {
let idx = self.timers.pop_min().map(|(idx, _)| idx).unwrap();
return Some(idx);
}
}
None
}
pub fn clear(&mut self) {
self.timers.clear();
}
}
impl Default for TimerQueue {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add() {
let mut tq = TimerQueue::with_capacity(10);
assert!(tq.is_empty());
let now = Instant::now();
tq.add(0, Duration::from_millis(100), now);
assert_eq!(tq.len(), 1);
tq.add(1, Duration::from_millis(200), now);
assert_eq!(tq.len(), 2);
tq.add(1, Duration::from_millis(300), now);
assert_eq!(tq.len(), 2);
}
#[test]
fn del() {
let mut tq = TimerQueue::default();
let now = Instant::now();
tq.add(0, Duration::from_millis(100), now);
assert_eq!(tq.len(), 1);
tq.del(&1);
assert_eq!(tq.len(), 1);
tq.del(&0);
assert!(tq.is_empty());
}
#[test]
fn expired() {
let mut tq = TimerQueue::default();
let now = Instant::now();
tq.add(0, Duration::from_millis(100), now);
tq.add(1, Duration::from_millis(200), now);
tq.add(2, Duration::from_millis(300), now);
assert!(tq.next_expire(now).is_none());
assert_eq!(tq.len(), 3);
let t = now + Duration::from_millis(100);
let idx = tq.next_expire(t);
assert!(idx.is_some());
assert_eq!(idx.unwrap(), 0);
assert_eq!(tq.len(), 2);
tq.del(&2);
tq.add(3, Duration::from_millis(1000), now);
tq.add(4, Duration::from_millis(1000), now);
tq.add(5, Duration::from_millis(1500), now);
let t = now + Duration::from_millis(1000);
assert_eq!(tq.next_expire(t), Some(1));
assert_eq!(tq.next_expire(t), Some(3));
assert_eq!(tq.next_expire(t), Some(4));
assert_eq!(tq.len(), 1);
}
#[test]
fn time_remaining() {
let mut tq = TimerQueue::default();
let now = Instant::now();
assert_eq!(tq.time_remaining(now), None);
tq.add(0, Duration::from_millis(100), now);
tq.add(1, Duration::from_millis(200), now);
tq.add(2, Duration::from_millis(300), now);
assert_eq!(tq.len(), 3);
assert_eq!(tq.time_remaining(now), Some(Duration::from_millis(100)));
}
}