From f5646ba352c809f78c55a9e1f6a5212aea787899 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 9 Mar 2024 13:32:32 +0200 Subject: Send exit code events on child process exit Fixes #7753. --- alacritty/src/event.rs | 2 +- alacritty_terminal/src/event.rs | 4 ++++ alacritty_terminal/src/event_loop.rs | 7 +++++-- alacritty_terminal/src/tty/mod.rs | 4 ++-- alacritty_terminal/src/tty/unix.rs | 2 +- alacritty_terminal/src/tty/windows/child.rs | 23 +++++++++++++++++------ alacritty_terminal/src/tty/windows/mod.rs | 2 +- 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index e1ee20f8..4fa397ad 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -1356,7 +1356,7 @@ impl input::Processor> { TerminalEvent::PtyWrite(text) => self.ctx.write_to_pty(text.into_bytes()), TerminalEvent::MouseCursorDirty => self.reset_mouse_cursor(), TerminalEvent::CursorBlinkingChange => self.ctx.update_cursor_blinking(), - TerminalEvent::Exit | TerminalEvent::Wakeup => (), + TerminalEvent::Exit | TerminalEvent::ChildExit(_) | TerminalEvent::Wakeup => (), }, #[cfg(unix)] EventType::IpcConfig(_) => (), diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs index caa1e9d6..e17d41ea 100644 --- a/alacritty_terminal/src/event.rs +++ b/alacritty_terminal/src/event.rs @@ -52,6 +52,9 @@ pub enum Event { /// Shutdown request. Exit, + + /// Child process exited with an error code. + ChildExit(i32), } impl Debug for Event { @@ -69,6 +72,7 @@ impl Debug for Event { Event::Wakeup => write!(f, "Wakeup"), Event::Bell => write!(f, "Bell"), Event::Exit => write!(f, "Exit"), + Event::ChildExit(code) => write!(f, "ChildExit({code})"), } } } diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs index 42025b00..929c642d 100644 --- a/alacritty_terminal/src/event_loop.rs +++ b/alacritty_terminal/src/event_loop.rs @@ -259,7 +259,11 @@ where for event in events.iter() { match event.key { tty::PTY_CHILD_EVENT_TOKEN => { - if let Some(tty::ChildEvent::Exited) = self.pty.next_child_event() { + if let Some(tty::ChildEvent::Exited(code)) = self.pty.next_child_event() + { + if let Some(code) = code { + self.event_proxy.send_event(Event::ChildExit(code)); + } if self.hold { // With hold enabled, make sure the PTY is drained. let _ = self.pty_read(&mut state, &mut buf, pipe.as_mut()); @@ -267,7 +271,6 @@ where // Without hold, shutdown the terminal. self.terminal.lock().exit(); } - self.event_proxy.send_event(Event::Wakeup); break 'event_loop; } diff --git a/alacritty_terminal/src/tty/mod.rs b/alacritty_terminal/src/tty/mod.rs index d1bb023c..35d227b8 100644 --- a/alacritty_terminal/src/tty/mod.rs +++ b/alacritty_terminal/src/tty/mod.rs @@ -67,8 +67,8 @@ pub trait EventedReadWrite { /// Events concerning TTY child processes. #[derive(Debug, PartialEq, Eq)] pub enum ChildEvent { - /// Indicates the child has exited. - Exited, + /// Indicates the child has exited, with an error code if available. + Exited(Option), } /// A pseudoterminal (or PTY). diff --git a/alacritty_terminal/src/tty/unix.rs b/alacritty_terminal/src/tty/unix.rs index 2c06d54f..1034c5a9 100644 --- a/alacritty_terminal/src/tty/unix.rs +++ b/alacritty_terminal/src/tty/unix.rs @@ -381,7 +381,7 @@ impl EventedPty for Pty { None }, Ok(None) => None, - Ok(_) => Some(ChildEvent::Exited), + Ok(exit_status) => Some(ChildEvent::Exited(exit_status.and_then(|s| s.code()))), } } } diff --git a/alacritty_terminal/src/tty/windows/child.rs b/alacritty_terminal/src/tty/windows/child.rs index 7de6563f..dc6b992c 100644 --- a/alacritty_terminal/src/tty/windows/child.rs +++ b/alacritty_terminal/src/tty/windows/child.rs @@ -7,10 +7,10 @@ use std::sync::{mpsc, Arc, Mutex}; use polling::os::iocp::{CompletionPacket, PollerIocpExt}; use polling::{Event, Poller}; -use windows_sys::Win32::Foundation::{BOOLEAN, HANDLE}; +use windows_sys::Win32::Foundation::{BOOLEAN, FALSE, HANDLE}; use windows_sys::Win32::System::Threading::{ - GetProcessId, RegisterWaitForSingleObject, UnregisterWait, INFINITE, WT_EXECUTEINWAITTHREAD, - WT_EXECUTEONLYONCE, + GetExitCodeProcess, GetProcessId, RegisterWaitForSingleObject, UnregisterWait, INFINITE, + WT_EXECUTEINWAITTHREAD, WT_EXECUTEONLYONCE, }; use crate::tty::ChildEvent; @@ -23,6 +23,7 @@ struct Interest { struct ChildExitSender { sender: mpsc::Sender, interest: Arc>>, + child_handle: AtomicPtr, } /// WinAPI callback to run when child process exits. @@ -32,7 +33,13 @@ extern "system" fn child_exit_callback(ctx: *mut c_void, timed_out: BOOLEAN) { } let event_tx: Box<_> = unsafe { Box::from_raw(ctx as *mut ChildExitSender) }; - let _ = event_tx.sender.send(ChildEvent::Exited); + + let mut exit_code = 0_u32; + let child_handle = event_tx.child_handle.load(Ordering::Relaxed) as HANDLE; + let status = unsafe { GetExitCodeProcess(child_handle, &mut exit_code) }; + let exit_code = if status == FALSE { None } else { Some(exit_code as i32) }; + event_tx.sender.send(ChildEvent::Exited(exit_code)).ok(); + let interest = event_tx.interest.lock().unwrap(); if let Some(interest) = interest.as_ref() { interest.poller.post(CompletionPacket::new(interest.event)).ok(); @@ -53,7 +60,11 @@ impl ChildExitWatcher { let mut wait_handle: HANDLE = 0; let interest = Arc::new(Mutex::new(None)); - let sender_ref = Box::new(ChildExitSender { sender: event_tx, interest: interest.clone() }); + let sender_ref = Box::new(ChildExitSender { + sender: event_tx, + interest: interest.clone(), + child_handle: AtomicPtr::from(child_handle as *mut c_void), + }); let success = unsafe { RegisterWaitForSingleObject( @@ -145,6 +156,6 @@ mod tests { poller.wait(&mut events, Some(WAIT_TIMEOUT)).unwrap(); assert_eq!(events.iter().next().unwrap().key, PTY_CHILD_EVENT_TOKEN); // Verify that at least one `ChildEvent::Exited` was received. - assert_eq!(child_exit_watcher.event_rx().try_recv(), Ok(ChildEvent::Exited)); + assert_eq!(child_exit_watcher.event_rx().try_recv(), Ok(ChildEvent::Exited(Some(1)))); } } diff --git a/alacritty_terminal/src/tty/windows/mod.rs b/alacritty_terminal/src/tty/windows/mod.rs index e42e2b6c..6af162c8 100644 --- a/alacritty_terminal/src/tty/windows/mod.rs +++ b/alacritty_terminal/src/tty/windows/mod.rs @@ -115,7 +115,7 @@ impl EventedPty for Pty { match self.child_watcher.event_rx().try_recv() { Ok(ev) => Some(ev), Err(TryRecvError::Empty) => None, - Err(TryRecvError::Disconnected) => Some(ChildEvent::Exited), + Err(TryRecvError::Disconnected) => Some(ChildEvent::Exited(None)), } } } -- cgit v1.2.3-54-g00ecf