use std::cmp;
use std::time::{Duration, Instant};
use ::{Config, RateLimiter};
const MIN_GOOD_MODE_TIME_DELAY: u64 = 1000;
const MAX_GOOD_MODE_TIME_DELAY: u64 = 60000;
#[derive(Debug, PartialEq)]
enum Mode {
Good,
Bad
}
#[derive(Debug)]
pub struct BinaryRateLimiter {
tick: u32,
max_tick: u32,
mode: Mode,
rtt_threshold: u32,
last_bad_time: Instant,
last_good_time: Instant,
good_time_duration: u64,
delay_until_good_mode: u64
}
impl RateLimiter for BinaryRateLimiter {
fn new(config: Config) -> BinaryRateLimiter {
let rate = config.send_rate as f32;
BinaryRateLimiter {
tick: 0,
max_tick: (rate / (33.0 / (100.0 / rate))) as u32,
mode: Mode::Good,
rtt_threshold: 250,
last_bad_time: Instant::now(),
last_good_time: Instant::now(),
good_time_duration: 0,
delay_until_good_mode: MIN_GOOD_MODE_TIME_DELAY
}
}
fn update(&mut self, rtt: u32, _: f32) {
let conditions = if rtt <= self.rtt_threshold {
self.good_time_duration += time_since(&self.last_good_time);
self.last_good_time = Instant::now();
Mode::Good
} else {
self.last_bad_time = Instant::now();
self.good_time_duration = 0;
Mode::Bad
};
match self.mode {
Mode::Good => match conditions {
Mode::Bad => {
self.mode = Mode::Bad;
if time_since(&self.last_bad_time) < 10000 {
self.delay_until_good_mode *= 2;
self.delay_until_good_mode = cmp::min(
self.delay_until_good_mode,
MAX_GOOD_MODE_TIME_DELAY
);
}
},
Mode::Good => {
if self.good_time_duration >= 10000 {
self.good_time_duration -= 10000;
self.delay_until_good_mode = cmp::max(
self.delay_until_good_mode,
MIN_GOOD_MODE_TIME_DELAY
);
}
}
},
Mode::Bad => {
if time_since(&self.last_bad_time) > self.delay_until_good_mode {
self.mode = Mode::Good;
}
}
}
self.tick += 1;
if self.tick == self.max_tick {
self.tick = 0;
}
}
fn congested(&self) -> bool {
self.mode == Mode::Bad
}
fn should_send(&self ) -> bool {
!self.congested() || self.tick == 0
}
fn reset(&mut self) {
self.tick = 0;
self.mode = Mode::Good;
self.last_bad_time = Instant::now();;
self.last_good_time = Instant::now();
self.good_time_duration = 0;
self.delay_until_good_mode = MIN_GOOD_MODE_TIME_DELAY;
}
}
fn time_since(i: &Instant) -> u64 {
millis_from_duration(i.elapsed())
}
fn millis_from_duration(d: Duration) -> u64 {
d.as_secs() * 1000 + (d.subsec_nanos() as u64 / 1000000)
}
#[cfg(test)]
mod test {
use std::thread;
use std::time::Duration;
use ::{Config, RateLimiter};
use super::BinaryRateLimiter;
#[test]
fn test_modes() {
let mut rl = BinaryRateLimiter::new(Config::default());
assert_eq!(rl.congested(), false);
assert_eq!(rl.should_send(), true);
rl.update(51, 0.0);
assert_eq!(rl.congested(), false);
assert_eq!(rl.should_send(), true);
rl.update(151, 0.0);
assert_eq!(rl.congested(), false);
assert_eq!(rl.should_send(), true);
rl.update(250, 0.0);
assert_eq!(rl.congested(), false);
assert_eq!(rl.should_send(), true);
rl.update(251, 0.0);
assert_eq!(rl.congested(), true);
assert_eq!(rl.should_send(), false);
rl.update(251, 0.0);
assert_eq!(rl.should_send(), false);
rl.update(251, 0.0);
assert_eq!(rl.should_send(), true);
thread::sleep(Duration::from_millis(2100));
rl.update(12, 0.0);
assert_eq!(rl.congested(), false);
assert_eq!(rl.should_send(), true);
}
#[test]
fn test_reset() {
let mut rl = BinaryRateLimiter::new(Config::default());
assert_eq!(rl.congested(), false);
assert_eq!(rl.should_send(), true);
rl.update(251, 0.0);
assert_eq!(rl.congested(), true);
assert_eq!(rl.should_send(), false);
rl.reset();
assert_eq!(rl.congested(), false);
assert_eq!(rl.should_send(), true);
}
}