From f87f0c0e1dc0c07e086989dcffc6984bc5ef71b0 Mon Sep 17 00:00:00 2001 From: Ben Kunkle Date: Fri, 8 May 2026 08:19:07 -0500 Subject: [PATCH] fs: Don't treat watcher errors as reason to do rescans (#56165) Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes https://github.com/zed-industries/zed/issues/56064 This behavior regressed in https://github.com/zed-industries/zed/pull/54481 Release Notes: - Fixed an issue where broken symlinks, permission errors, or other fs errors in watched directories could cause excessive CPU usage --- crates/fs/src/fs_watcher.rs | 45 ++++++++----------------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/crates/fs/src/fs_watcher.rs b/crates/fs/src/fs_watcher.rs index f99d5d0b70eb775455397b44176eb26efe5c370e..fec8b03bfe10bbfc1d048128d37cbaa9393f08c2 100644 --- a/crates/fs/src/fs_watcher.rs +++ b/crates/fs/src/fs_watcher.rs @@ -87,19 +87,11 @@ impl Watcher for FsWatcher { let path: Arc = path.into(); let registration_path = path.clone(); - let registration_id = global_watcher().add( - path.clone(), - self.mode, - move |result: Result<¬ify::Event, ¬ify::Error>| match result { - Ok(event) => { - log::trace!("watcher received event: {event:?}"); - push_notify_event(&tx, &pending_path_events, &root_path, path.as_ref(), event); - } - Err(error) => { - push_notify_error(&tx, &pending_path_events, path.as_ref(), error); - } - }, - )?; + let registration_id = + global_watcher().add(path.clone(), self.mode, move |event: ¬ify::Event| { + log::trace!("watcher received event: {event:?}"); + push_notify_event(&tx, &pending_path_events, &root_path, path.as_ref(), event); + })?; self.registrations .lock() @@ -176,23 +168,6 @@ fn push_notify_event( enqueue_path_events(tx, pending_path_events, path_events); } -fn push_notify_error( - tx: &smol::channel::Sender<()>, - pending_path_events: &Arc>>, - watched_root: &Path, - error: ¬ify::Error, -) { - log::warn!("watcher error for {watched_root:?}: {error}"); - enqueue_path_events( - tx, - pending_path_events, - vec![PathEvent { - path: watched_root.to_path_buf(), - kind: Some(PathEventKind::Rescan), - }], - ); -} - fn coalesce_pending_rescans(pending_paths: &mut Vec, path_events: &mut Vec) { if !path_events .iter() @@ -247,7 +222,7 @@ fn is_covered_rescan(kind: Option, path: &Path, ancestor: &Path) pub struct WatcherRegistrationId(u32); struct WatcherRegistrationState { - callback: Arc Fn(Result<&'a notify::Event, &'a notify::Error>) + Send + Sync>, + callback: Arc, path: Arc, mode: WatcherMode, } @@ -283,7 +258,7 @@ impl GlobalWatcher { &self, path: Arc, mode: WatcherMode, - cb: impl for<'a> Fn(Result<&'a notify::Event, &'a notify::Error>) + Send + Sync + 'static, + cb: impl Fn(¬ify::Event) + Send + Sync + 'static, ) -> anyhow::Result { let mut state = self.state.lock(); let registrations_for_mode = state.path_registrations(mode); @@ -483,13 +458,11 @@ fn handle_event(mode: WatcherMode, event: Result) match event { Ok(event) => { for callback in callbacks { - callback(Ok(&event)); + callback(&event); } } Err(error) => { - for callback in callbacks { - callback(Err(&error)); - } + log::warn!("watcher error for {mode:?}: {error}"); } } }