summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Chibisov <contact@kchibisov.com>2023-02-02 13:13:23 +0300
committerGitHub <noreply@github.com>2023-02-02 13:13:23 +0300
commit799d8f75bb4bcbf06f794dd23971633486f90906 (patch)
tree1fbd510cf6dd10afad83753d4834922d979b0521
parent3354203e571e427b7152999d13af8a1cbd14e0d8 (diff)
downloadalacritty-799d8f75bb4bcbf06f794dd23971633486f90906.tar.gz
alacritty-799d8f75bb4bcbf06f794dd23971633486f90906.zip
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.
-rw-r--r--Cargo.lock32
-rw-r--r--alacritty/Cargo.toml2
-rw-r--r--alacritty/src/config/monitor.rs74
3 files changed, 75 insertions, 33 deletions
diff --git a/Cargo.lock b/Cargo.lock
index c3b660b7..1d13cbb4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -28,7 +28,7 @@ dependencies = [
"glutin",
"libc",
"log",
- "notify-debouncer-mini",
+ "notify",
"objc",
"once_cell",
"parking_lot 0.12.1",
@@ -1149,9 +1149,9 @@ dependencies = [
[[package]]
name = "notify"
-version = "5.0.0"
+version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
+checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9"
dependencies = [
"bitflags",
"crossbeam-channel",
@@ -1162,16 +1162,7 @@ dependencies = [
"libc",
"mio 0.8.4",
"walkdir",
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "notify-debouncer-mini"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e23e9fa24f094b143c1eb61f90ac6457de87be6987bc70746e0179f7dbc9007b"
-dependencies = [
- "notify",
+ "windows-sys 0.42.0",
]
[[package]]
@@ -2058,6 +2049,21 @@ dependencies = [
[[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]]
+name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml
index 63edd5c0..0c1fac66 100644
--- a/alacritty/Cargo.toml
+++ b/alacritty/Cargo.toml
@@ -31,7 +31,7 @@ serde_yaml = "0.8"
serde_json = "1"
glutin = { version = "0.30.3", default-features = false, features = ["egl", "wgl"] }
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"
crossfont = { version = "0.5.0", features = ["force_system_fontconfig"] }
copypasta = { version = "0.8.1", default-features = false }
diff --git a/alacritty/src/config/monitor.rs b/alacritty/src/config/monitor.rs
index 622a993b..3548fc02 100644
--- a/alacritty/src/config/monitor.rs
+++ b/alacritty/src/config/monitor.rs
@@ -1,20 +1,19 @@
use std::path::PathBuf;
-use std::sync::mpsc;
-use std::time::Duration;
+use std::sync::mpsc::{self, RecvTimeoutError};
+use std::time::{Duration, Instant};
use log::{debug, error};
-use notify_debouncer_mini::new_debouncer;
-use notify_debouncer_mini::notify::RecursiveMode;
+use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use winit::event_loop::EventLoopProxy;
use alacritty_terminal::thread;
use crate::event::{Event, EventType};
-#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
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>) {
// 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.
let (tx, rx) = mpsc::channel();
- let mut debouncer = match new_debouncer(DEBOUNCE_DELAY, None, tx) {
- Ok(debouncer) => debouncer,
+ let mut watcher = match RecommendedWatcher::new(
+ tx,
+ Config::default().with_poll_interval(FALLBACK_POLLING_TIMEOUT),
+ ) {
+ Ok(watcher) => watcher,
Err(err) => {
error!("Unable to watch config file: {}", err);
return;
@@ -62,7 +64,6 @@ pub fn watch(mut paths: Vec<PathBuf>, event_proxy: EventLoopProxy<Event>) {
parents.sort_unstable();
parents.dedup();
- let watcher = debouncer.watcher();
// Watch all configuration file directories.
for parent in &parents {
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 {
- let events = match rx.recv() {
- Ok(Ok(events)) => events,
+ // We use `recv_timeout` to debounce the events coming from the watcher and reduce
+ // 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)) => {
debug!("Config watcher errors: {:?}", err);
- continue;
},
Err(err) => {
debug!("Config watcher channel dropped unexpectedly: {}", err);
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);
- }
}
});
}