pub mod atomic;
mod cache_padded;
pub mod local;
pub mod padded_atomic;
pub trait TimeStorage: crate::private::Sealed {
fn new(zero_time: f64) -> Self;
fn load(&self) -> f64;
fn store(&self, value: f64);
fn compare_exchange_weak(&self, current: f64, new: f64) -> Result<(), f64>;
}
pub(crate) enum TokenAcquisition {
Acquired(f64),
ZeroedAt(f64),
}
#[derive(Debug, Clone)]
pub(crate) struct TokenBucketStorage<S> {
inner: S,
}
impl<S: TimeStorage> TokenBucketStorage<S> {
pub fn new(storage: S) -> Self {
Self { inner: storage }
}
pub fn reset(&self, zero_time: f64) {
self.inner.store(zero_time);
}
pub fn balance(&self, rate: f64, burst_size: f64, now: f64) -> f64 {
debug_assert!(rate > 0.0);
debug_assert!(burst_size > 0.0);
let zt = self.inner.load();
((now - zt) * rate).min(burst_size)
}
pub fn consume<F>(&self, rate: f64, burst_size: f64, now: f64, f: F) -> f64
where
F: Fn(f64) -> f64,
{
debug_assert!(rate > 0.0);
debug_assert!(burst_size > 0.0);
let mut zero_time_old = self.inner.load();
loop {
let available = ((now - zero_time_old) * rate).min(burst_size);
let consumed = f(available);
if consumed == 0.0 {
return 0.0;
}
let tokens_new = available - consumed;
let zero_time_new = now - tokens_new / rate;
match self
.inner
.compare_exchange_weak(zero_time_old, zero_time_new)
{
Ok(()) => return consumed,
Err(actual_zero_time) => {
zero_time_old = actual_zero_time;
}
}
}
}
pub fn consume2<F>(&self, rate: f64, burst_size: f64, now: f64, f: F) -> TokenAcquisition
where
F: Fn(f64) -> f64,
{
debug_assert!(rate > 0.0);
debug_assert!(burst_size > 0.0);
let mut zero_time_old = self.inner.load();
loop {
let available = ((now - zero_time_old) * rate).min(burst_size);
let consumed = f(available);
if consumed == 0.0 {
return TokenAcquisition::ZeroedAt(zero_time_old);
}
let tokens_new = available - consumed;
let zero_time_new = now - tokens_new / rate;
match self
.inner
.compare_exchange_weak(zero_time_old, zero_time_new)
{
Ok(()) => return TokenAcquisition::Acquired(consumed),
Err(actual_zero_time) => {
zero_time_old = actual_zero_time;
}
}
}
}
pub fn return_tokens(&self, tokens: f64, rate: f64) -> f64 {
debug_assert!(rate > 0.0);
let mut zero_time_old = self.inner.load();
loop {
let zero_time_new = zero_time_old - tokens / rate;
match self
.inner
.compare_exchange_weak(zero_time_old, zero_time_new)
{
Ok(()) => return zero_time_new,
Err(actual_zero_time) => {
zero_time_old = actual_zero_time;
}
}
}
}
pub fn time_when_bucket(&self, rate: f64, target: f64) -> f64 {
self.inner.load() + target / rate
}
}