use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::mem::replace;
use std::time::{Duration, Instant};
use log::trace;
use crate::event::{self, Event, Ready};
#[derive(Debug)]
pub struct Timers {
deadlines: BinaryHeap<Reverse<Deadline>>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
struct Deadline {
deadline: Instant,
id: event::Id,
}
impl Timers {
pub fn new() -> Timers {
Timers {
deadlines: BinaryHeap::new(),
}
}
pub fn add_deadline(&mut self, id: event::Id, deadline: Instant) {
trace!("adding deadline: id={}, deadline={:?}", id, deadline);
self.deadlines.push(Reverse(Deadline { id, deadline }));
}
pub fn add_timeout(&mut self, id: event::Id, timeout: Duration) {
self.add_deadline(id, Instant::now() + timeout);
}
pub fn remove_deadline(&mut self, id: event::Id) {
trace!("removing deadline: id={}", id);
let index = self.deadlines.iter()
.position(|deadline| deadline.0.id == id);
if let Some(index) = index {
let deadlines = replace(&mut self.deadlines, BinaryHeap::new());
let mut deadlines_vec = deadlines.into_vec();
let removed_deadline = deadlines_vec.swap_remove(index);
debug_assert_eq!(removed_deadline.0.id, id, "remove_deadline: removed incorrect deadline");
drop(replace(&mut self.deadlines, BinaryHeap::from(deadlines_vec)));
}
}
}
impl<ES, E> event::Source<ES, E> for Timers
where ES: event::Sink,
{
fn max_timeout(&self) -> Option<Duration> {
self.deadlines.peek().map(|deadline| {
let now = Instant::now();
if deadline.0.deadline <= now {
Duration::from_millis(0)
} else {
deadline.0.deadline.duration_since(now)
}
})
}
fn poll(&mut self, event_sink: &mut ES) -> Result<(), E> {
trace!("polling timers");
let now = Instant::now();
for _ in 0..event_sink.capacity_left().min(self.deadlines.len()) {
match self.deadlines.peek() {
Some(deadline) if deadline.0.deadline <= now => {
let deadline = self.deadlines.pop().unwrap().0;
event_sink.add(Event::new(deadline.id, Ready::TIMER));
},
_ => break,
}
}
Ok(())
}
}
impl Default for Timers {
fn default() -> Timers {
Timers::new()
}
}