Skip to content

Commit

Permalink
Add leaky bucket to rate limiting of progress renders.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
aj-bagwell committed May 17, 2021
1 parent 8cacfd2 commit d1a2404
Showing 1 changed file with 29 additions and 17 deletions.
46 changes: 29 additions & 17 deletions src/state.rs
Expand Up @@ -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<Option<u64>>) -> 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,
},
}
}
Expand Down Expand Up @@ -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, .. } => {
Expand Down Expand Up @@ -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<u64>,
last_draw: Instant,
leaky_bucket: f64,
},
Remote {
state: Arc<RwLock<MultiProgressState>>,
Expand Down

0 comments on commit d1a2404

Please sign in to comment.