summaryrefslogtreecommitdiffstats
path: root/src/traverse.rs
diff options
context:
space:
mode:
authorCyandev <unixzii@gmail.com>2024-01-05 19:43:35 +0800
committerCyandev <unixzii@gmail.com>2024-01-05 19:43:35 +0800
commit983ba6172604b83c2e4efad0f03273206a43c5db (patch)
tree3765bce77937e991398e40c9f96e0077e1ba0405 /src/traverse.rs
parentad7c77ac8db663e7a870dd73e45159462c1cbe60 (diff)
Optimize UI responsiveness during scanning state
Diffstat (limited to 'src/traverse.rs')
-rw-r--r--src/traverse.rs250
1 files changed, 147 insertions, 103 deletions
diff --git a/src/traverse.rs b/src/traverse.rs
index c6026b1..49958aa 100644
--- a/src/traverse.rs
+++ b/src/traverse.rs
@@ -7,6 +7,7 @@ use std::{
fs::Metadata,
io,
path::{Path, PathBuf},
+ sync::Arc,
time::{Duration, SystemTime, UNIX_EPOCH},
};
@@ -70,10 +71,11 @@ pub struct Traversal {
}
impl Traversal {
- pub fn from_walk(
+ pub fn from_walk<T>(
mut walk_options: WalkOptions,
input: Vec<PathBuf>,
- mut update: impl FnMut(&mut Traversal) -> Result<bool>,
+ waker_rx: &crossbeam::channel::Receiver<T>,
+ mut update: impl FnMut(&mut Traversal, Option<T>) -> Result<bool>,
) -> Result<Option<Traversal>> {
#[derive(Default, Copy, Clone)]
struct EntryInfo {
@@ -149,129 +151,171 @@ impl Traversal {
parent.join(name).size_on_disk_fast(meta)
}
- for path in input.into_iter() {
- let device_id = match crossdev::init(path.as_ref()) {
- Ok(id) => id,
- Err(_) => {
- t.io_errors += 1;
- continue;
+ let (entry_tx, entry_rx) = crossbeam::channel::bounded(100);
+ let walk_options_clone = walk_options.clone();
+ std::thread::Builder::new()
+ .name("dua-fs-walk-dispatcher".to_string())
+ .spawn(move || {
+ for path in input.into_iter() {
+ let device_id = match crossdev::init(path.as_ref()) {
+ Ok(id) => id,
+ Err(_) => {
+ t.io_errors += 1;
+ continue;
+ }
+ };
+ let shared_path = Arc::new(path);
+
+ for entry in walk_options_clone
+ .iter_from_path(shared_path.as_ref(), device_id)
+ .into_iter()
+ {
+ if entry_tx
+ .send((entry, Arc::clone(&shared_path), device_id))
+ .is_err()
+ {
+ // The channel is closed, this means the user has
+ // requested to quit the app. Abort the walking.
+ return;
+ }
+ }
}
- };
- for entry in walk_options
- .iter_from_path(path.as_ref(), device_id)
- .into_iter()
- {
- t.entries_traversed += 1;
- let mut data = EntryData::default();
- match entry {
- Ok(entry) => {
- data.name = if entry.depth < 1 {
- path.clone()
- } else {
- entry.file_name.into()
- };
+ })?;
- let mut file_size = 0u128;
- let mut mtime: SystemTime = UNIX_EPOCH;
- match &entry.client_state {
- Some(Ok(ref m)) => {
- if !m.is_dir()
- && (walk_options.count_hard_links || inodes.add(m))
- && (walk_options.cross_filesystems
- || crossdev::is_same_device(device_id, m))
- {
- if walk_options.apparent_size {
- file_size = m.len() as u128;
+ loop {
+ crossbeam::select! {
+ recv(entry_rx) -> entry => {
+ let Ok((entry, path, device_id)) = entry else {
+ break;
+ };
+
+ t.entries_traversed += 1;
+ let mut data = EntryData::default();
+ match entry {
+ Ok(entry) => {
+ data.name = if entry.depth < 1 {
+ (*path).clone()
+ } else {
+ entry.file_name.into()
+ };
+
+ let mut file_size = 0u128;
+ let mut mtime: SystemTime = UNIX_EPOCH;
+ match &entry.client_state {
+ Some(Ok(ref m)) => {
+ if !m.is_dir()
+ && (walk_options.count_hard_links || inodes.add(m))
+ && (walk_options.cross_filesystems
+ || crossdev::is_same_device(device_id, m))
+ {
+ if walk_options.apparent_size {
+ file_size = m.len() as u128;
+ } else {
+ file_size = size_on_disk(&entry.parent_path, &data.name, m)
+ .unwrap_or_else(|_| {
+ t.io_errors += 1;
+ data.metadata_io_error = true;
+ 0
+ })
+ as u128;
+ }
} else {
- file_size = size_on_disk(&entry.parent_path, &data.name, m)
- .unwrap_or_else(|_| {
- t.io_errors += 1;
- data.metadata_io_error = true;
- 0
- })
- as u128;
+ data.entry_count = Some(0);
+ data.is_dir = true;
}
- } else {
- data.entry_count = Some(0);
- data.is_dir = true;
- }
- match m.modified() {
- Ok(modified) => {
- mtime = modified;
- }
- Err(_) => {
- t.io_errors += 1;
- data.metadata_io_error = true;
+ match m.modified() {
+ Ok(modified) => {
+ mtime = modified;
+ }
+ Err(_) => {
+ t.io_errors += 1;
+ data.metadata_io_error = true;
+ }
}
}
+ Some(Err(_)) => {
+ t.io_errors += 1;
+ data.metadata_io_error = true;
+ }
+ None => {}
}
- Some(Err(_)) => {
- t.io_errors += 1;
- data.metadata_io_error = true;
- }
- None => {}
- }
- match (entry.depth, previous_depth) {
- (n, p) if n > p => {
- directory_info_per_depth_level.push(current_directory_at_depth);
- current_directory_at_depth = EntryInfo {
- size: file_size,
- entries_count: Some(1),
- };
- parent_node_idx = previous_node_idx;
- }
- (n, p) if n < p => {
- for _ in n..p {
+ match (entry.depth, previous_depth) {
+ (n, p) if n > p => {
+ directory_info_per_depth_level.push(current_directory_at_depth);
+ current_directory_at_depth = EntryInfo {
+ size: file_size,
+ entries_count: Some(1),
+ };
+ parent_node_idx = previous_node_idx;
+ }
+ (n, p) if n < p => {
+ for _ in n..p {
+ set_entry_info_or_panic(
+ &mut t.tree,
+ parent_node_idx,
+ current_directory_at_depth,
+ );
+ let dir_info =
+ pop_or_panic(&mut directory_info_per_depth_level);
+
+ current_directory_at_depth.size += dir_info.size;
+ current_directory_at_depth.add_count(&dir_info);
+
+ parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx);
+ }
+ current_directory_at_depth.size += file_size;
+ *current_directory_at_depth.entries_count.get_or_insert(0) += 1;
set_entry_info_or_panic(
&mut t.tree,
parent_node_idx,
current_directory_at_depth,
);
- let dir_info =
- pop_or_panic(&mut directory_info_per_depth_level);
-
- current_directory_at_depth.size += dir_info.size;
- current_directory_at_depth.add_count(&dir_info);
-
- parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx);
}
- current_directory_at_depth.size += file_size;
- *current_directory_at_depth.entries_count.get_or_insert(0) += 1;
- set_entry_info_or_panic(
- &mut t.tree,
- parent_node_idx,
- current_directory_at_depth,
- );
- }
- _ => {
- current_directory_at_depth.size += file_size;
- *current_directory_at_depth.entries_count.get_or_insert(0) += 1;
- }
- };
-
- data.mtime = mtime;
- data.size = file_size;
- let entry_index = t.tree.add_node(data);
+ _ => {
+ current_directory_at_depth.size += file_size;
+ *current_directory_at_depth.entries_count.get_or_insert(0) += 1;
+ }
+ };
- t.tree.add_edge(parent_node_idx, entry_index, ());
- previous_node_idx = entry_index;
- previous_depth = entry.depth;
- }
- Err(_) => {
- if previous_depth == 0 {
- data.name = path.clone();
+ data.mtime = mtime;
+ data.size = file_size;
let entry_index = t.tree.add_node(data);
+
t.tree.add_edge(parent_node_idx, entry_index, ());
+ previous_node_idx = entry_index;
+ previous_depth = entry.depth;
}
+ Err(_) => {
+ if previous_depth == 0 {
+ data.name = (*path).clone();
+ let entry_index = t.tree.add_node(data);
+ t.tree.add_edge(parent_node_idx, entry_index, ());
+ }
- t.io_errors += 1
+ t.io_errors += 1
+ }
}
- }
- if throttle.can_update() && update(&mut t)? {
- return Ok(None);
+ if throttle.can_update() && update(&mut t, None)? {
+ return Ok(None);
+ }
+ },
+ recv(waker_rx) -> waker_value => {
+ let Ok(waker_value) = waker_value else {
+ continue;
+ };
+ if update(&mut t, Some(waker_value))? {
+ return Ok(None);
+ }
+ },
+ default(Duration::from_millis(250)) => {
+ // No events or new entries received, but we still need
+ // to keep updating the status message regularly.
+ if update(&mut t, None)? {
+ return Ok(None);
+ }
}
}
}