From d1a2404346c9d0b5a49a26cd10e00e77df891a7d Mon Sep 17 00:00:00 2001 From: AJ Bagwell Date: Mon, 17 May 2021 12:24:17 +0100 Subject: [PATCH] Add leaky bucket to rate limiting of progress renders. The leaky bucket makes sure that progress bars are drawn on average a the desired rate but allows it to burst faster if progress is not uniform. It works by having a "bucket" of redraws that are added to every time a draw is requested, when the bucket is full the redraws are skipped, for every tick a redraw is removed from the bucket. This is to fix the regression of #166 when join was removed. --- src/state.rs | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/state.rs b/src/state.rs index 8d589dd1..24f6b1fd 100644 --- a/src/state.rs +++ b/src/state.rs @@ -457,16 +457,13 @@ impl ProgressDrawTarget { /// /// Will panic if refresh_rate is `Some(0)`. To disable rate limiting use `None` instead. pub fn term(term: Term, refresh_rate: impl Into>) -> ProgressDrawTarget { - let rate = refresh_rate - .into() - .map(|x| Duration::from_millis(1000 / x)) - .unwrap_or_else(|| Duration::from_secs(0)); ProgressDrawTarget { kind: ProgressDrawTargetKind::Term { term, last_line_count: 0, - rate, - last_draw: Instant::now() - rate, + refresh_rate: refresh_rate.into(), + last_draw: Instant::now(), + leaky_bucket: MAX_GROUP_SIZE, }, } } @@ -507,20 +504,32 @@ impl ProgressDrawTarget { ProgressDrawTargetKind::Term { ref term, ref mut last_line_count, - rate, + refresh_rate, ref mut last_draw, - } if draw_state.finished || draw_state.force_draw || last_draw.elapsed() > rate => { - if !draw_state.lines.is_empty() && draw_state.move_cursor { - term.move_cursor_up(*last_line_count)?; + ref mut leaky_bucket, + } => { + if draw_state.finished || draw_state.force_draw || *leaky_bucket < MAX_GROUP_SIZE { + if !draw_state.lines.is_empty() && draw_state.move_cursor { + term.move_cursor_up(*last_line_count)?; + } else { + term.clear_last_lines(*last_line_count)?; + } + + draw_state.draw_to_term(term)?; + term.flush()?; + *last_line_count = draw_state.lines.len() - draw_state.orphan_lines; + *leaky_bucket += 1.0; + } + if let Some(refresh_rate) = refresh_rate { + let ticks = last_draw.elapsed().as_secs_f64() * (refresh_rate as f64); + *leaky_bucket -= ticks; } else { - term.clear_last_lines(*last_line_count)?; + *leaky_bucket -= 1.0; + } + if *leaky_bucket < 0.0 { + *leaky_bucket = 0.0; } - - draw_state.draw_to_term(term)?; - term.flush()?; - *last_line_count = draw_state.lines.len() - draw_state.orphan_lines; *last_draw = Instant::now(); - Ok(()) } ProgressDrawTargetKind::Remote { idx, ref state, .. } => { @@ -555,13 +564,16 @@ impl ProgressDrawTarget { }; } } + +const MAX_GROUP_SIZE: f64 = 32.0; #[derive(Debug)] pub(crate) enum ProgressDrawTargetKind { Term { term: Term, last_line_count: usize, - rate: Duration, + refresh_rate: Option, last_draw: Instant, + leaky_bucket: f64, }, Remote { state: Arc>,