@@ -186,6 +186,7 @@ pub struct FilePicker {
186186 scanned_files_count : Arc < AtomicUsize > ,
187187 background_watcher : Option < BackgroundWatcher > ,
188188 warmup_mmap_cache : bool ,
189+ cancelled : Arc < AtomicBool > ,
189190}
190191
191192impl std:: fmt:: Debug for FilePicker {
@@ -250,6 +251,7 @@ impl FilePicker {
250251 // rather than a stale `false` (the thread hasn't started yet).
251252 let scan_signal = Arc :: new ( AtomicBool :: new ( true ) ) ;
252253 let synced_files_count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
254+ let cancelled = Arc :: new ( AtomicBool :: new ( false ) ) ;
253255
254256 let picker = FilePicker {
255257 base_path : path. clone ( ) ,
@@ -258,6 +260,7 @@ impl FilePicker {
258260 scanned_files_count : Arc :: clone ( & synced_files_count) ,
259261 background_watcher : None ,
260262 warmup_mmap_cache,
263+ cancelled : Arc :: clone ( & cancelled) ,
261264 } ;
262265
263266 // Place the picker into the shared handle before spawning the
@@ -274,6 +277,7 @@ impl FilePicker {
274277 warmup_mmap_cache,
275278 shared_picker,
276279 shared_frecency,
280+ cancelled,
277281 ) ;
278282
279283 Ok ( ( ) )
@@ -578,6 +582,11 @@ impl FilePicker {
578582 . retain_files ( |file| !file. path . starts_with ( dir_path) )
579583 }
580584
585+ /// We use this to prevent any substantial background threads from acquiring the locks
586+ pub fn cancel ( & self ) {
587+ self . cancelled . store ( true , Ordering :: Release ) ;
588+ }
589+
581590 pub fn stop_background_monitor ( & mut self ) {
582591 if let Some ( watcher) = self . background_watcher . take ( ) {
583592 watcher. stop ( ) ;
@@ -644,6 +653,7 @@ fn spawn_scan_and_watcher(
644653 warmup_mmap_cache : bool ,
645654 shared_picker : SharedPicker ,
646655 shared_frecency : SharedFrecency ,
656+ cancelled : Arc < AtomicBool > ,
647657) {
648658 std:: thread:: spawn ( move || {
649659 // scan_signal is already `true` (set by the caller before spawning)
@@ -653,6 +663,12 @@ fn spawn_scan_and_watcher(
653663 let mut git_workdir = None ;
654664 match scan_filesystem ( & base_path, & synced_files_count, & shared_frecency) {
655665 Ok ( sync) => {
666+ if cancelled. load ( Ordering :: Acquire ) {
667+ info ! ( "Scan completed but picker was replaced, discarding results" ) ;
668+ scan_signal. store ( false , Ordering :: Relaxed ) ;
669+ return ;
670+ }
671+
656672 info ! (
657673 "Initial filesystem scan completed: found {} files" ,
658674 sync. files. len( )
@@ -672,13 +688,8 @@ fn spawn_scan_and_watcher(
672688 }
673689
674690 // OPTIMIZATION: Warmup mmap cache in background to avoid blocking first grep.
675- // The aggressive parallel warmup was causing cache thrashing and delaying
676- // initial searches. Now it runs async and doesn't block.
677- //
678- // We warmup under a read lock on the picker's actual files so that
679- // the OnceLock<Mmap> instances are populated in-place — no clone needed.
680- // Read locks allow concurrent readers so this doesn't block searches.
681691 if warmup_mmap_cache
692+ && !cancelled. load ( Ordering :: Acquire )
682693 && let Ok ( guard) = shared_picker. read ( )
683694 && let Some ( ref picker) = * guard
684695 {
@@ -691,6 +702,12 @@ fn spawn_scan_and_watcher(
691702 }
692703 scan_signal. store ( false , Ordering :: Relaxed ) ;
693704
705+ // Don't create a watcher if this picker instance was already replaced
706+ if cancelled. load ( Ordering :: Acquire ) {
707+ info ! ( "Picker was replaced, skipping background watcher creation" ) ;
708+ return ;
709+ }
710+
694711 match BackgroundWatcher :: new (
695712 base_path,
696713 git_workdir,
@@ -700,6 +717,15 @@ fn spawn_scan_and_watcher(
700717 Ok ( watcher) => {
701718 info ! ( "Background file watcher initialized successfully" ) ;
702719
720+ // Final cancellation check: if the picker was replaced between
721+ // watcher creation and this write, drop the watcher instead of
722+ // storing it in the wrong picker.
723+ if cancelled. load ( Ordering :: Acquire ) {
724+ info ! ( "Picker was replaced, dropping orphaned watcher" ) ;
725+ drop ( watcher) ;
726+ return ;
727+ }
728+
703729 let write_result = shared_picker. write ( ) . ok ( ) . map ( |mut guard| {
704730 if let Some ( ref mut picker) = * guard {
705731 picker. background_watcher = Some ( watcher) ;
0 commit comments