diff --git a/app/src/terminal/local_tty/mio_channel.rs b/app/src/terminal/local_tty/mio_channel.rs index fe83e48c2..d2b40b773 100644 --- a/app/src/terminal/local_tty/mio_channel.rs +++ b/app/src/terminal/local_tty/mio_channel.rs @@ -88,16 +88,15 @@ pub struct Sender { impl Sender { /// Try to send a value. /// - /// This works the same way as [`mpsc::Sender::send`]. After sending the - /// value, it wakes upthe [`mio::poll::Poll`]. - /// + /// This works the same way as [`mpsc::Sender::send`]. After sending the value, it wakes up the + /// [`mio::poll::Poll`]. pub fn send(&self, t: T) -> Result<(), SendError> { self.tx.send(t)?; let mut state = self.state.lock().unwrap(); if let Some(waker) = &mut state.waker { if let Err(e) = waker.wake() { - log::error!("PTY mio Waker::wake() failed: {e:?}; event loop may not wake up to process shutdown"); + log::error!("PTY mio Waker::wake() failed: {e:#}"); } } else { state.needs_wake_on_register = true; diff --git a/app/src/terminal/local_tty/terminal_manager.rs b/app/src/terminal/local_tty/terminal_manager.rs index 40d6b74c9..691fd24ca 100644 --- a/app/src/terminal/local_tty/terminal_manager.rs +++ b/app/src/terminal/local_tty/terminal_manager.rs @@ -194,39 +194,31 @@ impl TerminalManager { log::info!("Failed to send Shutdown {e:?}"); } + // On Windows, poll with a timeout instead of blocking indefinitely. If the event loop hangs + // (e.g. due to a pipe read or mio waker failure), a stuck join would prevent + // on_will_terminate from returning and thus prevent std::process::exit(0) from ever being + // called, leaving the warp.exe process alive with no visible window. If we time out, + // std::process::exit(0) will still kill the thread. The ConPTY handle will be closed by the + // OS when the process exits, which signals OpenConsole to exit on its own. if let Some(join_handle) = self.event_loop_handle.take() { - // On Windows, poll with a timeout instead of blocking indefinitely. - // If the event loop hangs (e.g. due to a pipe read or mio waker failure), - // a stuck join would prevent on_will_terminate from returning and thus - // prevent std::process::exit(0) from ever being called — leaving the - // warp.exe process alive with no visible window (APP-3702). - // - // If we time out, std::process::exit(0) will still kill the thread. - // The ConPTY handle will be closed by the OS when the process exits, - // which signals OpenConsole to exit on its own. #[cfg(windows)] { - const SHUTDOWN_TIMEOUT: std::time::Duration = - std::time::Duration::from_secs(5); - const POLL_INTERVAL: std::time::Duration = - std::time::Duration::from_millis(10); - let deadline = std::time::Instant::now() + SHUTDOWN_TIMEOUT; + use std::time::{Duration, Instant}; + const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); + const POLL_INTERVAL: Duration = Duration::from_millis(10); + let deadline = Instant::now() + SHUTDOWN_TIMEOUT; while !join_handle.is_finished() { - if std::time::Instant::now() >= deadline { + if Instant::now() >= deadline { log::warn!( - "PTY event loop did not exit within {SHUTDOWN_TIMEOUT:?}; \ - proceeding with app shutdown" + "PTY event loop did not exit within {SHUTDOWN_TIMEOUT:?}. Proceeding \ + with app shutdown." ); self.inactive_pty_reads_rx.close(); return; } std::thread::sleep(POLL_INTERVAL); } - if let Err(e) = join_handle.join() { - log::error!("Failed to join event loop handle {e:?}"); - } } - #[cfg(not(windows))] if let Err(e) = join_handle.join() { log::error!("Failed to join event loop handle {e:?}"); }