summaryrefslogtreecommitdiff
path: root/src/macos.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/macos.zig')
-rw-r--r--src/macos.zig41
1 files changed, 16 insertions, 25 deletions
diff --git a/src/macos.zig b/src/macos.zig
index d6ceb535..70469618 100644
--- a/src/macos.zig
+++ b/src/macos.zig
@@ -132,31 +132,22 @@ const Pty = struct {
};
/// A watched child exited: wake the host, which takes it (`takeExits`).
+/// Called on a watcher thread holding host_io's exit lock, which
+/// pardes_deinit takes to clear the wake: safe only because the runtime's
+/// wakeup never waits for the main thread (AppDelegate's is a
+/// DispatchQueue.main.async); one that did would deadlock the quit.
fn wakeForExit(ctx: ?*anyopaque) void {
const st: *State = @ptrCast(@alignCast(ctx orelse return));
wake(st);
}
-/// Each watched child that exited: reaped, the core told, and its pty
-/// closed if its end of file came first.
-fn takeExits(st: *State) bool {
- var did = false;
- while (host_io.takeExited()) |pid| for (&st.ptys) |*slot| {
- const pt = if (slot.*) |*pt| pt else continue;
- if (pt.pid != pid or pt.cmd.exited) continue;
- pt.cmd.exit((host_io.reapExited(pid) orelse break).status);
- pt.pid = 0; // reaped: no signal or retire may reach whoever gets it next
- break;
- };
- for (&st.ptys, 0..) |*slot, id| {
- const pt = if (slot.*) |*pt| pt else continue;
- if (!pt.cmd.due()) continue;
- pt.cmd.told = true;
- st.core.update(.{ .exited = .{ .pane = @intCast(id), .status = pt.cmd.status } });
- if (pt.cmd.eof) reap(st, @intCast(id));
- did = true;
- }
- return did;
+/// The command panes' exits, told once their output is in (host_io).
+fn takeExits(st: *State) void {
+ host_io.takeExits(st.core, &st.ptys, st, closeWatched);
+}
+
+fn closeWatched(st: *State, id: usize) void {
+ reap(st, @intCast(id));
}
const WatchedFile = struct {
@@ -1302,7 +1293,9 @@ export fn pardes_watch_changed(pane: u8, generation: u32) void {
fn drainInbox(st: *State) bool {
var batch = st.inbox.take(st.io);
- var did = takeExits(st) or batch.len > 0;
+ // After this batch's output, which an exit is told behind.
+ defer takeExits(st);
+ var did = batch.len > 0;
for (batch.slice()) |msg| {
defer msg.free(st.gpa);
switch (msg) {
@@ -1318,9 +1311,7 @@ fn drainInbox(st: *State) bool {
// closing it would hang up one that runs on without it.
// Its exit, if it came first, is told now its output is in.
if (pt.cmd.watched) {
- pt.cmd.eof = true;
- st.core.update(.{ .eof = .{ .pane = e.pane } });
- _ = takeExits(st);
+ host_io.commandEof(st.core, &st.ptys, e.pane, st, closeWatched);
continue;
}
// Unwatched, a command's exit is read here, as its end.
@@ -2460,7 +2451,7 @@ fn spawnShell(ctx: ?*anyopaque, pane: u8, cwd: []const u8) void {
.pid = child.pid,
.gen = gen,
.reader = .{ .any_future = null, .result = {} },
- .cmd = .{ .watched = (if (core.panes[pane]) |pn| pn.command != null else false) and host_io.watchExit(child.pid) },
+ .cmd = .{ .watched = (if (core.panes[pane]) |pn| pn.command != null else false) and host_io.watchExit(child.pid), .fd = child.file.handle },
};
var lbuf: [1024]u8 = undefined;
if (host_io.shellCwd(child.pid, &lbuf)) |wd| core.setCwd(pane, wd);