use varta_vlp::{Frame, Status};
pub const CAPACITY: usize = 64;
#[derive(Clone, Copy, Debug)]
pub struct Slot {
pub pid: u32,
pub last_nonce: u64,
pub last_ns: u64,
pub status: Status,
pub(crate) stall_emitted: bool,
}
impl Slot {
const EMPTY: Slot = Slot {
pid: 0,
last_nonce: 0,
last_ns: 0,
status: Status::Ok,
stall_emitted: false,
};
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Update {
Inserted,
Refreshed,
OutOfOrder,
CapacityExceeded,
}
pub struct Tracker {
entries: [Slot; CAPACITY],
len: usize,
evictions: u64,
capacity_exceeded: u64,
}
const _: () = assert!(core::mem::size_of::<Tracker>() <= CAPACITY * 40 + 16);
impl Default for Tracker {
fn default() -> Self {
Self::new()
}
}
impl Tracker {
pub const fn new() -> Self {
Tracker {
entries: [Slot::EMPTY; CAPACITY],
len: 0,
evictions: 0,
capacity_exceeded: 0,
}
}
pub fn record(&mut self, frame: &Frame, now_ns: u64, threshold_ns: u64) -> Update {
let status = frame.status;
for slot in &mut self.entries[..self.len] {
if slot.pid == frame.pid {
if frame.nonce <= slot.last_nonce {
return Update::OutOfOrder;
}
slot.last_nonce = frame.nonce;
slot.last_ns = now_ns;
slot.status = status;
slot.stall_emitted = false;
return Update::Refreshed;
}
}
if self.len >= CAPACITY {
if let Some(evict_idx) = self.find_evictable_slot(now_ns, threshold_ns) {
self.entries[evict_idx] = Slot {
pid: frame.pid,
last_nonce: frame.nonce,
last_ns: now_ns,
status,
stall_emitted: false,
};
self.evictions = self.evictions.saturating_add(1);
return Update::Inserted;
}
self.capacity_exceeded = self.capacity_exceeded.saturating_add(1);
return Update::CapacityExceeded;
}
self.entries[self.len] = Slot {
pid: frame.pid,
last_nonce: frame.nonce,
last_ns: now_ns,
status,
stall_emitted: false,
};
self.len += 1;
Update::Inserted
}
fn find_evictable_slot(&self, now_ns: u64, threshold_ns: u64) -> Option<usize> {
let evict_threshold = threshold_ns.saturating_mul(10);
let mut best_idx: Option<usize> = None;
let mut best_last_ns: u64 = u64::MAX;
for (idx, slot) in self.entries[..self.len].iter().enumerate() {
if slot.stall_emitted
&& now_ns.saturating_sub(slot.last_ns) > evict_threshold
&& slot.last_ns < best_last_ns
{
best_last_ns = slot.last_ns;
best_idx = Some(idx);
}
}
best_idx
}
pub fn take_evictions(&mut self) -> u64 {
let count = self.evictions;
self.evictions = 0;
count
}
pub fn take_capacity_exceeded(&mut self) -> u64 {
let count = self.capacity_exceeded;
self.capacity_exceeded = 0;
count
}
pub fn iter_stalled(&self, now_ns: u64, threshold_ns: u64) -> impl Iterator<Item = &Slot> + '_ {
self.entries[..self.len]
.iter()
.filter(move |slot| now_ns.saturating_sub(slot.last_ns) >= threshold_ns)
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub(crate) fn mark_stall_emitted(&mut self, pid: u32) {
for slot in &mut self.entries[..self.len] {
if slot.pid == pid {
slot.stall_emitted = true;
return;
}
}
}
}