Skip to content

Commit

Permalink
termio/exec: if pty fd HUP, end read thread
Browse files Browse the repository at this point in the history
Fixes ghostty-org#4884

When our command exits, it will close the pty slave fd. This will
trigger a HUP on our poll. Previously, we only checked for IN. When a fd
is closed, IN triggers forever which would leave to an infinite loop and
100% CPU.

Now, detect the HUP and exit the read thread.
  • Loading branch information
mitchellh committed Jan 24, 2025
1 parent fd8caca commit 8475768
Showing 1 changed file with 18 additions and 2 deletions.
20 changes: 18 additions & 2 deletions src/termio/Exec.zig
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,17 @@ pub fn threadExit(self: *Exec, td: *termio.Termio.ThreadData) void {
// Quit our read thread after exiting the subprocess so that
// we don't get stuck waiting for data to stop flowing if it is
// a particularly noisy process.
_ = posix.write(exec.read_thread_pipe, "x") catch |err|
log.warn("error writing to read thread quit pipe err={}", .{err});
_ = posix.write(exec.read_thread_pipe, "x") catch |err| switch (err) {
// BrokenPipe means that our read thread is closed already,
// which is completely fine since that is what we were trying
// to achieve.
error.BrokenPipe => {},

else => log.warn(
"error writing to read thread quit pipe err={}",
.{err},
),
};

if (comptime builtin.os.tag == .windows) {
// Interrupt the blocking read so the thread can see the quit message
Expand Down Expand Up @@ -1467,6 +1476,13 @@ pub const ReadThread = struct {
log.info("read thread got quit signal", .{});
return;
}

// If our pty fd is closed, then we're also done with our
// read thread.
if (pollfds[0].revents & posix.POLL.HUP != 0) {
log.info("pty fd closed, read thread exiting", .{});
return;
}
}
}

Expand Down

0 comments on commit 8475768

Please sign in to comment.