use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug, Clone)]
pub struct AimdConfig {
pub initial_limit: usize,
pub min_limit: usize,
pub max_limit: usize,
pub increase_by: usize,
pub decrease_factor: f64,
}
impl Default for AimdConfig {
fn default() -> Self {
Self {
initial_limit: 10,
min_limit: 1,
max_limit: 100,
increase_by: 1,
decrease_factor: 0.5,
}
}
}
impl AimdConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_initial_limit(mut self, limit: usize) -> Self {
self.initial_limit = limit;
self
}
pub fn with_min_limit(mut self, limit: usize) -> Self {
self.min_limit = limit;
self
}
pub fn with_max_limit(mut self, limit: usize) -> Self {
self.max_limit = limit;
self
}
pub fn with_increase_by(mut self, amount: usize) -> Self {
self.increase_by = amount;
self
}
pub fn with_decrease_factor(mut self, factor: f64) -> Self {
self.decrease_factor = factor;
self
}
}
pub struct AimdController {
limit: AtomicUsize,
config: AimdConfig,
}
impl AimdController {
pub fn new(config: AimdConfig) -> Self {
let initial = config
.initial_limit
.clamp(config.min_limit, config.max_limit);
Self {
limit: AtomicUsize::new(initial),
config,
}
}
pub fn limit(&self) -> usize {
self.limit.load(Ordering::Relaxed)
}
pub fn min_limit(&self) -> usize {
self.config.min_limit
}
pub fn max_limit(&self) -> usize {
self.config.max_limit
}
pub fn record_success(&self) {
let current = self.limit.load(Ordering::Relaxed);
let new_limit = current
.saturating_add(self.config.increase_by)
.min(self.config.max_limit);
self.limit.store(new_limit, Ordering::Relaxed);
}
pub fn record_failure(&self) {
let current = self.limit.load(Ordering::Relaxed);
let decreased = (current as f64 * self.config.decrease_factor) as usize;
let new_limit = decreased.max(self.config.min_limit);
self.limit.store(new_limit, Ordering::Relaxed);
}
pub fn record_successes(&self, count: usize) {
let current = self.limit.load(Ordering::Relaxed);
let increase = self.config.increase_by.saturating_mul(count);
let new_limit = current.saturating_add(increase).min(self.config.max_limit);
self.limit.store(new_limit, Ordering::Relaxed);
}
pub fn reset(&self) {
let initial = self
.config
.initial_limit
.clamp(self.config.min_limit, self.config.max_limit);
self.limit.store(initial, Ordering::Relaxed);
}
pub fn config(&self) -> &AimdConfig {
&self.config
}
}
impl Clone for AimdController {
fn clone(&self) -> Self {
Self {
limit: AtomicUsize::new(self.limit.load(Ordering::Relaxed)),
config: self.config.clone(),
}
}
}
impl std::fmt::Debug for AimdController {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AimdController")
.field("limit", &self.limit())
.field("config", &self.config)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = AimdConfig::default();
assert_eq!(config.initial_limit, 10);
assert_eq!(config.min_limit, 1);
assert_eq!(config.max_limit, 100);
assert_eq!(config.increase_by, 1);
assert!((config.decrease_factor - 0.5).abs() < f64::EPSILON);
}
#[test]
fn test_config_builder() {
let config = AimdConfig::new()
.with_initial_limit(50)
.with_min_limit(10)
.with_max_limit(200)
.with_increase_by(5)
.with_decrease_factor(0.75);
assert_eq!(config.initial_limit, 50);
assert_eq!(config.min_limit, 10);
assert_eq!(config.max_limit, 200);
assert_eq!(config.increase_by, 5);
assert!((config.decrease_factor - 0.75).abs() < f64::EPSILON);
}
#[test]
fn test_controller_initial_limit() {
let config = AimdConfig::default().with_initial_limit(25);
let controller = AimdController::new(config);
assert_eq!(controller.limit(), 25);
}
#[test]
fn test_controller_initial_clamped_to_max() {
let config = AimdConfig::default()
.with_initial_limit(200)
.with_max_limit(50);
let controller = AimdController::new(config);
assert_eq!(controller.limit(), 50);
}
#[test]
fn test_controller_initial_clamped_to_min() {
let config = AimdConfig::default()
.with_initial_limit(0)
.with_min_limit(5);
let controller = AimdController::new(config);
assert_eq!(controller.limit(), 5);
}
#[test]
fn test_additive_increase() {
let config = AimdConfig::default()
.with_initial_limit(10)
.with_increase_by(2);
let controller = AimdController::new(config);
controller.record_success();
assert_eq!(controller.limit(), 12);
controller.record_success();
assert_eq!(controller.limit(), 14);
}
#[test]
fn test_increase_respects_max() {
let config = AimdConfig::default()
.with_initial_limit(98)
.with_max_limit(100)
.with_increase_by(5);
let controller = AimdController::new(config);
controller.record_success();
assert_eq!(controller.limit(), 100); }
#[test]
fn test_multiplicative_decrease() {
let config = AimdConfig::default()
.with_initial_limit(100)
.with_decrease_factor(0.5);
let controller = AimdController::new(config);
controller.record_failure();
assert_eq!(controller.limit(), 50);
controller.record_failure();
assert_eq!(controller.limit(), 25);
}
#[test]
fn test_decrease_respects_min() {
let config = AimdConfig::default()
.with_initial_limit(10)
.with_min_limit(5)
.with_decrease_factor(0.1);
let controller = AimdController::new(config);
controller.record_failure();
assert_eq!(controller.limit(), 5); }
#[test]
fn test_on_successes_batch() {
let config = AimdConfig::default()
.with_initial_limit(10)
.with_increase_by(1);
let controller = AimdController::new(config);
controller.record_successes(5);
assert_eq!(controller.limit(), 15);
}
#[test]
fn test_reset() {
let config = AimdConfig::default().with_initial_limit(50);
let controller = AimdController::new(config);
controller.record_success();
controller.record_success();
assert_eq!(controller.limit(), 52);
controller.reset();
assert_eq!(controller.limit(), 50);
}
#[test]
fn test_sawtooth_pattern() {
let config = AimdConfig::default()
.with_initial_limit(10)
.with_min_limit(1)
.with_max_limit(100)
.with_increase_by(1)
.with_decrease_factor(0.5);
let controller = AimdController::new(config);
for _ in 0..10 {
controller.record_success();
}
assert_eq!(controller.limit(), 20);
controller.record_failure();
assert_eq!(controller.limit(), 10);
for _ in 0..20 {
controller.record_success();
}
assert_eq!(controller.limit(), 30);
controller.record_failure();
assert_eq!(controller.limit(), 15);
}
#[test]
fn test_clone() {
let config = AimdConfig::default().with_initial_limit(42);
let controller = AimdController::new(config);
controller.record_success();
let cloned = controller.clone();
assert_eq!(cloned.limit(), 43);
controller.record_failure();
assert_eq!(controller.limit(), 21);
assert_eq!(cloned.limit(), 43);
}
#[test]
fn test_debug() {
let config = AimdConfig::default().with_initial_limit(10);
let controller = AimdController::new(config);
let debug_str = format!("{:?}", controller);
assert!(debug_str.contains("AimdController"));
assert!(debug_str.contains("10"));
}
#[test]
fn test_thread_safety() {
use std::sync::Arc;
use std::thread;
let config = AimdConfig::default()
.with_initial_limit(1000)
.with_max_limit(10000);
let controller = Arc::new(AimdController::new(config));
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&controller);
handles.push(thread::spawn(move || {
for _ in 0..100 {
c.record_success();
}
}));
}
for handle in handles {
handle.join().unwrap();
}
assert!(controller.limit() > 1000);
}
}