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/>.
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::{
|
use std::{path::Path, time::Duration};
|
||||||
path::Path,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossbeam::channel::Sender;
|
use crossbeam::{channel::Sender, select};
|
||||||
use log::{error, info, warn};
|
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<()> {
|
pub fn initialize_and_spawn(config_dir: &Path, watcher_notify: Sender<()>) -> Result<()> {
|
||||||
let config_dir = config_dir.to_path_buf();
|
let config_dir = config_dir.to_path_buf();
|
||||||
|
|
||||||
|
let (debounce_tx, debounce_rx) = crossbeam::channel::unbounded();
|
||||||
|
|
||||||
std::thread::Builder::new()
|
std::thread::Builder::new()
|
||||||
.name("watcher".to_string())
|
.name("watcher".to_string())
|
||||||
.spawn(move || {
|
.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(())
|
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 (tx, rx) = std::sync::mpsc::channel();
|
||||||
|
|
||||||
let mut watcher: RecommendedWatcher =
|
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");
|
.expect("unable to create file watcher");
|
||||||
|
|
||||||
watcher
|
watcher
|
||||||
|
@ -55,8 +61,6 @@ fn watcher_main(config_dir: &Path, watcher_notify: &Sender<()>) {
|
||||||
|
|
||||||
info!("watching for changes in path: {:?}", config_dir);
|
info!("watching for changes in path: {:?}", config_dir);
|
||||||
|
|
||||||
let mut last_event_arrival = Instant::now();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let should_reload = match rx.recv() {
|
let should_reload = match rx.recv() {
|
||||||
Ok(event) => {
|
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 {
|
||||||
if should_reload
|
if let Err(error) = debounce_tx.send(()) {
|
||||||
&& last_event_arrival.elapsed() > std::time::Duration::from_secs(WATCHER_DEBOUNCE_DURATION)
|
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(()) {
|
if let Err(error) = watcher_notify.send(()) {
|
||||||
error!("unable to send watcher file changed event: {}", error);
|
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