From cdedad9485b12b664c4b7f3a17e269b6e8b932f9 Mon Sep 17 00:00:00 2001 From: Noethix55555 <277300782+Noethix55555@users.noreply.github.com> Date: Wed, 17 Jun 2026 22:38:45 -0400 Subject: [PATCH] fix(watcher): store Debouncer in RepoWatcher to prevent OS handle leak `create_watcher` called `std::mem::forget(bouncer)` to keep the file-watcher alive, but this permanently leaked the OS watcher handle on every call. Each submodule repo opened added another leaked handle that was never cleaned up. Fix: remove `create_watcher` and the spawned thread that called it. Create the debouncer inline in `RepoWatcher::new` and store it in a new `_watcher` field so the OS handle is released when `RepoWatcher` is dropped. --- src/watcher.rs | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/src/watcher.rs b/src/watcher.rs index ea30c7f135..a680968e2f 100644 --- a/src/watcher.rs +++ b/src/watcher.rs @@ -1,12 +1,14 @@ use anyhow::Result; use crossbeam_channel::{unbounded, Sender}; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; -use notify_debouncer_mini::{new_debouncer, DebounceEventResult}; -use scopetime::scope_time; +use notify_debouncer_mini::{ + new_debouncer, DebounceEventResult, Debouncer, +}; use std::{path::Path, thread, time::Duration}; pub struct RepoWatcher { receiver: crossbeam_channel::Receiver<()>, + _watcher: Debouncer, } impl RepoWatcher { @@ -18,12 +20,13 @@ impl RepoWatcher { let (tx, rx) = std::sync::mpsc::channel(); - let workdir = workdir.to_string(); - - thread::spawn(move || { - let timeout = Duration::from_secs(2); - create_watcher(timeout, tx, &workdir); - }); + let timeout = Duration::from_secs(2); + let mut bouncer = + new_debouncer(timeout, tx).expect("Watch create error"); + bouncer + .watcher() + .watch(Path::new(workdir), RecursiveMode::Recursive) + .expect("Watch error"); let (out_tx, out_rx) = unbounded(); @@ -34,7 +37,10 @@ impl RepoWatcher { } }); - Self { receiver: out_rx } + Self { + receiver: out_rx, + _watcher: bouncer, + } } /// @@ -63,20 +69,3 @@ impl RepoWatcher { } } } - -fn create_watcher( - timeout: Duration, - tx: std::sync::mpsc::Sender, - workdir: &str, -) { - scope_time!("create_watcher"); - - let mut bouncer = - new_debouncer(timeout, tx).expect("Watch create error"); - bouncer - .watcher() - .watch(Path::new(&workdir), RecursiveMode::Recursive) - .expect("Watch error"); - - std::mem::forget(bouncer); -}