mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 07:46:12 +03:00
termio: detect child process exit
This commit is contained in:
@ -105,6 +105,10 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
||||
// Start our subprocess
|
||||
const master_fd = try self.subprocess.start(alloc);
|
||||
errdefer self.subprocess.stop();
|
||||
const pid = pid: {
|
||||
const command = self.subprocess.command orelse return error.ProcessNotStarted;
|
||||
break :pid command.pid orelse return error.ProcessNoPid;
|
||||
};
|
||||
|
||||
// Setup our data that is used for callbacks
|
||||
var ev_data_ptr = try alloc.create(EventData);
|
||||
@ -118,6 +122,10 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
||||
var wakeup = try xev.Async.init();
|
||||
errdefer wakeup.deinit();
|
||||
|
||||
// Watcher to detect subprocess exit
|
||||
var process = try xev.Process.init(pid);
|
||||
errdefer process.deinit();
|
||||
|
||||
// Setup our event data before we start
|
||||
ev_data_ptr.* = .{
|
||||
.writer_mailbox = thread.mailbox,
|
||||
@ -125,6 +133,7 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
||||
.renderer_state = self.renderer_state,
|
||||
.renderer_wakeup = self.renderer_wakeup,
|
||||
.renderer_mailbox = self.renderer_mailbox,
|
||||
.process = process,
|
||||
.data_stream = stream,
|
||||
.loop = &thread.loop,
|
||||
.terminal_stream = .{
|
||||
@ -143,6 +152,15 @@ pub fn threadEnter(self: *Exec, thread: *termio.Thread) !ThreadData {
|
||||
self.data = ev_data_ptr;
|
||||
errdefer self.data = null;
|
||||
|
||||
// Start our process watcher
|
||||
process.wait(
|
||||
ev_data_ptr.loop,
|
||||
&ev_data_ptr.process_wait_c,
|
||||
EventData,
|
||||
ev_data_ptr,
|
||||
processExit,
|
||||
);
|
||||
|
||||
// Start our reader thread
|
||||
const read_thread = try std.Thread.spawn(
|
||||
.{},
|
||||
@ -164,8 +182,12 @@ pub fn threadExit(self: *Exec, data: ThreadData) void {
|
||||
self.data = null;
|
||||
|
||||
// Stop our subprocess
|
||||
if (data.ev.process_exited) self.subprocess.externalExit();
|
||||
self.subprocess.stop();
|
||||
|
||||
// Wait for our subprocess to report it exited.
|
||||
// data.ev.loop.run(.once);
|
||||
|
||||
// Wait for our reader thread to end
|
||||
data.read_thread.join();
|
||||
}
|
||||
@ -272,6 +294,14 @@ const EventData = struct {
|
||||
/// The mailbox for notifying the renderer of things.
|
||||
renderer_mailbox: *renderer.Thread.Mailbox,
|
||||
|
||||
/// The process watcher
|
||||
process: xev.Process,
|
||||
process_exited: bool = false,
|
||||
|
||||
/// This is used for both waiting for the process to exit and then
|
||||
/// subsequently to wait for the data_stream to close.
|
||||
process_wait_c: xev.Completion = .{},
|
||||
|
||||
/// The data stream is the main IO for the pty.
|
||||
data_stream: xev.Stream,
|
||||
|
||||
@ -301,6 +331,9 @@ const EventData = struct {
|
||||
|
||||
// Stop our data stream
|
||||
self.data_stream.deinit();
|
||||
|
||||
// Stop our process watcher
|
||||
self.process.deinit();
|
||||
}
|
||||
|
||||
/// This queues a render operation with the renderer thread. The render
|
||||
@ -311,6 +344,38 @@ const EventData = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn processExit(
|
||||
ev_: ?*EventData,
|
||||
loop: *xev.Loop,
|
||||
completion: *xev.Completion,
|
||||
r: xev.Process.WaitError!u32,
|
||||
) xev.CallbackAction {
|
||||
const code = r catch unreachable;
|
||||
log.debug("child process exited status={}", .{code});
|
||||
|
||||
const ev = ev_.?;
|
||||
ev.process_exited = true;
|
||||
ev.data_stream.close(loop, completion, EventData, ev, ttyClose);
|
||||
|
||||
return .disarm;
|
||||
}
|
||||
|
||||
fn ttyClose(
|
||||
_: ?*EventData,
|
||||
_: *xev.Loop,
|
||||
_: *xev.Completion,
|
||||
_: xev.Stream,
|
||||
r: xev.Stream.CloseError!void,
|
||||
) xev.CallbackAction {
|
||||
_ = r catch |err| {
|
||||
log.warn("error closing tty err={}", .{err});
|
||||
return .disarm;
|
||||
};
|
||||
|
||||
log.debug("tty parent closed", .{});
|
||||
return .disarm;
|
||||
}
|
||||
|
||||
fn ttyWrite(
|
||||
ev_: ?*EventData,
|
||||
_: *xev.Loop,
|
||||
@ -537,6 +602,12 @@ const Subprocess = struct {
|
||||
return pty.master;
|
||||
}
|
||||
|
||||
/// Called to notify that we exited externally so we can unset our
|
||||
/// running state.
|
||||
pub fn externalExit(self: *Subprocess) void {
|
||||
self.command = null;
|
||||
}
|
||||
|
||||
/// Stop the subprocess. This is safe to call anytime. This will wait
|
||||
/// for the subprocess to end so it will block.
|
||||
pub fn stop(self: *Subprocess) void {
|
||||
|
2
vendor/libxev
vendored
2
vendor/libxev
vendored
@ -1 +1 @@
|
||||
Subproject commit 46e3dafa477a9a92abe7b425cafdbcbe7ba3574d
|
||||
Subproject commit 9024a57ef9133382f95927eaf4b6c2b1662598e0
|
Reference in New Issue
Block a user