Allow specifying polling/debounce frequency.

pull/3/head
Joshua Potter 2021-12-31 09:41:44 -05:00
parent 7db0656e89
commit c6bd229640
3 changed files with 38 additions and 12 deletions

View File

@ -14,9 +14,6 @@ use std::time::Duration;
// TODO(jrpotter): Add logging. // TODO(jrpotter): Add logging.
// TODO(jrpotter): Add pid file to only allow one daemon at a time. // TODO(jrpotter): Add pid file to only allow one daemon at a time.
// Used for both polling and debouncing file system events.
const WATCH_FREQUENCY: Duration = Duration::from_secs(5);
// ======================================== // ========================================
// Polling // Polling
// ======================================== // ========================================
@ -49,7 +46,7 @@ fn resolve_pending(tx: &Sender<DebouncedEvent>, pending: &HashSet<PathBuf>) -> V
to_remove to_remove
} }
fn poll_pending(tx: Sender<DebouncedEvent>, rx: Receiver<PollEvent>) { fn poll_pending(tx: Sender<DebouncedEvent>, rx: Receiver<PollEvent>, freq_secs: u64) {
let mut pending = HashSet::new(); let mut pending = HashSet::new();
loop { loop {
match rx.try_recv() { match rx.try_recv() {
@ -61,7 +58,7 @@ fn poll_pending(tx: Sender<DebouncedEvent>, rx: Receiver<PollEvent>) {
resolve_pending(&tx, &pending).iter().for_each(|r| { resolve_pending(&tx, &pending).iter().for_each(|r| {
pending.remove(r); pending.remove(r);
}); });
thread::sleep(WATCH_FREQUENCY); thread::sleep(Duration::from_secs(freq_secs));
} }
Err(TryRecvError::Disconnected) => panic!("Polling channel closed."), Err(TryRecvError::Disconnected) => panic!("Polling channel closed."),
} }
@ -142,7 +139,7 @@ impl<'a> WatchState<'a> {
// Daemon // Daemon
// ======================================== // ========================================
pub fn launch(mut config: PathConfig) -> Result<(), Box<dyn Error>> { pub fn launch(mut config: PathConfig, freq_secs: u64) -> Result<(), Box<dyn Error>> {
let (poll_tx, poll_rx) = channel(); let (poll_tx, poll_rx) = channel();
let (watch_tx, watch_rx) = channel(); let (watch_tx, watch_rx) = channel();
let watch_tx1 = watch_tx.clone(); let watch_tx1 = watch_tx.clone();
@ -150,12 +147,12 @@ pub fn launch(mut config: PathConfig) -> Result<(), Box<dyn Error>> {
// watch, but this fails if no file exists at the given path. In these // watch, but this fails if no file exists at the given path. In these
// cases, we rely on a basic polling strategy to check if the files ever // cases, we rely on a basic polling strategy to check if the files ever
// come into existence. // come into existence.
thread::spawn(move || poll_pending(watch_tx, poll_rx)); thread::spawn(move || poll_pending(watch_tx, poll_rx, freq_secs));
// Track our original config file separately from the other files that may // Track our original config file separately from the other files that may
// be defined in the config. We want to make sure we're always alerted on // be defined in the config. We want to make sure we're always alerted on
// changes to it for hot reloading purposes, and not worry that our wrapper // changes to it for hot reloading purposes, and not worry that our wrapper
// will ever clear it from its watch state. // will ever clear it from its watch state.
let mut watcher: RecommendedWatcher = Watcher::new(watch_tx1, WATCH_FREQUENCY)?; let mut watcher: RecommendedWatcher = Watcher::new(watch_tx1, Duration::from_secs(freq_secs))?;
watcher.watch(&config.0, RecursiveMode::NonRecursive)?; watcher.watch(&config.0, RecursiveMode::NonRecursive)?;
let mut state = WatchState::new(poll_tx, &mut watcher)?; let mut state = WatchState::new(poll_tx, &mut watcher)?;
state.update(&config); state.update(&config);

View File

@ -13,8 +13,8 @@ pub fn run_add(_config: PathConfig) -> Result<(), config::Error> {
Ok(()) Ok(())
} }
pub fn run_daemon(config: PathConfig) -> Result<(), Box<dyn Error>> { pub fn run_daemon(config: PathConfig, freq_secs: u64) -> Result<(), Box<dyn Error>> {
daemon::launch(config)?; daemon::launch(config, freq_secs)?;
Ok(()) Ok(())
} }

View File

@ -19,7 +19,25 @@ fn main() {
.takes_value(true), .takes_value(true),
) )
.subcommand(App::new("add").about("Add new configuration to local repository")) .subcommand(App::new("add").about("Add new configuration to local repository"))
.subcommand(App::new("daemon").about("Start up a new homesync daemon")) .subcommand(
App::new("daemon")
.about("Start up a new homesync daemon")
.arg(
Arg::new("frequency")
.short('f')
.long("frequency")
.value_name("FREQUENCY")
.help("How often (in seconds) we poll/debounce file system changes")
.long_help(
"There exists a balance between how responsive changes are \
made and how expensive it is to look for changes. \
Empirically we found the default value to offer a nice \
compromise but this can be tweaked based on preference.",
)
.takes_value(true)
.default_value("5"),
),
)
.subcommand(App::new("init").about("Initialize the homesync local repository")) .subcommand(App::new("init").about("Initialize the homesync local repository"))
.subcommand(App::new("list").about("See which packages homesync manages")) .subcommand(App::new("list").about("See which packages homesync manages"))
.subcommand(App::new("pull").about("Pull remote repository into local repository")) .subcommand(App::new("pull").about("Pull remote repository into local repository"))
@ -44,7 +62,18 @@ fn dispatch(matches: clap::ArgMatches) -> Result<(), Box<dyn Error>> {
let config = homesync::config::load(&candidates)?; let config = homesync::config::load(&candidates)?;
match subcommand { match subcommand {
Some(("add", _)) => Ok(homesync::run_add(config)?), Some(("add", _)) => Ok(homesync::run_add(config)?),
Some(("daemon", _)) => Ok(homesync::run_daemon(config)?), Some(("daemon", matches)) => {
let freq_secs: u64 = match matches.value_of("frequency") {
Some(f) => f.parse().unwrap_or(0),
None => 5,
};
if freq_secs > 0 {
homesync::run_daemon(config, freq_secs)?;
} else {
eprintln!("Invalid frequency. Expected a positive integer.");
}
Ok(())
}
Some(("list", _)) => Ok(homesync::run_list(config)?), Some(("list", _)) => Ok(homesync::run_list(config)?),
Some(("pull", _)) => Ok(homesync::run_pull(config)?), Some(("pull", _)) => Ok(homesync::run_pull(config)?),
Some(("push", _)) => Ok(homesync::run_push(config)?), Some(("push", _)) => Ok(homesync::run_push(config)?),