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 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
// ========================================
@ -49,7 +46,7 @@ fn resolve_pending(tx: &Sender<DebouncedEvent>, pending: &HashSet<PathBuf>) -> V
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();
loop {
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| {
pending.remove(r);
});
thread::sleep(WATCH_FREQUENCY);
thread::sleep(Duration::from_secs(freq_secs));
}
Err(TryRecvError::Disconnected) => panic!("Polling channel closed."),
}
@ -142,7 +139,7 @@ impl<'a> WatchState<'a> {
// 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 (watch_tx, watch_rx) = channel();
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
// cases, we rely on a basic polling strategy to check if the files ever
// 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
// 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
// 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)?;
let mut state = WatchState::new(poll_tx, &mut watcher)?;
state.update(&config);

View File

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

View File

@ -19,7 +19,25 @@ fn main() {
.takes_value(true),
)
.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("list").about("See which packages homesync manages"))
.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)?;
match subcommand {
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(("pull", _)) => Ok(homesync::run_pull(config)?),
Some(("push", _)) => Ok(homesync::run_push(config)?),