fix(core): fix config watcher debouncing mechanism. Fix #769
This commit is contained in:
parent
d1ebcf62c8
commit
2c25d60e87
|
@ -17,36 +17,42 @@
|
|||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{
|
||||
path::Path,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use std::{path::Path, time::Duration};
|
||||
|
||||
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
|
||||
use anyhow::Result;
|
||||
use crossbeam::channel::Sender;
|
||||
use crossbeam::{channel::Sender, select};
|
||||
use log::{error, info, warn};
|
||||
|
||||
const WATCHER_DEBOUNCE_DURATION: u64 = 1;
|
||||
const WATCHER_NOTIFY_DELAY_MS: u64 = 500;
|
||||
const WATCHER_DEBOUNCE_DURATION_MS: u64 = 1000;
|
||||
|
||||
pub fn initialize_and_spawn(config_dir: &Path, watcher_notify: Sender<()>) -> Result<()> {
|
||||
let config_dir = config_dir.to_path_buf();
|
||||
|
||||
let (debounce_tx, debounce_rx) = crossbeam::channel::unbounded();
|
||||
|
||||
std::thread::Builder::new()
|
||||
.name("watcher".to_string())
|
||||
.spawn(move || {
|
||||
watcher_main(&config_dir, &watcher_notify);
|
||||
watcher_main(&config_dir, debounce_tx);
|
||||
})?;
|
||||
|
||||
std::thread::Builder::new()
|
||||
.name("watcher-debouncer".to_string())
|
||||
.spawn(move || {
|
||||
debouncer_main(debounce_rx, &watcher_notify);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn watcher_main(config_dir: &Path, watcher_notify: &Sender<()>) {
|
||||
fn watcher_main(config_dir: &Path, debounce_tx: crossbeam::channel::Sender<()>) {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
let mut watcher: RecommendedWatcher =
|
||||
Watcher::new(tx, Duration::from_secs(WATCHER_DEBOUNCE_DURATION))
|
||||
Watcher::new(tx, Duration::from_millis(WATCHER_NOTIFY_DELAY_MS))
|
||||
.expect("unable to create file watcher");
|
||||
|
||||
watcher
|
||||
|
@ -55,8 +61,6 @@ fn watcher_main(config_dir: &Path, watcher_notify: &Sender<()>) {
|
|||
|
||||
info!("watching for changes in path: {:?}", config_dir);
|
||||
|
||||
let mut last_event_arrival = Instant::now();
|
||||
|
||||
loop {
|
||||
let should_reload = match rx.recv() {
|
||||
Ok(event) => {
|
||||
|
@ -92,16 +96,35 @@ fn watcher_main(config_dir: &Path, watcher_notify: &Sender<()>) {
|
|||
}
|
||||
};
|
||||
|
||||
// Send only one event, otherwise we could run the risk of useless reloads or even race conditions.
|
||||
if should_reload
|
||||
&& last_event_arrival.elapsed() > std::time::Duration::from_secs(WATCHER_DEBOUNCE_DURATION)
|
||||
{
|
||||
if should_reload {
|
||||
if let Err(error) = debounce_tx.send(()) {
|
||||
error!(
|
||||
"unable to send watcher file changed event to debouncer: {}",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn debouncer_main(debounce_rx: crossbeam::channel::Receiver<()>, watcher_notify: &Sender<()>) {
|
||||
let mut has_received_event = false;
|
||||
|
||||
loop {
|
||||
select! {
|
||||
recv(debounce_rx) -> _ => {
|
||||
has_received_event = true;
|
||||
},
|
||||
default(Duration::from_millis(WATCHER_DEBOUNCE_DURATION_MS)) => {
|
||||
if has_received_event {
|
||||
if let Err(error) = watcher_notify.send(()) {
|
||||
error!("unable to send watcher file changed event: {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
last_event_arrival = Instant::now();
|
||||
has_received_event = false;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user