use std::sync::Arc;
use super::clock::{Clock, SystemClock};
use super::token_bucket::TokenBucket;
pub struct HierarchicalLimiter {
parent: Arc<TokenBucket>,
children: Vec<Arc<TokenBucket>>,
}
impl HierarchicalLimiter {
pub fn new(
parent_capacity: u64,
parent_rate: f64,
num_children: usize,
child_capacity: u64,
child_rate: f64,
) -> Self {
Self::with_clock_fn(
parent_capacity,
parent_rate,
num_children,
child_capacity,
child_rate,
|| Box::new(SystemClock::new()),
)
}
pub fn with_clock_fn<F>(
parent_capacity: u64,
parent_rate: f64,
num_children: usize,
child_capacity: u64,
child_rate: f64,
mut clock_fn: F,
) -> Self
where
F: FnMut() -> Box<dyn Clock>,
{
let parent = Arc::new(TokenBucket::with_clock(
parent_capacity,
parent_rate,
clock_fn(),
));
let children = (0..num_children.max(1))
.map(|_| {
Arc::new(TokenBucket::with_clock(
child_capacity,
child_rate,
clock_fn(),
))
})
.collect();
Self { parent, children }
}
pub fn try_acquire(&self, child_id: usize, n: u64) -> bool {
let child = match self.children.get(child_id) {
Some(c) => c,
None => return false,
};
if self.parent_available_at_least(n) {
if !child.try_acquire(n) {
return false;
}
if self.parent.try_acquire(n) {
return true;
}
false
} else {
false
}
}
pub fn parent(&self) -> &TokenBucket {
&self.parent
}
pub fn child(&self, child_id: usize) -> Option<&TokenBucket> {
self.children.get(child_id).map(|c| c.as_ref())
}
pub fn num_children(&self) -> usize {
self.children.len()
}
fn parent_available_at_least(&self, n: u64) -> bool {
self.parent.available() >= n
}
}
#[cfg(test)]
#[path = "hierarchical_tests.rs"]
mod tests;