diff options
author | Aram Drevekenin <aram@poor.dev> | 2023-06-16 21:04:22 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-16 21:04:22 +0200 |
commit | b7436742cddc3987df46371428d1be0d0ebfa282 (patch) | |
tree | b46981be175fe5a9558f0ed0571717492b651934 | |
parent | 317bdfc90252058d51b3f2a1847b2074baf5e80e (diff) |
performance(plugins): use a debounced fs watcher (#2546)
* performance(plugins): use a debounced fs watcher
* style(fmt): rustfmt
* fix(macos): use recommended watcher
-rw-r--r-- | Cargo.lock | 24 | ||||
-rw-r--r-- | zellij-server/src/plugins/wasm_bridge.rs | 8 | ||||
-rw-r--r-- | zellij-server/src/plugins/watch_filesystem.rs | 145 | ||||
-rw-r--r-- | zellij-utils/Cargo.toml | 2 | ||||
-rw-r--r-- | zellij-utils/src/lib.rs | 4 |
5 files changed, 127 insertions, 56 deletions
diff --git a/Cargo.lock b/Cargo.lock index b410d17f4..535879b8d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1006,6 +1006,15 @@ dependencies = [ ] [[package]] +name = "file-id" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13be71e6ca82e91bc0cb862bebaac0b2d1924a5a1d970c822b2f98b63fda8c3" +dependencies = [ + "winapi-util", +] + +[[package]] name = "filedescriptor" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1832,6 +1841,19 @@ dependencies = [ ] [[package]] +name = "notify-debouncer-full" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4812c1eb49be776fb8df4961623bdc01ec9dfdc1abe8211ceb09150a2e64219" +dependencies = [ + "crossbeam-channel", + "file-id", + "notify", + "parking_lot 0.12.1", + "walkdir", +] + +[[package]] name = "ntapi" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4424,7 +4446,7 @@ dependencies = [ "log4rs", "miette", "nix 0.23.1", - "notify", + "notify-debouncer-full", "once_cell", "percent-encoding", "regex", diff --git a/zellij-server/src/plugins/wasm_bridge.rs b/zellij-server/src/plugins/wasm_bridge.rs index 0e53ce41e..5ae23be4f 100644 --- a/zellij-server/src/plugins/wasm_bridge.rs +++ b/zellij-server/src/plugins/wasm_bridge.rs @@ -13,7 +13,7 @@ use std::{ }; use wasmer::{Instance, Module, Store, Value}; use zellij_utils::async_std::task::{self, JoinHandle}; -use zellij_utils::notify::{RecommendedWatcher, Watcher}; +use zellij_utils::notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, FileIdMap}; use crate::{ background_jobs::BackgroundJob, screen::ScreenInstruction, thread_bus::ThreadSenders, @@ -50,7 +50,7 @@ pub struct WasmBridge { loading_plugins: HashMap<(PluginId, RunPlugin), JoinHandle<()>>, // plugin_id to join-handle pending_plugin_reloads: HashSet<RunPlugin>, path_to_default_shell: PathBuf, - watcher: Option<RecommendedWatcher>, + watcher: Option<Debouncer<RecommendedWatcher, FileIdMap>>, zellij_cwd: PathBuf, capabilities: PluginCapabilities, client_attributes: ClientAttributes, @@ -530,7 +530,9 @@ impl WasmBridge { for plugin_id in &plugin_ids { drop(self.unload_plugin(*plugin_id)); } - drop(self.watcher.as_mut().map(|w| w.unwatch(&self.zellij_cwd))); + if let Some(watcher) = self.watcher.take() { + watcher.stop_nonblocking(); + } } fn run_plugin_of_plugin_id(&self, plugin_id: PluginId) -> Option<&RunPlugin> { self.loading_plugins diff --git a/zellij-server/src/plugins/watch_filesystem.rs b/zellij-server/src/plugins/watch_filesystem.rs index 864bd45f9..9ac0a320c 100644 --- a/zellij-server/src/plugins/watch_filesystem.rs +++ b/zellij-server/src/plugins/watch_filesystem.rs @@ -3,61 +3,108 @@ use std::path::PathBuf; use crate::thread_bus::ThreadSenders; use std::path::Path; +use std::time::Duration; -use zellij_utils::{data::Event, errors::prelude::*}; +use zellij_utils::notify_debouncer_full::{ + new_debouncer, + notify::{EventKind, RecommendedWatcher, RecursiveMode, Watcher}, + DebounceEventResult, Debouncer, FileIdMap, +}; +use zellij_utils::{data::Event, errors::prelude::Result}; -use zellij_utils::notify::{self, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; -pub fn watch_filesystem(senders: ThreadSenders, zellij_cwd: &Path) -> Result<RecommendedWatcher> { +const DEBOUNCE_DURATION_MS: u64 = 500; + +pub fn watch_filesystem( + senders: ThreadSenders, + zellij_cwd: &Path, +) -> Result<Debouncer<RecommendedWatcher, FileIdMap>> { let path_prefix_in_plugins = PathBuf::from("/host"); let current_dir = PathBuf::from(zellij_cwd); - let mut watcher = notify::recommended_watcher({ - move |res: notify::Result<notify::Event>| match res { - Ok(event) => { - let paths: Vec<PathBuf> = event - .paths - .iter() - .map(|p| { - let stripped_prefix_path = - p.strip_prefix(¤t_dir).unwrap_or_else(|_| p); - path_prefix_in_plugins.join(stripped_prefix_path) + let mut debouncer = new_debouncer( + Duration::from_millis(DEBOUNCE_DURATION_MS), + None, + move |result: DebounceEventResult| match result { + Ok(events) => { + let mut create_events = vec![]; + let mut read_events = vec![]; + let mut update_events = vec![]; + let mut delete_events = vec![]; + for event in events { + match event.kind { + EventKind::Access(_) => read_events.push(event), + EventKind::Create(_) => create_events.push(event), + EventKind::Modify(_) => update_events.push(event), + EventKind::Remove(_) => delete_events.push(event), + _ => {}, + } + } + let create_paths: Vec<PathBuf> = create_events + .drain(..) + .map(|e| { + e.paths + .iter() + .map(|p| { + let stripped_prefix_path = + p.strip_prefix(¤t_dir).unwrap_or_else(|_| p); + path_prefix_in_plugins.join(stripped_prefix_path) + }) + .collect() }) .collect(); - match event.kind { - EventKind::Access(_) => { - let _ = senders.send_to_plugin(PluginInstruction::Update(vec![( - None, - None, - Event::FileSystemRead(paths), - )])); - }, - EventKind::Create(_) => { - let _ = senders.send_to_plugin(PluginInstruction::Update(vec![( - None, - None, - Event::FileSystemCreate(paths), - )])); - }, - EventKind::Modify(_) => { - let _ = senders.send_to_plugin(PluginInstruction::Update(vec![( - None, - None, - Event::FileSystemUpdate(paths), - )])); - }, - EventKind::Remove(_) => { - let _ = senders.send_to_plugin(PluginInstruction::Update(vec![( - None, - None, - Event::FileSystemDelete(paths), - )])); - }, - _ => {}, - } + let read_paths: Vec<PathBuf> = read_events + .drain(..) + .map(|e| { + e.paths + .iter() + .map(|p| { + let stripped_prefix_path = + p.strip_prefix(¤t_dir).unwrap_or_else(|_| p); + path_prefix_in_plugins.join(stripped_prefix_path) + }) + .collect() + }) + .collect(); + let update_paths: Vec<PathBuf> = update_events + .drain(..) + .map(|e| { + e.paths + .iter() + .map(|p| { + let stripped_prefix_path = + p.strip_prefix(¤t_dir).unwrap_or_else(|_| p); + path_prefix_in_plugins.join(stripped_prefix_path) + }) + .collect() + }) + .collect(); + let delete_paths: Vec<PathBuf> = delete_events + .drain(..) + .map(|e| { + e.paths + .iter() + .map(|p| { + let stripped_prefix_path = + p.strip_prefix(¤t_dir).unwrap_or_else(|_| p); + path_prefix_in_plugins.join(stripped_prefix_path) + }) + .collect() + }) + .collect(); + let _ = senders.send_to_plugin(PluginInstruction::Update(vec![ + (None, None, Event::FileSystemRead(read_paths)), + (None, None, Event::FileSystemCreate(create_paths)), + (None, None, Event::FileSystemUpdate(update_paths)), + (None, None, Event::FileSystemDelete(delete_paths)), + ])); }, - Err(e) => log::error!("watch error: {:?}", e), - } - })?; + Err(errors) => errors + .iter() + .for_each(|error| log::error!("watch error: {error:?}")), + }, + )?; - watcher.watch(zellij_cwd, RecursiveMode::Recursive)?; - Ok(watcher) + debouncer + .watcher() + .watch(zellij_cwd, RecursiveMode::Recursive)?; + Ok(debouncer) } diff --git a/zellij-utils/Cargo.toml b/zellij-utils/Cargo.toml index 2469715f6..cbee7bb86 100644 --- a/zellij-utils/Cargo.toml +++ b/zellij-utils/Cargo.toml @@ -40,7 +40,6 @@ tempfile = "3.2.0" kdl = { version = "4.5.0", features = ["span"] } shellexpand = "3.0.0" uuid = { version = "0.8.2", features = ["serde", "v4"] } -notify = "6.0.0" async-channel = "1.8.0" #[cfg(not(target_family = "wasm"))] @@ -50,6 +49,7 @@ log4rs = "1.2.0" signal-hook = "0.3" interprocess = "1.2.1" async-std = { version = "1.3.0", features = ["unstable"] } +notify-debouncer-full = "0.1.0" [dev-dependencies] insta = { version = "1.6.0", features = ["backtrace"] } diff --git a/zellij-utils/src/lib.rs b/zellij-utils/src/lib.rs index 87637de55..b07fe8bdc 100644 --- a/zellij-utils/src/lib.rs +++ b/zellij-utils/src/lib.rs @@ -20,6 +20,6 @@ pub mod logging; // Requires log4rs #[cfg(not(target_family = "wasm"))] pub use ::{ - anyhow, async_channel, async_std, clap, interprocess, lazy_static, libc, miette, nix, notify, - regex, serde, signal_hook, tempfile, termwiz, vte, + anyhow, async_channel, async_std, clap, interprocess, lazy_static, libc, miette, nix, + notify_debouncer_full, regex, serde, signal_hook, tempfile, termwiz, vte, }; |