Fix notify doing active polling
The `notify-debouncer-mini` spawn a thread which checks the events every timeout, which is not desired since we want to avoid active polling. This commit re-implements debouncer based on the `RecommendedWatcher` without adding an extra thread on top and not doing any busy-waiting. Fixes #6652.
This commit is contained in:
parent
3354203e57
commit
799d8f75bb
|
@ -28,7 +28,7 @@ dependencies = [
|
||||||
"glutin",
|
"glutin",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"notify-debouncer-mini",
|
"notify",
|
||||||
"objc",
|
"objc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
|
@ -1149,9 +1149,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "5.0.0"
|
version = "5.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
|
checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
@ -1162,16 +1162,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mio 0.8.4",
|
"mio 0.8.4",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"winapi 0.3.9",
|
"windows-sys 0.42.0",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "notify-debouncer-mini"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e23e9fa24f094b143c1eb61f90ac6457de87be6987bc70746e0179f7dbc9007b"
|
|
||||||
dependencies = [
|
|
||||||
"notify",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2056,6 +2047,21 @@ dependencies = [
|
||||||
"windows_x86_64_msvc 0.36.1",
|
"windows_x86_64_msvc 0.36.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc 0.42.1",
|
||||||
|
"windows_i686_gnu 0.42.1",
|
||||||
|
"windows_i686_msvc 0.42.1",
|
||||||
|
"windows_x86_64_gnu 0.42.1",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc 0.42.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.45.0"
|
version = "0.45.0"
|
||||||
|
|
|
@ -31,7 +31,7 @@ serde_yaml = "0.8"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
glutin = { version = "0.30.3", default-features = false, features = ["egl", "wgl"] }
|
glutin = { version = "0.30.3", default-features = false, features = ["egl", "wgl"] }
|
||||||
winit = { version = "0.28.0", default-features = false, features = ["serde"] }
|
winit = { version = "0.28.0", default-features = false, features = ["serde"] }
|
||||||
notify-debouncer-mini = { version = "0.2.1", default-features = false }
|
notify = "5.1.0"
|
||||||
parking_lot = "0.12.0"
|
parking_lot = "0.12.0"
|
||||||
crossfont = { version = "0.5.0", features = ["force_system_fontconfig"] }
|
crossfont = { version = "0.5.0", features = ["force_system_fontconfig"] }
|
||||||
copypasta = { version = "0.8.1", default-features = false }
|
copypasta = { version = "0.8.1", default-features = false }
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc::{self, RecvTimeoutError};
|
||||||
use std::time::Duration;
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use notify_debouncer_mini::new_debouncer;
|
use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
use notify_debouncer_mini::notify::RecursiveMode;
|
|
||||||
use winit::event_loop::EventLoopProxy;
|
use winit::event_loop::EventLoopProxy;
|
||||||
|
|
||||||
use alacritty_terminal::thread;
|
use alacritty_terminal::thread;
|
||||||
|
|
||||||
use crate::event::{Event, EventType};
|
use crate::event::{Event, EventType};
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
|
|
||||||
const DEBOUNCE_DELAY: Duration = Duration::from_millis(10);
|
const DEBOUNCE_DELAY: Duration = Duration::from_millis(10);
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
|
||||||
const DEBOUNCE_DELAY: Duration = Duration::from_millis(1000);
|
/// The fallback for `RecommendedWatcher` polling.
|
||||||
|
const FALLBACK_POLLING_TIMEOUT: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventLoopProxy<Event>) {
|
pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventLoopProxy<Event>) {
|
||||||
// Don't monitor config if there is no path to watch.
|
// Don't monitor config if there is no path to watch.
|
||||||
|
@ -41,8 +40,11 @@ pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventLoopProxy<Event>) {
|
||||||
|
|
||||||
// The Duration argument is a debouncing period.
|
// The Duration argument is a debouncing period.
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let mut debouncer = match new_debouncer(DEBOUNCE_DELAY, None, tx) {
|
let mut watcher = match RecommendedWatcher::new(
|
||||||
Ok(debouncer) => debouncer,
|
tx,
|
||||||
|
Config::default().with_poll_interval(FALLBACK_POLLING_TIMEOUT),
|
||||||
|
) {
|
||||||
|
Ok(watcher) => watcher,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Unable to watch config file: {}", err);
|
error!("Unable to watch config file: {}", err);
|
||||||
return;
|
return;
|
||||||
|
@ -62,7 +64,6 @@ pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventLoopProxy<Event>) {
|
||||||
parents.sort_unstable();
|
parents.sort_unstable();
|
||||||
parents.dedup();
|
parents.dedup();
|
||||||
|
|
||||||
let watcher = debouncer.watcher();
|
|
||||||
// Watch all configuration file directories.
|
// Watch all configuration file directories.
|
||||||
for parent in &parents {
|
for parent in &parents {
|
||||||
if let Err(err) = watcher.watch(parent, RecursiveMode::NonRecursive) {
|
if let Err(err) = watcher.watch(parent, RecursiveMode::NonRecursive) {
|
||||||
|
@ -70,24 +71,59 @@ pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventLoopProxy<Event>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The current debouncing time.
|
||||||
|
let mut debouncing_deadline: Option<Instant> = None;
|
||||||
|
|
||||||
|
// The events accumulated during the debounce period.
|
||||||
|
let mut received_events = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let events = match rx.recv() {
|
// We use `recv_timeout` to debounce the events coming from the watcher and reduce
|
||||||
Ok(Ok(events)) => events,
|
// the amount of config reloads.
|
||||||
|
let event = match debouncing_deadline.as_ref() {
|
||||||
|
Some(debouncing_deadline) => {
|
||||||
|
rx.recv_timeout(debouncing_deadline.saturating_duration_since(Instant::now()))
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let event = rx.recv().map_err(Into::into);
|
||||||
|
// Set the debouncing deadline after receiving the event.
|
||||||
|
debouncing_deadline = Some(Instant::now() + DEBOUNCE_DELAY);
|
||||||
|
event
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Ok(Ok(event)) => match event.kind {
|
||||||
|
EventKind::Any
|
||||||
|
| EventKind::Create(_)
|
||||||
|
| EventKind::Modify(_)
|
||||||
|
| EventKind::Other => {
|
||||||
|
received_events.push(event);
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Err(RecvTimeoutError::Timeout) => {
|
||||||
|
// Go back to polling the events.
|
||||||
|
debouncing_deadline = None;
|
||||||
|
|
||||||
|
if received_events
|
||||||
|
.drain(..)
|
||||||
|
.flat_map(|event| event.paths.into_iter())
|
||||||
|
.any(|path| paths.contains(&path))
|
||||||
|
{
|
||||||
|
// Always reload the primary configuration file.
|
||||||
|
let event = Event::new(EventType::ConfigReload(paths[0].clone()), None);
|
||||||
|
let _ = event_proxy.send_event(event);
|
||||||
|
}
|
||||||
|
},
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
debug!("Config watcher errors: {:?}", err);
|
debug!("Config watcher errors: {:?}", err);
|
||||||
continue;
|
|
||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!("Config watcher channel dropped unexpectedly: {}", err);
|
debug!("Config watcher channel dropped unexpectedly: {}", err);
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if events.iter().any(|e| paths.contains(&e.path)) {
|
|
||||||
// Always reload the primary configuration file.
|
|
||||||
let event = Event::new(EventType::ConfigReload(paths[0].clone()), None);
|
|
||||||
let _ = event_proxy.send_event(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue