mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-18 13:55:23 -05:00
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
3 changed files with 75 additions and 33 deletions
32
Cargo.lock
generated
32
Cargo.lock
generated
|
@ -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]]
|
||||
|
@ -2056,6 +2047,21 @@ dependencies = [
|
|||
"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]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue