use tokio::time::{Duration, Instant};
use super::priority::RequestPriority;
#[derive(Clone, Copy)]
pub(super) struct Waiter {
pub(super) priority: RequestPriority,
pub(super) seq: u64,
pub(super) enqueued_at: Instant,
}
pub(super) struct State {
pub(super) tokens: f64,
pub(super) capacity: f64,
pub(super) rate_per_sec: f64,
pub(super) aging_interval: Duration,
pub(super) last_refill: Instant,
pub(super) waiters: Vec<Waiter>,
pub(super) next_seq: u64,
}
#[derive(Clone, Copy, Debug)]
pub struct SchedulerPermit {
pub queued: Duration,
pub queue_depth: usize,
pub rate_budget_remaining: f64,
}
impl State {
pub(super) fn refill(&mut self, now: Instant) {
let elapsed = now
.saturating_duration_since(self.last_refill)
.as_secs_f64();
if elapsed > 0.0 {
self.tokens = (self.tokens + elapsed * self.rate_per_sec).min(self.capacity);
self.last_refill = now;
}
}
pub(super) fn time_until_token(&self) -> Duration {
if self.tokens >= 1.0 {
Duration::ZERO
} else {
Duration::from_secs_f64((1.0 - self.tokens) / self.rate_per_sec)
}
}
pub(super) fn effective_priority(&self, w: &Waiter, now: Instant) -> u32 {
let base = w.priority as u32;
if self.aging_interval.is_zero() {
return base;
}
let waited = now.saturating_duration_since(w.enqueued_at).as_secs_f64();
let bumps = (waited / self.aging_interval.as_secs_f64()) as u32;
(base + bumps).min(RequestPriority::Critical as u32)
}
pub(super) fn best_seq(&self, now: Instant) -> Option<u64> {
self.waiters
.iter()
.max_by(|a, b| {
self.effective_priority(a, now)
.cmp(&self.effective_priority(b, now))
.then_with(|| b.seq.cmp(&a.seq))
})
.map(|w| w.seq)
}
pub(super) fn remove(&mut self, seq: u64) {
self.waiters.retain(|w| w.seq != seq);
}
}