summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2020-02-02 17:23:30 +0100
committerCanop <cano.petrole@gmail.com>2020-02-02 17:23:30 +0100
commitacc1486032aaad94fc852f3001e6b13bbeecfe11 (patch)
treee38815eaf7a5da88bcb0c603d9f7cfedb2ddeee4
parentb10cc444d58cb617c820f4a98d7a6e3d2bd09b46 (diff)
background computation of git repo statistics
This is still a temporary no-caching solution.
-rw-r--r--Cargo.lock2
-rw-r--r--src/app.rs36
-rw-r--r--src/app_state.rs4
-rw-r--r--src/browser_states.rs73
-rw-r--r--src/browser_verbs.rs6
-rw-r--r--src/displayable_tree.rs5
-rw-r--r--src/file_sizes/file_sizes_default.rs4
-rw-r--r--src/file_sizes/file_sizes_unix.rs18
-rw-r--r--src/file_sizes/mod.rs6
-rw-r--r--src/flat_tree.rs22
-rw-r--r--src/git_status_computer.rs69
-rw-r--r--src/help_states.rs4
-rw-r--r--src/help_verbs.rs4
-rw-r--r--src/lib.rs3
-rw-r--r--src/task_sync.rs170
-rw-r--r--src/tree_build/builder.rs24
16 files changed, 357 insertions, 93 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4d7083e..180cecb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -93,7 +93,7 @@ dependencies = [
[[package]]
name = "broot"
-version = "0.12.2"
+version = "0.13.0"
dependencies = [
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/src/app.rs b/src/app.rs
index 6ab3068..573c893 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -22,7 +22,7 @@ use {
screens::Screen,
skin::Skin,
status::Status,
- task_sync::TaskLifetime,
+ task_sync::Dam,
},
crossterm::{
self, cursor,
@@ -77,18 +77,18 @@ impl App {
}
/// execute all the pending tasks until there's none remaining or
- /// the allowed lifetime is expired (usually when the user typed a new key)
+ /// the dam asks for interruption
fn do_pending_tasks(
&mut self,
w: &mut impl Write,
cmd: &Command,
screen: &mut Screen,
con: &AppContext,
- tl: TaskLifetime,
+ dam: &mut Dam,
) -> Result<(), ProgramError> {
let state = self.mut_state();
- while state.has_pending_task() & !tl.is_expired() {
- state.do_pending_task(screen, &tl);
+ while state.has_pending_task() & !dam.has_event() {
+ state.do_pending_task(screen, dam);
state.display(w, screen, con)?;
state.write_status(w, cmd, &screen, con)?;
}
@@ -187,22 +187,24 @@ impl App {
debug!("we're on screen");
let mut screen = Screen::new(con, skin)?;
- // we listen for events in a separate thread so that we can go on listening
- // when a long search is running, and interrupt it if needed
let mut writer = WriteCleanup::build(
writer,
|w| just_queue(w, EnableMouseCapture),
|w| just_queue(w, DisableMouseCapture),
)?;
+
+ // we listen for events in a separate thread so that we can go on listening
+ // when a long search is running, and interrupt it if needed
let event_source = EventSource::new()?;
let rx_events = event_source.receiver();
+ let mut dam = Dam::from(rx_events);
self.push(Box::new(
BrowserState::new(
con.launch_args.root.clone(),
con.launch_args.tree_options.clone(),
&screen,
- &TaskLifetime::unlimited(),
+ &Dam::unlimited(),
)?
.expect("Failed to create BrowserState"),
));
@@ -212,10 +214,15 @@ impl App {
// if some commands were passed to the application
// we execute them before even starting listening for events
if let Some(unparsed_commands) = &con.launch_args.commands {
- let lifetime = TaskLifetime::unlimited();
for arg_cmd in parse_command_sequence(unparsed_commands, con)? {
cmd = self.apply_command(&mut writer, arg_cmd, &mut screen, con)?;
- self.do_pending_tasks(&mut writer, &cmd, &mut screen, con, lifetime.clone())?;
+ self.do_pending_tasks(
+ &mut writer,
+ &cmd,
+ &mut screen,
+ con,
+ &mut dam,
+ )?;
if self.quitting {
return Ok(self.launch_at_end.take());
}
@@ -229,13 +236,12 @@ impl App {
screen.input_field.display_on(&mut writer)?;
loop {
- let tl = TaskLifetime::new(event_source.shared_event_count());
if !self.quitting {
- self.do_pending_tasks(&mut writer, &cmd, &mut screen, con, tl)?;
+ self.do_pending_tasks(&mut writer, &cmd, &mut screen, con, &mut dam)?;
}
- let event = match rx_events.recv() {
- Ok(event) => event,
- Err(_) => {
+ let event = match dam.next_event() {
+ Some(event) => event,
+ None => {
// this is how we quit the application,
// when the input thread is properly closed
break;
diff --git a/src/app_state.rs b/src/app_state.rs
index 5952929..1d8b1b2 100644
--- a/src/app_state.rs
+++ b/src/app_state.rs
@@ -6,7 +6,7 @@ use {
errors::{ProgramError, TreeBuildError},
external::Launchable,
screens::Screen,
- task_sync::TaskLifetime,
+ task_sync::Dam,
},
std::io::Write,
};
@@ -59,7 +59,7 @@ pub trait AppState {
fn refresh(&mut self, screen: &Screen, con: &AppContext) -> Command;
- fn do_pending_task(&mut self, screen: &mut Screen, tl: &TaskLifetime);
+ fn do_pending_task(&mut self, screen: &mut Screen, dam: &mut Dam);
fn has_pending_task(&self) -> bool;
diff --git a/src/browser_states.rs b/src/browser_states.rs
index 97be070..05033a1 100644
--- a/src/browser_states.rs
+++ b/src/browser_states.rs
@@ -7,11 +7,12 @@ use {
errors::{ProgramError, TreeBuildError},
external::Launchable,
flat_tree::{LineType, Tree},
+ git_status_computer,
help_states::HelpState,
patterns::Pattern,
screens::{self, Screen},
status::Status,
- task_sync::TaskLifetime,
+ task_sync::Dam,
tree_build::TreeBuilder,
tree_options::TreeOptions,
verb_store::PrefixSearchResult,
@@ -40,11 +41,11 @@ impl BrowserState {
path: PathBuf,
mut options: TreeOptions,
screen: &Screen,
- tl: &TaskLifetime,
+ dam: &Dam,
) -> Result<Option<BrowserState>, TreeBuildError> {
let pending_pattern = options.pattern.take();
let builder = TreeBuilder::from(path, options, BrowserState::page_height(screen) as usize)?;
- Ok(builder.build(tl, false).map(move |tree| BrowserState {
+ Ok(builder.build(false, dam).map(move |tree| BrowserState {
tree,
filtered_tree: None,
pending_pattern,
@@ -65,7 +66,7 @@ impl BrowserState {
tree.root().clone(),
options,
screen,
- &TaskLifetime::unlimited(),
+ &Dam::unlimited(),
),
Command::from_pattern(&tree.options.pattern),
)
@@ -94,7 +95,6 @@ impl BrowserState {
) -> Result<AppStateCmdResult, ProgramError> {
let tree = self.displayed_tree();
let line = tree.selected_line();
- let tl = TaskLifetime::unlimited();
match &line.line_type {
LineType::File => match open::that(&line.path) {
Ok(exit_status) => {
@@ -112,8 +112,9 @@ impl BrowserState {
target = PathBuf::from(parent);
}
}
+ let dam = Dam::unlimited();
Ok(AppStateCmdResult::from_optional_state(
- BrowserState::new(target, tree.options.without_pattern(), screen, &tl),
+ BrowserState::new(target, tree.options.without_pattern(), screen, &dam),
Command::new(),
))
}
@@ -217,7 +218,13 @@ fn make_opener(
impl AppState for BrowserState {
fn has_pending_task(&self) -> bool {
- self.pending_pattern.is_some() || self.displayed_tree().has_dir_missing_size()
+ debug!(
+ "CHECK self.displayed_tree().is_missing_git_status_computation() = {}",
+ self.displayed_tree().is_missing_git_status_computation(),
+ );
+ self.pending_pattern.is_some()
+ || self.displayed_tree().has_dir_missing_size()
+ || self.displayed_tree().is_missing_git_status_computation()
}
fn write_status(
@@ -391,8 +398,9 @@ impl AppState for BrowserState {
}
/// do some work, totally or partially, if there's some to do.
- /// Stop as soon as the lifetime is expired.
- fn do_pending_task(&mut self, screen: &mut Screen, tl: &TaskLifetime) {
+ /// Stop as soon as the dam asks for interruption
+ fn do_pending_task(&mut self, screen: &mut Screen, dam: &mut Dam) {
+ debug!("entering do_pending_task");
if self.pending_pattern.is_some() {
let pattern_str = self.pending_pattern.to_string();
let mut options = self.tree.options.clone();
@@ -410,7 +418,7 @@ impl AppState for BrowserState {
Info,
"tree filtering",
pattern_str,
- builder.build(tl, self.total_search_required),
+ builder.build(self.total_search_required, dam),
); // can be None if a cancellation was required
self.total_search_required = false;
if let Some(ref mut ft) = filtered_tree {
@@ -418,8 +426,51 @@ impl AppState for BrowserState {
ft.make_selection_visible(BrowserState::page_height(screen));
self.filtered_tree = filtered_tree;
}
+
+ } else if self.displayed_tree().is_missing_git_status_computation() {
+ let root_path = self.displayed_tree().root().to_path_buf();
+ let git_status = dam.try_compute(||
+ git_status_computer::compute_tree_status(root_path)
+ );
+ debug!("computation result: {:?}", &git_status);
+ self.displayed_tree_mut().git_status = git_status;
+ debug!(
+ "self.displayed_tree().git_status = {:?}",
+ &self.displayed_tree().git_status,
+ );
+
+ debug!(
+ "AFTERCOMPUT self.displayed_tree().is_missing_git_status_computation() = {}",
+ self.displayed_tree().is_missing_git_status_computation(),
+ );
+
+
+ //if self.options.show_git_file_info {
+ // let root_path = &self.blines[self.root_id].path;
+ // if let Ok(git_repo) = Repository::discover(root_path) {
+ // tree.git_status = time!(
+ // Debug,
+ // "TreeGitStatus::from",
+ // TreeGitStatus::from(&git_repo),
+ // );
+ // let repo_root_path = git_repo.path().parent().unwrap();
+ // for mut line in tree.lines.iter_mut() {
+ // if let Some(relative_path) = pathdiff::diff_paths(&line.path, &repo_root_path) {
+ // line.git_status = time!(
+ // Debug,
+ // "LineGitStatus",
+ // &relative_path,
+ // LineGitStatus::from(&git_repo, &relative_path),
+ // );
+ // };
+ // }
+ // }
+ //}
+
+
+
} else {
- self.displayed_tree_mut().fetch_some_missing_dir_size(tl);
+ self.displayed_tree_mut().fetch_some_missing_dir_size(dam);
}
}
diff --git a/src/browser_verbs.rs b/src/browser_verbs.rs
index 6c9e6cb..998d7bc 100644
--- a/src/browser_verbs.rs
+++ b/src/browser_verbs.rs
@@ -9,7 +9,7 @@ use {
flat_tree::Tree,
help_states::HelpState,
screens::Screen,
- task_sync::TaskLifetime,
+ task_sync::Dam,
tree_options::TreeOptions,
verb_invocation::VerbInvocation,
verbs::{Verb, VerbExecutor},
@@ -24,7 +24,7 @@ fn focus_path(path: PathBuf, screen: &mut Screen, tree: &Tree) -> AppStateCmdRes
path,
tree.options.clone(),
screen,
- &TaskLifetime::unlimited(),
+ &Dam::unlimited(),
),
Command::from_pattern(&tree.options.pattern),
)
@@ -95,7 +95,7 @@ impl VerbExecutor for BrowserState {
path.to_path_buf(),
self.displayed_tree().options.without_pattern(),
screen,
- &TaskLifetime::unlimited(),
+ &Dam::unlimited(),
),
Command::new(),
),
diff --git a/src/displayable_tree.rs b/src/displayable_tree.rs
index 4a1f13b..7565879 100644
--- a/src/displayable_tree.rs
+++ b/src/displayable_tree.rs
@@ -3,6 +3,7 @@ use {
errors::ProgramError,
file_sizes::FileSize,
flat_tree::{LineType, Tree, TreeLine},
+ task_sync::ComputationResult,
git_status_display::GitStatusDisplay,
patterns::Pattern,
skin::Skin,
@@ -265,7 +266,7 @@ impl<'s, 't> DisplayableTree<'s, 't> {
self.extend_line(f, selected)?;
let title_len = title.chars().count();
if title_len < self.area.width as usize {
- if let Some(git_status) = &self.tree.git_status {
+ if let ComputationResult::Done(git_status) = &self.tree.git_status {
// git status is displayed if there's enough space for it
let git_status_display = GitStatusDisplay::from(
git_status,
@@ -323,7 +324,7 @@ impl<'s, 't> DisplayableTree<'s, 't> {
if line_index < tree.lines.len() {
let line = &tree.lines[line_index];
selected = self.in_app && line_index == tree.selection;
- if tree.git_status.is_some() {
+ if !tree.git_status.is_none() {
self.write_line_git_status(f, line)?;
}
for depth in 0..line.depth {
diff --git a/src/file_sizes/file_sizes_default.rs b/src/file_sizes/file_sizes_default.rs
index ed5832d..af32a0d 100644
--- a/src/file_sizes/file_sizes_default.rs
+++ b/src/file_sizes/file_sizes_default.rs
@@ -1,7 +1,7 @@
//! size computation for non linux
use {
- crate::task_sync::TaskLifetime,
+ crate::task_sync::Dam,
crossbeam::{channel::unbounded, sync::WaitGroup},
std::{
fs,
@@ -16,7 +16,7 @@ use {
// Note that this version doesn't try to compute the real size taken
// on disk but report the value given by the `len` function
-pub fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
+pub fn compute_dir_size(path: &Path, dam: &Dam) -> Option<u64> {
let size = Arc::new(AtomicUsize::new(0));
// this MPMC channel contains the directory paths which must be handled
diff --git a/src/file_sizes/file_sizes_unix.rs b/src/file_sizes/file_sizes_unix.rs
index 8491699..186bf21 100644
--- a/src/file_sizes/file_sizes_unix.rs
+++ b/src/file_sizes/file_sizes_unix.rs
@@ -1,6 +1,10 @@
use {
- crate::task_sync::TaskLifetime,
- crossbeam::{channel::unbounded, sync::WaitGroup},
+ crate::task_sync::Dam,
+ crossbeam::{
+ channel::unbounded,
+ //crossbeam_utils::thread,
+ sync::WaitGroup,
+ },
std::{
collections::HashSet,
fs,
@@ -16,7 +20,7 @@ use {
super::FileSize,
};
-pub fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
+pub fn compute_dir_size(path: &Path, dam: &Dam) -> Option<u64> {
debug!("compute size of dir {:?} --------------- ", path);
let inodes = Arc::new(Mutex::new(HashSet::<u64>::default())); // to avoid counting twice an inode
// the computation is done on blocks of 512 bytes
@@ -39,9 +43,9 @@ pub fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
let busy = Arc::clone(&busy);
let wg = wg.clone();
let (dirs_sender, dirs_receiver) = (dirs_sender.clone(), dirs_receiver.clone());
- let tl = tl.clone();
let inodes = inodes.clone();
- thread::spawn(move || {
+ let observer = dam.observer();
+ thread::spawn(move|| {
loop {
let o = dirs_receiver.recv_timeout(period);
if let Ok(Some(open_dir)) = o {
@@ -67,7 +71,7 @@ pub fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
} else if busy.load(Ordering::Relaxed) < 1 {
break;
}
- if tl.is_expired() {
+ if observer.has_event() {
break;
}
}
@@ -76,7 +80,7 @@ pub fn compute_dir_size(path: &Path, tl: &TaskLifetime) -> Option<u64> {
}
wg.wait();
- if tl.is_expired() {
+ if dam.has_event() {
return None;
}
let blocks = blocks.load(Ordering::Relaxed);
diff --git a/src/file_sizes/mod.rs b/src/file_sizes/mod.rs
index d6efd74..2220445 100644
--- a/src/file_sizes/mod.rs
+++ b/src/file_sizes/mod.rs
@@ -5,7 +5,7 @@
/// twice an inode.
///
use {
- crate::task_sync::TaskLifetime,
+ crate::task_sync::Dam,
std::{
collections::HashMap,
fmt,
@@ -47,12 +47,12 @@ impl FileSize {
/// Return the size of the directory, either by computing it of by
/// fetching it from cache.
/// If the lifetime expires before complete computation, None is returned.
- pub fn from_dir(path: &Path, tl: &TaskLifetime) -> Option<Self> {
+ pub fn from_dir(path: &Path, dam: &Dam) -> Option<Self> {
let mut size_cache = SIZE_CACHE_MUTEX.lock().unwrap();
if let Some(s) = size_cache.get(path) {
return Some(Self::new(*s, false));
}
- if let Some(s) = time!(Debug, "size sum", path, compute_dir_size(path, tl)) {
+ if let Some(s) = time!(Debug, "size sum", path, compute_dir_size(path, dam)) {
size_cache.insert(PathBuf::from(path), s);
Some(FileSize::new(s, false))
} else {
diff --git a/src/flat_tree.rs b/src/flat_tree.rs
index 0ce5f0b..8d531cd 100644
--- a/src/flat_tree.rs
+++ b/src/flat_tree.rs
@@ -8,8 +8,11 @@ use {
LineGitStatus,
TreeGitStatus,
},
+ task_sync::{
+ ComputationResult,
+ },
selection_type::SelectionType,
- task_sync::TaskLifetime,
+ task_sync::Dam,
tree_build::TreeBuilder,
tree_options::TreeOptions,
},
@@ -65,7 +68,7 @@ pub struct Tree {
pub scroll: i32, // the number of lines at the top hidden because of scrolling
pub nb_gitignored: u32, // number of times a gitignore pattern excluded a file
pub total_search: bool, // whether the search was made on all children
- pub git_status: Option<TreeGitStatus>,
+ pub git_status: ComputationResult<TreeGitStatus>,
}
impl TreeLine {
@@ -191,11 +194,11 @@ impl Tree {
self.root().to_path_buf(),
self.options.clone(),
page_height,
- )?;
+ )?;
let mut tree = builder.build(
- &TaskLifetime::unlimited(),
false, // on refresh we always do a non total search
- ).unwrap(); // should not fail
+ &Dam::unlimited(),
+ ).unwrap(); // should not fail
// we save the old selection to try restore it
let selected_path = self.selected_line().path.to_path_buf();
mem::swap(&mut self.lines, &mut tree.lines);
@@ -447,6 +450,11 @@ impl Tree {
)
}
+ pub fn is_missing_git_status_computation(&self) -> bool {
+ self.options.show_git_file_info
+ && self.git_status.is_not_computed()
+ }
+
pub fn fetch_file_sizes(&mut self) {
for i in 1..self.lines.len() {
if self.lines[i].is_file() {
@@ -460,10 +468,10 @@ impl Tree {
///
/// To compute the size of all of them, this should be called until
/// has_dir_missing_size returns false
- pub fn fetch_some_missing_dir_size(&mut self, tl: &TaskLifetime) {
+ pub fn fetch_some_missing_dir_size(&mut self, dam: &Dam) {
for i in 1..self.lines.len() {
if self.lines[i].size.is_none() && self.lines[i].line_type == LineType::Dir {
- self.lines[i].size = FileSize::from_dir(&self.lines[i].path, tl);
+ self.lines[i].size = FileSize::from_dir(&self.lines[i].path, dam);
self.sort_siblings_by_size();
return;
}
diff --git a/src/git_status_computer.rs b/src/git_status_computer.rs
new file mode 100644
index 0000000..4511f3e
--- /dev/null
+++ b/src/git_status_computer.rs
@@ -0,0 +1,69 @@
+
+use {
+ crate::{
+ git_status::*,
+ task_sync::{
+ Dam,
+ ComputationResult,
+ },
+ },
+ git2::{
+ Repository,
+ },
+ //rayon::{
+ // ThreadPool,
+ // ThreadPoolBuilder,
+ //},
+ crossbeam::{channel::bounded, sync::WaitGroup},
+ std::{
+ collections::HashSet,
+ fs,
+ os::unix::fs::MetadataExt,
+ path::{Path, PathBuf},
+ sync::{
+ atomic::{AtomicIsize, AtomicU64, Ordering},
+ Arc, Mutex,
+ },
+ thread,
+ time::Duration,
+ },
+};
+
+
+//struct Computer {
+//
+//}
+//impl Computer {
+// pub fn new() -> Self {
+//
+// }
+//}
+
+
+pub fn compute_tree_status(root_path: PathBuf) -> ComputationResult<TreeGitStatus> {
+ match Repository::discover(root_path) {
+ Ok(git_repo) => {
+ debug!("repo opened");
+ for _ in 0..20 {
+ time!(
+ Debug,
+ "compute_tree_status",
+ TreeGitStatus::from(&git_repo),
+ );
+ }
+ let tree_git_status = time!(
+ Debug,
+ "compute_tree_status",
+ TreeGitStatus::from(&git_repo),
+ );
+ match tree_git_status {
+ Some(gs) => ComputationResult::Done(gs),
+ None => ComputationResult::None,
+ }
+ }
+ Err(e) => {
+ debug!("failed to discover repo: {:?}", e);
+ ComputationResult::None
+ }
+ }
+}
diff --git a/src/help_states.rs b/src/help_states.rs
index 6e6011d..673662a 100644
--- a/src/help_states.rs
+++ b/src/help_states.rs
@@ -8,7 +8,7 @@ use {
help_content,
screens::Screen,
status::Status,
- task_sync::TaskLifetime,
+ task_sync::Dam,
verb_store::PrefixSearchResult,
verbs::VerbExecutor,
},
@@ -88,7 +88,7 @@ impl AppState for HelpState {
Command::new()
}
- fn do_pending_task(&mut self, _screen: &mut Screen, _tl: &TaskLifetime) {
+ fn do_pending_task(&mut self, _screen: &mut Screen, _dam: &mut Dam) {
unreachable!();
}
diff --git a/src/help_verbs.rs b/src/help_verbs.rs
index 89ffcee..bccad02 100644
--- a/src/help_verbs.rs
+++ b/src/help_verbs.rs
@@ -10,7 +10,7 @@ use crate::{
external::{self, Launchable},
help_states::HelpState,
screens::Screen,
- task_sync::TaskLifetime,
+ task_sync::Dam,
tree_options::TreeOptions,
verb_invocation::VerbInvocation,
verbs::{Verb, VerbExecutor},
@@ -34,7 +34,7 @@ impl VerbExecutor for HelpState {
conf::dir(),
TreeOptions::default(),
screen,
- &TaskLifetime::unlimited(),
+ &Dam::unlimited(),
),
Command::new(),
),
diff --git a/src/lib.rs b/src/lib.rs
index b6fa1d6..89c8c20 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,6 @@
#[macro_use]
+extern crate crossbeam;
+#[macro_use]
extern crate minimad;
#[macro_use]
extern crate lazy_static;
@@ -28,6 +30,7 @@ pub mod flat_tree;
pub mod fuzzy_patterns;
pub mod git_ignore;
pub mod git_status;
+pub mod git_status_computer;
pub mod git_status_display;
pub mod help_content;
pub mod help_states;
diff --git a/src/task_sync.rs b/src/task_sync.rs
index 926d9ed..db3474f 100644
--- a/src/task_sync.rs
+++ b/src/task_sync.rs
@@ -1,40 +1,162 @@
+
use {
+ crossbeam::channel::{
+ self,
+ bounded,
+ Receiver,
+ },
lazy_static::lazy_static,
- std::sync::{
- atomic::{AtomicUsize, Ordering},
- Arc,
+ std::{
+ thread,
},
+ termimad::Event,
};
-/// a TL initialized from an Arc<AtomicUsize> stays
-/// alive as long as the passed arc doesn't change.
-/// When it changes, is_expired returns true
#[derive(Debug, Clone)]
-pub struct TaskLifetime {
- initial_value: usize,
- external_value: Arc<AtomicUsize>,
+pub enum ComputationResult<T> {
+ NotComputed, // not computed but will probably be
+ Done(T),
+ None, // nothing to compute, cancelled, failed, etc.
+}
+impl<T> ComputationResult<T> {
+ pub fn is_done(&self) -> bool {
+ match &self {
+ Self::Done(_) => true,
+ _ => false,
+ }
+ }
+ pub fn is_not_computed(&self) -> bool {
+ match &self {
+ Self::NotComputed => true,
+ _ => false,
+ }
+ }
+ pub fn is_none(&self) -> bool {
+ match &self {
+ Self::None => true,
+ _ => false,
+ }
+ }
+}
+
+/// a dam is used in broot to manage long computations and,
+/// when the user presses a key, either tell the computation
+/// to stop (the computation function checking `has_event`)
+/// or drop the computation.
+pub struct Dam {
+ receiver: Receiver<Event>,
+ in_dam: Option<Event>,
}
-impl TaskLifetime {
- pub fn new(external_value: Arc<AtomicUsize>) -> TaskLifetime {
- TaskLifetime {
- initial_value: external_value.load(Ordering::Relaxed),
- external_value,
+// cache problem: we must receive the result of dropped
+// computattion to use it if we receive the same
+// computation key later
+impl Dam {
+ pub fn from(receiver: Receiver<Event>) -> Self {
+ Self {
+ receiver,
+ in_dam: None,
}
}
- pub fn unlimited() -> TaskLifetime {
- // Use a global static Arc<AtomicUsize> so that we don't have to
- // allocate more than once
- lazy_static! {
- static ref ZERO: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));
+ pub fn unlimited() -> Self {
+ Self::from(channel::never())
+ }
+
+ /// provide an observer which can be used for periodic
+ /// check a task can be used.
+ /// The observer can safely be moved to another thread
+ /// but Be careful not to use it
+ /// after the event listener started again. In any case
+ /// using try_compute should be prefered for immediate
+ /// return to the ui thread.
+
+ pub fn observer(&self) -> DamObserver {
+ DamObserver::from(self)
+ }
+
+ /// launch the computation on a new thread and return
+ /// when it finishes or when a new event appears on
+ /// the channel
+ pub fn try_compute<T: Send + 'static, F: Send + 'static + FnOnce() -> ComputationResult<T>>(
+ &mut self,
+ f: F,
+ ) -> ComputationResult<T> {
+ let (comp_sender, comp_receiver) = bounded(1);
+ thread::spawn(move|| {
+ let comp_res = time!(Debug, "comp in dam", f());
+ if comp_sender.send(comp_res).is_err() {
+ debug!("no channel at end of computation");
+ }
+ });
+ self.select(comp_receiver)
+ }
+
+ fn select<T>(
+ &mut self,
+ computation_channel: Receiver<ComputationResult<T>>,
+ ) -> ComputationResult<T> {
+ if self.in_dam.is_some() {
+ // should probably not happen
+ debug!("There's already an event in dam");
+ ComputationResult::None
+ } else {
+ //
+ debug!("start select! in dam");
+ select! {
+ recv(self.receiver) -> event => {
+ // interruption
+ debug!("dam interrupts computation");
+ self.in_dam = event.ok();
+ ComputationResult::None
+ }
+ recv(computation_channel) -> comp_res => {
+ // computation finished
+ debug!("computation passes dam");
+ comp_res.unwrap_or(ComputationResult::None)
+ }
+ }
}
+ }
+
+ /// non blocking
+ pub fn has_event(&self) -> bool {
+ !self.receiver.is_empty()
+ }
- TaskLifetime {
- initial_value: 0,
- external_value: ZERO.clone(),
+ /// block until next event (including the one which
+ /// may have been pushed back into the dam).
+ /// no event means the source is dead (i.e. we
+ /// must quit broot)
+ /// There's no event kept in dam after this call.
+ pub fn next_event(&mut self) -> Option<Event> {
+ if self.in_dam.is_some() {
+ self.in_dam.take()
+ } else {
+ match self.receiver.recv() {
+ Ok(event) => Some(event),
+ Err(_) => {
+ debug!("dead dam"); // should be logged once
+ None
+ }
+ }
}
}
- pub fn is_expired(&self) -> bool {
- self.initial_value != self.external_value.load(Ordering::Relaxed)
+}
+
+pub struct DamObserver {
+ receiver: Receiver<Event>,
+}
+impl DamObserver {
+ pub fn from(dam: &Dam) -> Self {
+ Self {
+ receiver: dam.receiver.clone()
+ }
+ }
+ /// be careful that this can be used as a thread
+ /// stop condition only before the event receiver
+ /// start being active to avoid a race condition.
+ pub fn has_event(&self) -> bool {
+ !self.receiver.is_empty()
}
}
+
diff --git a/src/tree_build/builder.rs b/src/tree_build/builder.rs
index 8ab1263..c7b1951 100644
--- a/src/tree_build/builder.rs
+++ b/src/tree_build/builder.rs
@@ -4,13 +4,15 @@ use {
flat_tree::{Tree, TreeLine},
git_status::{
LineGitStatus,
- TreeGitStatus,
+ },
+ task_sync::{
+ ComputationResult,
},
git_ignore::{
GitIgnorer,
GitIgnoreChain,
},
- task_sync::TaskLifetime,
+ task_sync::Dam,
tree_options::{
TreeOptions,
},
@@ -221,8 +223,8 @@ impl TreeBuilder {
/// If there's a pattern, we try to gather more lines that will be sorted afterwards.
fn gather_lines(
&mut self,
- task_lifetime: &TaskLifetime,
total_sea