#![warn(missing_docs)]
extern crate fibers;
extern crate futures;
use fibers::time::timer::{self, Timeout};
use futures::{Async, Future};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::time::{Duration, SystemTime};
#[derive(Debug)]
pub struct TimeoutQueue<T> {
queue: BinaryHeap<Item<T>>,
next_timeout: Option<Timeout>,
}
impl<T> TimeoutQueue<T> {
pub fn new() -> Self {
TimeoutQueue {
queue: BinaryHeap::new(),
next_timeout: None,
}
}
pub fn push(&mut self, item: T, timeout: Duration) {
let expiry_time = SystemTime::now() + timeout;
let reset_next_timeout = self
.queue
.peek()
.map_or(false, |x| expiry_time < x.expiry_time);
self.queue.push(Item { expiry_time, item });
if reset_next_timeout {
self.next_timeout = None;
}
self.poll_timeout();
}
pub fn pop(&mut self) -> Option<T> {
self.filter_pop(|_| true)
}
pub fn filter_pop<F>(&mut self, filter: F) -> Option<T>
where
F: Fn(&T) -> bool,
{
let now = SystemTime::now();
while let Some(x) = self.queue.pop() {
if !filter(&x.item) {
continue;
}
if x.expiry_time > now {
self.queue.push(x);
break;
}
return Some(x.item);
}
self.poll_timeout();
None
}
pub fn len(&self) -> usize {
self.queue.len()
}
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
fn poll_timeout(&mut self) {
if let Ok(Async::Ready(_)) = self.next_timeout.poll() {
self.next_timeout = self
.queue
.peek()
.and_then(|x| x.expiry_time.duration_since(SystemTime::now()).ok())
.map(timer::timeout);
}
}
}
impl<T> Default for TimeoutQueue<T> {
fn default() -> Self {
TimeoutQueue {
queue: BinaryHeap::new(),
next_timeout: None,
}
}
}
#[derive(Debug)]
struct Item<T> {
expiry_time: SystemTime,
item: T,
}
impl<T> PartialOrd for Item<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
other.expiry_time.partial_cmp(&self.expiry_time)
}
}
impl<T> Ord for Item<T> {
fn cmp(&self, other: &Self) -> Ordering {
other.expiry_time.cmp(&self.expiry_time)
}
}
impl<T> PartialEq for Item<T> {
fn eq(&self, other: &Self) -> bool {
self.expiry_time == other.expiry_time
}
}
impl<T> Eq for Item<T> {}
#[cfg(test)]
mod tests {
use std::thread;
use super::*;
#[test]
fn push_and_pop_works() {
let mut queue = TimeoutQueue::new();
assert!(queue.is_empty());
queue.push(1, Duration::from_millis(20));
queue.push(2, Duration::from_millis(10));
assert_eq!(queue.pop(), None);
assert_eq!(queue.len(), 2);
thread::sleep(Duration::from_millis(12));
assert_eq!(queue.pop(), Some(2));
assert_eq!(queue.len(), 1);
thread::sleep(Duration::from_millis(10));
assert_eq!(queue.pop(), Some(1));
assert_eq!(queue.len(), 0);
}
#[test]
fn filter_pop_works() {
let mut queue = TimeoutQueue::new();
for i in 0..100 {
queue.push(i, Duration::from_millis(i));
}
thread::sleep(Duration::from_millis(50));
assert_eq!(queue.len(), 100);
assert_eq!(queue.filter_pop(|n| *n == 25), Some(25));
assert_eq!(queue.filter_pop(|n| *n == 80), None);
assert_eq!(queue.len(), 20);
thread::sleep(Duration::from_millis(50));
assert_eq!(queue.pop(), Some(80));
assert_eq!(queue.len(), 19);
}
}