Skip to content

Commit

Permalink
Send exit code events on child process exit
Browse files Browse the repository at this point in the history
  • Loading branch information
SomeoneToIgnore authored and chrisduerr committed Mar 15, 2024
1 parent 469e02a commit 875070e
Show file tree
Hide file tree
Showing 7 changed files with 31 additions and 13 deletions.
2 changes: 1 addition & 1 deletion alacritty/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1356,7 +1356,7 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
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(_) => (),
Expand Down
4 changes: 4 additions & 0 deletions alacritty_terminal/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ pub enum Event {

/// Shutdown request.
Exit,

/// Child process exited with an error code.
ChildExit(i32),
}

impl Debug for Event {
Expand All @@ -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})"),
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions alacritty_terminal/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,18 @@ 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());
} else {
// Without hold, shutdown the terminal.
self.terminal.lock().exit();
}

self.event_proxy.send_event(Event::Wakeup);
break 'event_loop;
}
Expand Down
4 changes: 2 additions & 2 deletions alacritty_terminal/src/tty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<i32>),
}

/// A pseudoterminal (or PTY).
Expand Down
2 changes: 1 addition & 1 deletion alacritty_terminal/src/tty/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))),
}
}
}
Expand Down
23 changes: 17 additions & 6 deletions alacritty_terminal/src/tty/windows/child.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,6 +23,7 @@ struct Interest {
struct ChildExitSender {
sender: mpsc::Sender<ChildEvent>,
interest: Arc<Mutex<Option<Interest>>>,
child_handle: AtomicPtr<c_void>,
}

/// WinAPI callback to run when child process exits.
Expand All @@ -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();
Expand All @@ -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(
Expand Down Expand Up @@ -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))));
}
}
2 changes: 1 addition & 1 deletion alacritty_terminal/src/tty/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
}
}
}
Expand Down

0 comments on commit 875070e

Please sign in to comment.