use std::sync::Arc;
use std::time::Instant;
use super::BufferEstimator;
pub trait QueueDepthSource: Send + Sync {
fn queued_points(&self) -> u64;
fn sample_rate(&self) -> u32 {
0
}
}
#[derive(Default)]
pub struct RuntimeAuthorityEstimator {
source: Option<Arc<dyn QueueDepthSource>>,
}
impl RuntimeAuthorityEstimator {
pub fn new() -> Self {
Self::default()
}
pub fn with_source(source: Arc<dyn QueueDepthSource>) -> Self {
Self {
source: Some(source),
}
}
pub fn set_source(&mut self, source: Arc<dyn QueueDepthSource>) {
self.source = Some(source);
}
pub fn clear_source(&mut self) {
self.source = None;
}
}
impl BufferEstimator for RuntimeAuthorityEstimator {
fn estimated_fullness(&self, _now: Instant, pps: u32) -> u64 {
self.source.as_ref().map_or(0, |s| {
let queued_samples = s.queued_points();
let sample_rate = s.sample_rate();
if sample_rate == 0 {
return queued_samples;
}
(queued_samples as u128 * pps as u128 / sample_rate as u128) as u64
})
}
fn needs_clock(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU64, Ordering};
struct AtomicCounter {
queued: AtomicU64,
sample_rate: u32,
}
impl AtomicCounter {
fn new(queued: u64, sample_rate: u32) -> Self {
Self {
queued: AtomicU64::new(queued),
sample_rate,
}
}
}
impl QueueDepthSource for AtomicCounter {
fn queued_points(&self) -> u64 {
self.queued.load(Ordering::Relaxed)
}
fn sample_rate(&self) -> u32 {
self.sample_rate
}
}
#[test]
fn defaults_to_zero_with_no_source() {
let est = RuntimeAuthorityEstimator::new();
assert_eq!(est.estimated_fullness(Instant::now(), 30_000), 0);
}
#[test]
fn converts_sample_rate_depth_to_pps_points() {
let counter = Arc::new(AtomicCounter::new(0, 48_000));
let est = RuntimeAuthorityEstimator::with_source(counter.clone());
counter.queued.store(42, Ordering::Relaxed);
assert_eq!(est.estimated_fullness(Instant::now(), 48_000), 42);
counter.queued.store(0, Ordering::Relaxed);
assert_eq!(est.estimated_fullness(Instant::now(), 48_000), 0);
}
#[test]
fn depth_in_samples_reports_pps_points_not_raw_samples() {
let counter = Arc::new(AtomicCounter::new(600, 96_000));
let est = RuntimeAuthorityEstimator::with_source(counter);
assert_eq!(est.estimated_fullness(Instant::now(), 30_000), 187);
}
#[test]
fn default_sample_rate_passes_depth_through_unscaled() {
struct DepthOnly(u64);
impl QueueDepthSource for DepthOnly {
fn queued_points(&self) -> u64 {
self.0
}
}
let est = RuntimeAuthorityEstimator::with_source(Arc::new(DepthOnly(600)));
assert_eq!(est.estimated_fullness(Instant::now(), 30_000), 600);
}
#[test]
fn clear_source_returns_zero() {
let counter = Arc::new(AtomicCounter::new(7, 30_000));
let mut est = RuntimeAuthorityEstimator::with_source(counter);
assert_eq!(est.estimated_fullness(Instant::now(), 30_000), 7);
est.clear_source();
assert_eq!(est.estimated_fullness(Instant::now(), 30_000), 0);
}
}