use std::time::{Duration, Instant};
#[derive(Clone, Debug)]
pub struct Gcra {
interval: Duration,
tat: Instant,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct GcraReservation {
pub scheduled_at: Instant,
pub next_tat: Instant,
}
impl Gcra {
pub fn new(rate_per_second: f64, now: Instant) -> Self {
assert!(
rate_per_second.is_finite() && rate_per_second > 0.0,
"GCRA rate must be finite and positive"
);
Self {
interval: rate_to_interval(rate_per_second),
tat: now,
}
}
pub fn rate(&self) -> f64 {
self.interval.as_secs_f64().recip()
}
pub fn interval(&self) -> Duration {
self.interval
}
pub fn next_at(&self, now: Instant) -> Instant {
self.tat.max(now)
}
pub fn reserve(&mut self, now: Instant) -> GcraReservation {
let scheduled_at = self.next_at(now);
let next_tat = saturating_add(scheduled_at, self.interval);
self.tat = next_tat;
GcraReservation {
scheduled_at,
next_tat,
}
}
pub fn cancel_last(&mut self, reservation: GcraReservation) -> bool {
if self.tat == reservation.next_tat {
self.tat = reservation.scheduled_at;
true
} else {
false
}
}
pub fn set_rate(&mut self, rate_per_second: f64, now: Instant) {
assert!(
rate_per_second.is_finite() && rate_per_second > 0.0,
"GCRA rate must be finite and positive"
);
self.tat = self.next_at(now);
self.interval = rate_to_interval(rate_per_second);
}
pub fn commit(&mut self, dispatched_at: Instant) {
let base = self.next_at(dispatched_at);
self.tat = saturating_add(base, self.interval);
}
pub fn tat(&self) -> Instant {
self.tat
}
}
fn rate_to_interval(rate_per_second: f64) -> Duration {
let seconds = (1.0 / rate_per_second).max(1e-9);
Duration::from_secs_f64(seconds)
}
pub(crate) fn saturating_add(instant: Instant, duration: Duration) -> Instant {
instant.checked_add(duration).unwrap_or(instant)
}