@@ -382,7 +382,7 @@ async fn read_mdfind_results(
382382 home_dir : & str ,
383383 receiver : & mut tokio:: sync:: watch:: Receiver < ( String , Vec < String > ) > ,
384384 output : & mut iced:: futures:: channel:: mpsc:: Sender < Message > ,
385- ) {
385+ ) -> bool {
386386 use crate :: app:: { FILE_SEARCH_BATCH_SIZE , FILE_SEARCH_MAX_RESULTS } ;
387387
388388 let mut reader = tokio:: io:: BufReader :: new ( stdout) ;
@@ -395,7 +395,7 @@ async fn read_mdfind_results(
395395 result = reader. read_line( & mut line) => result,
396396 _ = receiver. changed( ) => {
397397 // New query arrived — caller will handle it.
398- break ;
398+ return true ;
399399 }
400400 } ;
401401
@@ -408,7 +408,7 @@ async fn read_mdfind_results(
408408 . await
409409 . ok ( ) ;
410410 }
411- break ;
411+ return false ;
412412 }
413413 Ok ( _) => {
414414 if let Some ( app) = crate :: commands:: path_to_app ( line. trim ( ) , home_dir) {
@@ -428,10 +428,10 @@ async fn read_mdfind_results(
428428 . await
429429 . ok ( ) ;
430430 }
431- break ;
431+ return false ;
432432 }
433433 }
434- Err ( _) => break ,
434+ Err ( _) => return false ,
435435 }
436436 }
437437}
@@ -491,23 +491,15 @@ fn handle_file_search() -> impl futures::Stream<Item = Message> {
491491 let home_dir = std:: env:: var ( "HOME" ) . unwrap_or_else ( |_| "/" . to_string ( ) ) ;
492492 assert ! ( !home_dir. is_empty( ) , "HOME must not be empty." ) ;
493493
494- let run_loop = NSRunLoop :: currentRunLoop ( ) ;
495- let run_loop_mode = unsafe { NSDefaultRunLoopMode } ;
496- let tick_seconds: f64 = 0.05 ;
497- let idle_sleep = Duration :: from_millis ( ( tick_seconds * 1000.0 ) as u64 ) ;
494+ let mut child: Option < tokio:: process:: Child > = None ;
495+ let mut wait_for_change = true ;
498496
499- loop {
500- // Tick the run loop to process notifications.
501- let timeout = NSDate :: dateWithTimeIntervalSinceNow ( tick_seconds) ;
502- let handled_source = run_loop. runMode_beforeDate ( run_loop_mode, & timeout) ;
503-
504- // Drain results only when the finish-gathering notification has fired.
505- if results_ready. swap ( false , Ordering :: AcqRel ) {
506- if query. resultCount ( ) > 0 {
507- let paths = drain_metadata_results ( & query, & home_dir, & msg_tx) ;
508- load_file_search_icons ( & paths, & msg_tx, & watch_rx) ;
497+ loop {
498+ if wait_for_change && receiver. changed ( ) . await . is_err ( ) {
499+ break ;
509500 }
510- receiver. borrow_and_update ( ) ;
501+
502+ wait_for_change = true ;
511503
512504 // Kill previous mdfind if still running.
513505 if let Some ( ref mut proc) = child {
@@ -516,7 +508,7 @@ fn handle_file_search() -> impl futures::Stream<Item = Message> {
516508 }
517509 child = None ;
518510
519- let ( query, dirs) = receiver. borrow ( ) . clone ( ) ;
511+ let ( query, dirs) = receiver. borrow_and_update ( ) . clone ( ) ;
520512 assert ! ( query. len( ) < 1024 , "Query too long." ) ;
521513
522514 if query. len ( ) < 2 {
@@ -535,18 +527,51 @@ fn handle_file_search() -> impl futures::Stream<Item = Message> {
535527 args. push ( expanded) ;
536528 }
537529
538- if msg_tx. is_closed ( ) {
539- break ;
540- }
530+ let mut command = tokio:: process:: Command :: new ( "mdfind" ) ;
531+ command. args ( & args) ;
532+ command. stdout ( std:: process:: Stdio :: piped ( ) ) ;
533+ command. stderr ( std:: process:: Stdio :: null ( ) ) ;
534+
535+ let mut spawned = match command. spawn ( ) {
536+ Ok ( child) => child,
537+ Err ( error) => {
538+ warn ! ( "Failed to spawn mdfind: {error}" ) ;
539+ continue ;
540+ }
541+ } ;
541542
542- // NSRunLoop can return immediately when no input sources/timers are active.
543- // Without this guard, the thread can busy-spin and consume a full core.
544- if !handled_source {
545- std:: thread:: sleep ( idle_sleep) ;
543+ let stdout = match spawned. stdout . take ( ) {
544+ Some ( stdout) => stdout,
545+ None => {
546+ warn ! ( "mdfind stdout was not captured" ) ;
547+ spawned. kill ( ) . await . ok ( ) ;
548+ spawned. wait ( ) . await . ok ( ) ;
549+ continue ;
550+ }
551+ } ;
552+
553+ child = Some ( spawned) ;
554+
555+ let canceled = read_mdfind_results ( stdout, & home_dir, & mut receiver, & mut output) . await ;
556+
557+ if let Some ( ref mut proc) = child {
558+ if canceled {
559+ proc. kill ( ) . await . ok ( ) ;
560+ }
561+ proc. wait ( ) . await . ok ( ) ;
562+ }
563+ child = None ;
564+
565+ // `read_mdfind_results` consumed the watch notification when canceled,
566+ // so process the latest query immediately.
567+ if canceled {
568+ wait_for_change = false ;
569+ }
546570 }
547- }
548571
549- read_mdfind_results ( stdout, & home_dir, & mut receiver, & mut output) . await ;
572+ if let Some ( ref mut proc) = child {
573+ proc. kill ( ) . await . ok ( ) ;
574+ proc. wait ( ) . await . ok ( ) ;
550575 }
551576 } )
552577}
0 commit comments