use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
pub struct RateLimiter {
tat_ns: AtomicU64,
period_ns: u64,
burst_ns: u64,
origin: Instant,
}
impl RateLimiter {
pub fn new(rate_per_sec: f64, burst_capacity: u64) -> Self {
let period_ns = (1_000_000_000.0 / rate_per_sec) as u64;
let burst_ns = period_ns.saturating_mul(burst_capacity);
Self {
tat_ns: AtomicU64::new(0),
period_ns,
burst_ns,
origin: Instant::now(),
}
}
pub fn try_acquire(&self) -> bool {
let now = self.origin.elapsed().as_nanos() as u64;
loop {
let tat = self.tat_ns.load(Ordering::Acquire);
let new_tat = tat.max(now).saturating_add(self.period_ns);
if new_tat.saturating_sub(now) > self.burst_ns {
return false;
}
match self.tat_ns.compare_exchange_weak(
tat,
new_tat,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return true,
Err(_) => continue,
}
}
}
pub fn rate_per_sec(&self) -> f64 {
1_000_000_000.0 / self.period_ns as f64
}
pub fn burst_capacity(&self) -> u64 {
self.burst_ns.checked_div(self.period_ns).unwrap_or(0)
}
}
#[cfg(feature = "harness")]
pub mod recipe;