From 0809a45e1cddee23e212c15285064c81cf107e33 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Mon, 1 Dec 2025 13:23:30 -0800 Subject: [PATCH] blocking --- codex-rs/cli/src/debug_sandbox/pid_tracker.rs | 6 +- codex-rs/core/src/message_history.rs | 1 - codex-rs/login/src/server.rs | 12 ++- codex-rs/tui/src/app.rs | 6 +- codex-rs/tui/src/file_search.rs | 78 +++++++++++-------- 5 files changed, 64 insertions(+), 39 deletions(-) diff --git a/codex-rs/cli/src/debug_sandbox/pid_tracker.rs b/codex-rs/cli/src/debug_sandbox/pid_tracker.rs index 07bd2fd046..f47ed275e7 100644 --- a/codex-rs/cli/src/debug_sandbox/pid_tracker.rs +++ b/codex-rs/cli/src/debug_sandbox/pid_tracker.rs @@ -288,8 +288,8 @@ mod tests { } #[cfg(target_os = "macos")] - #[test] - fn list_child_pids_includes_spawned_child() { + #[tokio::test] + async fn list_child_pids_includes_spawned_child() { let mut child = Command::new("/bin/sleep") .arg("5") .stdin(Stdio::null()) @@ -305,7 +305,7 @@ mod tests { found = true; break; } - std::thread::sleep(Duration::from_millis(10)); + tokio::time::sleep(Duration::from_millis(10)).await; } let _ = child.kill(); diff --git a/codex-rs/core/src/message_history.rs b/codex-rs/core/src/message_history.rs index e96d8d5026..5d3cdaca3c 100644 --- a/codex-rs/core/src/message_history.rs +++ b/codex-rs/core/src/message_history.rs @@ -17,7 +17,6 @@ use std::fs::File; use std::fs::OpenOptions; use std::io::Result; -use std::io::Write; use std::path::PathBuf; use serde::Deserialize; diff --git a/codex-rs/login/src/server.rs b/codex-rs/login/src/server.rs index 999c19072e..a825d39fb2 100644 --- a/codex-rs/login/src/server.rs +++ b/codex-rs/login/src/server.rs @@ -447,6 +447,16 @@ fn bind_server(port: u16) -> io::Result { const MAX_ATTEMPTS: u32 = 10; const RETRY_DELAY: Duration = Duration::from_millis(200); + fn retry_sleep(delay: Duration) { + if tokio::runtime::Handle::try_current().is_ok() { + let _ = tokio::task::block_in_place(|| { + std::thread::sleep(delay); + }); + } else { + std::thread::sleep(delay); + } + } + loop { match Server::http(&bind_address) { Ok(server) => return Ok(server), @@ -467,7 +477,7 @@ fn bind_server(port: u16) -> io::Result { } } - thread::sleep(RETRY_DELAY); + retry_sleep(RETRY_DELAY); if attempts >= MAX_ATTEMPTS { return Err(io::Error::new( diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index d0e057102c..9bf32edcf9 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -50,10 +50,10 @@ use std::path::PathBuf; use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; -use std::thread; use std::time::Duration; use tokio::select; use tokio::sync::mpsc::unbounded_channel; +use tokio::time::sleep; #[cfg(not(debug_assertions))] use crate::history_cell::UpdateAvailableHistoryCell; @@ -502,9 +502,9 @@ impl App { { let tx = self.app_event_tx.clone(); let running = self.commit_anim_running.clone(); - thread::spawn(move || { + tokio::spawn(async move { while running.load(Ordering::Relaxed) { - thread::sleep(Duration::from_millis(50)); + sleep(Duration::from_millis(50)).await; tx.send(AppEvent::CommitTick); } }); diff --git a/codex-rs/tui/src/file_search.rs b/codex-rs/tui/src/file_search.rs index af46512640..3534c54b69 100644 --- a/codex-rs/tui/src/file_search.rs +++ b/codex-rs/tui/src/file_search.rs @@ -25,8 +25,9 @@ use std::sync::Arc; use std::sync::Mutex; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; -use std::thread; use std::time::Duration; +use tokio::task::spawn_blocking; +use tokio::time::sleep; use crate::app_event::AppEvent; use crate::app_event_sender::AppEventSender; @@ -117,16 +118,16 @@ impl FileSearchManager { let state = self.state.clone(); let search_dir = self.search_dir.clone(); let tx_clone = self.app_tx.clone(); - thread::spawn(move || { + tokio::spawn(async move { // Always do a minimum debounce, but then poll until the // `active_search` is cleared. - thread::sleep(FILE_SEARCH_DEBOUNCE); + sleep(FILE_SEARCH_DEBOUNCE).await; loop { #[expect(clippy::unwrap_used)] if state.lock().unwrap().active_search.is_none() { break; } - thread::sleep(ACTIVE_SEARCH_COMPLETE_POLL_INTERVAL); + sleep(ACTIVE_SEARCH_COMPLETE_POLL_INTERVAL).await; } // The debounce timer has expired, so start a search using the @@ -162,36 +163,51 @@ impl FileSearchManager { cancellation_token: Arc, search_state: Arc>, ) { - let compute_indices = true; - std::thread::spawn(move || { - let matches = file_search::run( - &query, - MAX_FILE_SEARCH_RESULTS, - &search_dir, - Vec::new(), - NUM_FILE_SEARCH_THREADS, - cancellation_token.clone(), - compute_indices, - true, - ) - .map(|res| res.matches) - .unwrap_or_default(); + tokio::spawn({ + let query_for_result = query.clone(); + let cancellation_token_for_result = cancellation_token.clone(); + let query_for_search = query.clone(); + let cancellation_token_for_search = cancellation_token.clone(); + let search_state = Arc::clone(&search_state); + let search_dir = search_dir.clone(); + async move { + let compute_indices = true; + let matches = spawn_blocking(move || { + file_search::run( + &query_for_search, + MAX_FILE_SEARCH_RESULTS, + &search_dir, + Vec::new(), + NUM_FILE_SEARCH_THREADS, + cancellation_token_for_search, + compute_indices, + true, + ) + .map(|res| res.matches) + .unwrap_or_default() + }) + .await + .unwrap_or_default(); - let is_cancelled = cancellation_token.load(Ordering::Relaxed); - if !is_cancelled { - tx.send(AppEvent::FileSearchResult { query, matches }); - } + let is_cancelled = cancellation_token_for_result.load(Ordering::Relaxed); + if !is_cancelled { + tx.send(AppEvent::FileSearchResult { + query: query_for_result, + matches, + }); + } - // Reset the active search state. Do a pointer comparison to verify - // that we are clearing the ActiveSearch that corresponds to the - // cancellation token we were given. - { - #[expect(clippy::unwrap_used)] - let mut st = search_state.lock().unwrap(); - if let Some(active_search) = &st.active_search - && Arc::ptr_eq(&active_search.cancellation_token, &cancellation_token) + // Reset the active search state. Do a pointer comparison to verify + // that we are clearing the ActiveSearch that corresponds to the + // cancellation token we were given. { - st.active_search = None; + #[expect(clippy::unwrap_used)] + let mut st = search_state.lock().unwrap(); + if let Some(active_search) = &st.active_search + && Arc::ptr_eq(&active_search.cancellation_token, &cancellation_token) + { + st.active_search = None; + } } } });