From 6ee5567bfa3819839792c6b2a6d22bdca5f6030f Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Sat, 23 May 2026 15:48:41 +0100 Subject: [PATCH] Stop the fswatch reader after EOF When the external fswatch process closed stdout, the reader loop enqueued Watcher_terminated and then immediately tried to read again. Since the channel stayed at EOF, it could spin and keep filling the scheduler queue. Enqueue the termination event once and let the reader thread exit. Signed-off-by: Rudi Grinberg --- src/dune_scheduler/file_watcher.ml | 36 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/dune_scheduler/file_watcher.ml b/src/dune_scheduler/file_watcher.ml index 1cd9973700b..82495d708f4 100644 --- a/src/dune_scheduler/file_watcher.ml +++ b/src/dune_scheduler/file_watcher.ml @@ -403,26 +403,28 @@ let create_external_fswatch ~(scheduler : Scheduler.t) ~backend ~watch_exclusion let pipe, pid = spawn_external_watcher ~backend ~watch_exclusions in let (_ : Thread.t) = Thread0.spawn ~name:"file-watcher" (fun () -> - while true do - (* This job runs on the scheduler thread because it uses [sync_table]. *) - let job = - match input_line pipe with - | exception End_of_file -> fun () -> [ Event.Watcher_terminated ] - | path_s -> - fun () -> - if Fs_sync.is_special_file ~path_as_reported_by_file_watcher:path_s - then ( - match Fs_sync.consume_event sync_table path_s with - | None -> [] - | Some id -> [ Event.Sync id ]) - else ( - let path = Path.Expert.try_localize_external (Path.of_string path_s) in - [ Fs_memo_event (Fs_memo_event.create ~kind:File_changed ~path) ]) - in + let enqueue job = Mutex.protect event_mtx (fun () -> jobs := job :: !jobs; Condition.signal event_cv) - done) + in + let rec loop () = + (* This job runs on the scheduler thread because it uses [sync_table]. *) + match input_line pipe with + | exception End_of_file -> enqueue (fun () -> [ Event.Watcher_terminated ]) + | path_s -> + enqueue (fun () -> + if Fs_sync.is_special_file ~path_as_reported_by_file_watcher:path_s + then ( + match Fs_sync.consume_event sync_table path_s with + | None -> [] + | Some id -> [ Event.Sync id ]) + else ( + let path = Path.Expert.try_localize_external (Path.of_string path_s) in + [ Fs_memo_event (Fs_memo_event.create ~kind:File_changed ~path) ])); + loop () + in + loop ()) in { kind = Fswatch { pid }; sync_table } in