Skip to content

Commit

Permalink
generalize the throttle implementation to allow usagein UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed May 5, 2023
1 parent b61ec97 commit e03c560
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 66 deletions.
78 changes: 12 additions & 66 deletions src/aggregate.rs
Original file line number Diff line number Diff line change
@@ -1,73 +1,16 @@
use crate::{crossdev, InodeFilter, WalkOptions, WalkResult};
use crate::{crossdev, InodeFilter, Throttle, WalkOptions, WalkResult};
use anyhow::Result;
use filesize::PathExt;
use owo_colors::{AnsiColors as Color, OwoColorize};
use std::time::Duration;
use std::{io, path::Path};
use std::{
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread,
time::Duration,
};

/// Throttle access to an optional `io::Write` to the specified `Duration`
#[derive(Debug)]
struct ThrottleWriter<W> {
out: Option<W>,
trigger: Arc<AtomicBool>,
}

impl<W> ThrottleWriter<W>
where
W: io::Write,
{
fn new(out: Option<W>, duration: Duration) -> Self {
let writer = Self {
out,
trigger: Default::default(),
};

if writer.out.is_some() {
let trigger = Arc::downgrade(&writer.trigger);
thread::spawn(move || {
thread::sleep(Duration::from_secs(1));
while let Some(t) = trigger.upgrade() {
t.store(true, Ordering::Relaxed);
thread::sleep(duration);
}
});
}

writer
}

fn throttled<F>(&mut self, f: F)
where
F: FnOnce(&mut W),
{
if self.trigger.swap(false, Ordering::Relaxed) {
self.unthrottled(f);
}
}

fn unthrottled<F>(&mut self, f: F)
where
F: FnOnce(&mut W),
{
if let Some(ref mut out) = self.out {
f(out);
}
}
}

/// Aggregate the given `paths` and write information about them to `out` in a human-readable format.
/// If `compute_total` is set, it will write an additional line with the total size across all given `paths`.
/// If `sort_by_size_in_bytes` is set, we will sort all sizes (ascending) before outputting them.
pub fn aggregate(
mut out: impl io::Write,
err: Option<impl io::Write>,
mut err: Option<impl io::Write>,
walk_options: WalkOptions,
compute_total: bool,
sort_by_size_in_bytes: bool,
Expand All @@ -82,7 +25,7 @@ pub fn aggregate(
let mut num_roots = 0;
let mut aggregates = Vec::new();
let mut inodes = InodeFilter::default();
let mut progress = ThrottleWriter::new(err, Duration::from_millis(100));
let progress = Throttle::new(Duration::from_millis(100));

for path in paths.into_iter() {
num_roots += 1;
Expand All @@ -99,8 +42,10 @@ pub fn aggregate(
};
for entry in walk_options.iter_from_path(path.as_ref(), device_id) {
stats.entries_traversed += 1;
progress.throttled(|out| {
write!(out, "Enumerating {} entries\r", stats.entries_traversed).ok();
progress.throttled(|| {
if let Some(err) = err.as_mut() {
write!(err, "Enumerating {} entries\r", stats.entries_traversed).ok();
}
});
match entry {
Ok(entry) => {
Expand Down Expand Up @@ -134,9 +79,10 @@ pub fn aggregate(
Err(_) => num_errors += 1,
}
}
progress.unthrottled(|out| {
write!(out, "\x1b[2K\r").ok();
});

if let Some(err) = err.as_mut() {
write!(err, "\x1b[2K\r").ok();
}

if sort_by_size_in_bytes {
aggregates.push((path.as_ref().to_owned(), num_bytes, num_errors));
Expand Down
37 changes: 37 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use crate::crossdev;
use crate::traverse::{EntryData, Tree, TreeIndex};
use byte_unit::{n_gb_bytes, n_gib_bytes, n_mb_bytes, n_mib_bytes, ByteUnit};
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use std::{fmt, path::Path};

pub fn get_entry_or_panic(tree: &Tree, node_idx: TreeIndex) -> &EntryData {
Expand Down Expand Up @@ -114,6 +117,40 @@ pub enum TraversalSorting {
AlphabeticalByFileName,
}

/// Throttle access to an optional `io::Write` to the specified `Duration`
#[derive(Debug)]
pub struct Throttle {
trigger: Arc<AtomicBool>,
}

impl Throttle {
pub fn new(duration: Duration) -> Self {
let instance = Self {
trigger: Default::default(),
};

let trigger = Arc::downgrade(&instance.trigger);
std::thread::spawn(move || {
std::thread::sleep(Duration::from_secs(1));
while let Some(t) = trigger.upgrade() {
t.store(true, Ordering::Relaxed);
std::thread::sleep(duration);
}
});

instance
}

pub fn throttled<F>(&self, f: F)
where
F: FnOnce(),
{
if self.trigger.swap(false, Ordering::Relaxed) {
f()
}
}
}

/// Configures a filesystem walk, including output and formatting options.
#[derive(Clone)]
pub struct WalkOptions {
Expand Down

0 comments on commit e03c560

Please sign in to comment.