use crate::id::types::ChildId;
use std::collections::HashMap;
#[derive(Debug)]
pub struct FairnessProbe {
scheduling_opportunities: u64,
per_child_ops: HashMap<ChildId, u64>,
last_probe_unix_nanos: u128,
probe_interval_ns: u128,
min_ops_per_window: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StarvationAlert {
pub starved_child_id: ChildId,
pub skip_count: u64,
pub probe_start_unix_nanos: u128,
pub probe_end_unix_nanos: u128,
}
impl FairnessProbe {
pub fn new(now_unix_nanos: u128) -> Self {
Self {
scheduling_opportunities: 0,
per_child_ops: HashMap::new(),
last_probe_unix_nanos: now_unix_nanos,
probe_interval_ns: 10_000_000_000,
min_ops_per_window: 1,
}
}
pub fn record_opportunity(&mut self, child_id: &ChildId) {
self.scheduling_opportunities += 1;
*self.per_child_ops.entry(child_id.clone()).or_insert(0) += 1;
}
pub fn check(
&mut self,
now_unix_nanos: u128,
all_child_ids: &[ChildId],
) -> Option<StarvationAlert> {
let elapsed = now_unix_nanos.saturating_sub(self.last_probe_unix_nanos);
if elapsed < self.probe_interval_ns {
return None;
}
let probe_start = self.last_probe_unix_nanos;
self.last_probe_unix_nanos = now_unix_nanos;
for child_id in all_child_ids {
let ops = self.per_child_ops.get(child_id).copied().unwrap_or(0);
if ops < self.min_ops_per_window {
let alert = StarvationAlert {
starved_child_id: child_id.clone(),
skip_count: self.min_ops_per_window.saturating_sub(ops),
probe_start_unix_nanos: probe_start,
probe_end_unix_nanos: now_unix_nanos,
};
self.per_child_ops.clear();
return Some(alert);
}
}
self.per_child_ops.clear();
None
}
}