This commit is contained in:
Andy Carlson
2026-05-05 22:06:14 -07:00
parent f4e43a8e8c
commit c3f7bf671a
2 changed files with 16 additions and 25 deletions

View File

@@ -88,16 +88,15 @@ pub struct Sender<T> {
impl<T> Sender<T> {
/// 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<T>> {
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;

View File

@@ -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:?}");
}