use std::cmp;
use std::collections::BinaryHeap;
use std::time::Instant;
use deadline::Deadline;
use device::command::Command;
#[derive(Debug)]
pub struct DeadlineQueue {
seqno: u64,
heap: BinaryHeap<Item>,
}
impl DeadlineQueue {
pub fn new() -> Self {
DeadlineQueue {
seqno: 0,
heap: BinaryHeap::new(),
}
}
pub fn push(&mut self, command: Command) {
let deadline = AbsoluteDeadline::new(command.deadline());
let item = Item {
seqno: self.seqno,
command,
deadline,
};
self.heap.push(item);
self.seqno += 1;
}
pub fn pop(&mut self) -> Option<Command> {
self.heap.pop().map(|t| t.command)
}
pub fn len(&self) -> usize {
self.heap.len()
}
}
#[derive(Debug)]
struct Item {
seqno: u64, command: Command,
deadline: AbsoluteDeadline,
}
impl PartialEq for Item {
fn eq(&self, other: &Self) -> bool {
self.seqno == other.seqno
}
}
impl Eq for Item {}
impl PartialOrd for Item {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Item {
fn cmp(&self, other: &Self) -> cmp::Ordering {
other
.deadline
.cmp(&self.deadline)
.then_with(|| other.seqno.cmp(&self.seqno))
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum AbsoluteDeadline {
Immediate,
Until(Instant),
Infinity,
}
impl AbsoluteDeadline {
fn new(relative: Deadline) -> Self {
match relative {
Deadline::Immediate => AbsoluteDeadline::Immediate,
Deadline::Within(d) => AbsoluteDeadline::Until(Instant::now() + d),
Deadline::Infinity => AbsoluteDeadline::Infinity,
}
}
}
#[cfg(test)]
mod tests {
use std::thread;
use std::time::Duration;
use super::*;
use deadline::Deadline;
use device::command::{Command, GetLump};
use lump::LumpId;
#[test]
fn deadline_works() {
let mut queue = DeadlineQueue::new();
queue.push(command(0, Deadline::Infinity));
queue.push(command(1, Deadline::Immediate));
queue.push(command(2, Deadline::Within(Duration::from_millis(1))));
thread::sleep(Duration::from_millis(5));
queue.push(command(3, Deadline::Within(Duration::from_millis(0))));
queue.push(command(4, Deadline::Immediate));
assert_eq!(queue.len(), 5);
assert_eq!(lump_id(queue.pop()), Some(1));
assert_eq!(lump_id(queue.pop()), Some(4)); assert_eq!(lump_id(queue.pop()), Some(2));
assert_eq!(lump_id(queue.pop()), Some(3));
assert_eq!(lump_id(queue.pop()), Some(0));
assert_eq!(lump_id(queue.pop()), None);
assert_eq!(queue.len(), 0);
}
fn command(lump_id: u128, deadline: Deadline) -> Command {
Command::Get(GetLump::new(LumpId::new(lump_id), deadline, false).0)
}
fn lump_id(command: Option<Command>) -> Option<u128> {
command.map(|c| {
if let Command::Get(c) = c {
c.lump_id().as_u128()
} else {
unreachable!()
}
})
}
}