fs: Don't treat watcher errors as reason to do rescans (#56165)

Ben Kunkle created

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

Change summary

crates/fs/src/fs_watcher.rs | 45 +++++++-------------------------------
1 file changed, 9 insertions(+), 36 deletions(-)

Detailed changes

crates/fs/src/fs_watcher.rs 🔗

@@ -87,19 +87,11 @@ impl Watcher for FsWatcher {
         let path: Arc<std::path::Path> = path.into();
 
         let registration_path = path.clone();
-        let registration_id = global_watcher().add(
-            path.clone(),
-            self.mode,
-            move |result: Result<&notify::Event, &notify::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: &notify::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<Mutex<Vec<PathEvent>>>,
-    watched_root: &Path,
-    error: &notify::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<PathEvent>, path_events: &mut Vec<PathEvent>) {
     if !path_events
         .iter()
@@ -247,7 +222,7 @@ fn is_covered_rescan(kind: Option<PathEventKind>, path: &Path, ancestor: &Path)
 pub struct WatcherRegistrationId(u32);
 
 struct WatcherRegistrationState {
-    callback: Arc<dyn for<'a> Fn(Result<&'a notify::Event, &'a notify::Error>) + Send + Sync>,
+    callback: Arc<dyn Fn(&notify::Event) + Send + Sync>,
     path: Arc<std::path::Path>,
     mode: WatcherMode,
 }
@@ -283,7 +258,7 @@ impl GlobalWatcher {
         &self,
         path: Arc<std::path::Path>,
         mode: WatcherMode,
-        cb: impl for<'a> Fn(Result<&'a notify::Event, &'a notify::Error>) + Send + Sync + 'static,
+        cb: impl Fn(&notify::Event) + Send + Sync + 'static,
     ) -> anyhow::Result<WatcherRegistrationId> {
         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<notify::Event, notify::Error>)
     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}");
         }
     }
 }