threaded watcher creation and arg for config

new argument `polling` allows to force watcher to use polling instead of file system events.
this should address the issue in #1436 and maybe even #1437
This commit is contained in:
extrawurst 2022-11-21 18:09:08 +01:00
parent 8cdb02349f
commit bbcadcb5d1
3 changed files with 80 additions and 28 deletions

View File

@ -2,8 +2,8 @@ use crate::bug_report;
use anyhow::{anyhow, Result};
use asyncgit::sync::RepoPath;
use clap::{
crate_authors, crate_description, crate_name, crate_version, Arg,
Command as ClapApp,
crate_authors, crate_description, crate_name, crate_version,
value_parser, Arg, Command as ClapApp,
};
use simplelog::{Config, LevelFilter, WriteLogger};
use std::{
@ -15,6 +15,7 @@ use std::{
pub struct CliArgs {
pub theme: PathBuf,
pub repo_path: RepoPath,
pub poll_watcher: bool,
}
pub fn process_cmdline() -> Result<CliArgs> {
@ -46,17 +47,20 @@ pub fn process_cmdline() -> Result<CliArgs> {
.get_one::<String>("theme")
.map_or_else(|| PathBuf::from("theme.ron"), PathBuf::from);
if get_app_config_path()?.join(&arg_theme).is_file() {
Ok(CliArgs {
theme: get_app_config_path()?.join(arg_theme),
repo_path,
})
let theme = if get_app_config_path()?.join(&arg_theme).is_file() {
get_app_config_path()?.join(arg_theme)
} else {
Ok(CliArgs {
theme: get_app_config_path()?.join("theme.ron"),
repo_path,
})
}
get_app_config_path()?.join("theme.ron")
};
let arg_poll: bool =
*arg_matches.get_one("poll").unwrap_or(&false);
Ok(CliArgs {
theme,
poll_watcher: arg_poll,
repo_path,
})
}
fn app() -> ClapApp {
@ -90,6 +94,13 @@ fn app() -> ClapApp {
.long("logging")
.num_args(0),
)
.arg(
Arg::new("poll")
.help("Poll folder for changes instead of using file system events. This can be useful if you run into issues with maximum # of file descriptors")
.long("polling")
.num_args(0)
.value_parser(value_parser!(bool)),
)
.arg(
Arg::new("bugreport")
.help("Generate a bug report")

View File

@ -150,6 +150,7 @@ fn main() -> Result<()> {
theme,
key_config.clone(),
&input,
cliargs.poll_watcher,
&mut terminal,
)?;
@ -170,13 +171,17 @@ fn run_app(
theme: Theme,
key_config: KeyConfig,
input: &Input,
poll_watcher: bool,
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
) -> Result<QuitState, anyhow::Error> {
let (tx_git, rx_git) = unbounded();
let (tx_app, rx_app) = unbounded();
let rx_input = input.receiver();
let watcher = RepoWatcher::new(repo_work_dir(&repo)?.as_str())?;
let watcher = RepoWatcher::new(
repo_work_dir(&repo)?.as_str(),
poll_watcher,
);
let rx_watcher = watcher.receiver();
let spinner_ticker = tick(SPINNER_INTERVAL);

View File

@ -1,8 +1,11 @@
use anyhow::Result;
use crossbeam_channel::{unbounded, Sender};
use notify::{Error, RecommendedWatcher, RecursiveMode};
use notify::{
Config, Error, PollWatcher, RecommendedWatcher, RecursiveMode,
Watcher,
};
use notify_debouncer_mini::{
new_debouncer, DebouncedEvent, Debouncer,
new_debouncer, new_debouncer_opt, DebouncedEvent,
};
use scopetime::scope_time;
use std::{
@ -11,22 +14,23 @@ use std::{
pub struct RepoWatcher {
receiver: crossbeam_channel::Receiver<()>,
#[allow(dead_code)]
debouncer: Debouncer<RecommendedWatcher>,
}
impl RepoWatcher {
pub fn new(workdir: &str) -> Result<Self> {
scope_time!("RepoWatcher::new");
pub fn new(workdir: &str, poll: bool) -> Self {
log::trace!(
"poll watcher: {poll} recommended: {:?}",
RecommendedWatcher::kind()
);
let (tx, rx) = std::sync::mpsc::channel();
let mut debouncer =
new_debouncer(Duration::from_secs(2), None, tx)?;
let workdir = workdir.to_string();
debouncer
.watcher()
.watch(Path::new(workdir), RecursiveMode::Recursive)?;
thread::spawn(move || {
let timeout = Duration::from_secs(2);
create_watcher(poll, timeout, tx, &workdir);
});
let (out_tx, out_rx) = unbounded();
@ -37,10 +41,7 @@ impl RepoWatcher {
}
});
Ok(Self {
debouncer,
receiver: out_rx,
})
Self { receiver: out_rx }
}
///
@ -71,3 +72,38 @@ impl RepoWatcher {
}
}
}
fn create_watcher(
poll: bool,
timeout: Duration,
tx: std::sync::mpsc::Sender<
Result<Vec<DebouncedEvent>, Vec<Error>>,
>,
workdir: &str,
) {
scope_time!("create_watcher");
if poll {
let config = Config::default()
.with_poll_interval(Duration::from_secs(2));
let mut bouncer = new_debouncer_opt::<_, PollWatcher>(
timeout, None, tx, config,
)
.expect("Watch create error");
bouncer
.watcher()
.watch(Path::new(&workdir), RecursiveMode::Recursive)
.expect("Watch error");
std::mem::forget(bouncer);
} else {
let mut bouncer = new_debouncer(timeout, None, tx)
.expect("Watch create error");
bouncer
.watcher()
.watch(Path::new(&workdir), RecursiveMode::Recursive)
.expect("Watch error");
std::mem::forget(bouncer);
};
}