diff options
author | Kirill Chibisov <contact@kchibisov.com> | 2023-02-02 13:13:23 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-02 13:13:23 +0300 |
commit | 799d8f75bb4bcbf06f794dd23971633486f90906 (patch) | |
tree | 1fbd510cf6dd10afad83753d4834922d979b0521 | |
parent | 3354203e571e427b7152999d13af8a1cbd14e0d8 (diff) | |
download | alacritty-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.lock | 32 | ||||
-rw-r--r-- | alacritty/Cargo.toml | 2 | ||||
-rw-r--r-- | alacritty/src/config/monitor.rs | 74 |
3 files changed, 75 insertions, 33 deletions
@@ -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); - } } }); } |