use std::time::Duration;
use crate::kernel::Packet;
use crate::rule::Verdict;
use crate::EnterGuard;
#[derive(Debug)]
struct Scheduled {
deliver_at: Duration,
seq: u64,
pkt: Packet,
}
#[derive(Debug, Default)]
pub(crate) struct Scheduler {
now: Duration,
pending: Vec<Scheduled>,
next_seq: u64,
egress: Vec<Packet>,
}
impl Scheduler {
pub fn new() -> Self {
Self {
egress: Vec::with_capacity(64),
..Self::default()
}
}
pub fn tick(&mut self, guard: &EnterGuard, dt: Duration) {
self.now += dt;
let ready_end = self
.pending
.iter()
.position(|s| s.deliver_at > self.now)
.unwrap_or(self.pending.len());
let ready: Vec<Scheduled> = self.pending.drain(..ready_end).collect();
for s in ready {
guard.deliver(s.pkt);
}
let mut egress = std::mem::take(&mut self.egress);
guard.egress_all(&mut egress);
for pkt in egress.drain(..) {
match guard.evaluate(&pkt) {
Verdict::Drop => {}
Verdict::Deliver(d) if d.is_zero() => guard.deliver(pkt),
Verdict::Deliver(d) => self.schedule(pkt, d),
Verdict::Pass => guard.deliver(pkt),
}
}
self.egress = egress;
}
fn schedule(&mut self, pkt: Packet, delay: Duration) {
let deliver_at = self.now + delay;
let seq = self.next_seq;
self.next_seq += 1;
let entry = Scheduled {
deliver_at,
seq,
pkt,
};
let idx = self
.pending
.binary_search_by(|s| (s.deliver_at, s.seq).cmp(&(entry.deliver_at, entry.seq)))
.unwrap_or_else(|i| i);
self.pending.insert(idx, entry);
}
}