diff options
Diffstat (limited to 'src/macos.zig')
| -rw-r--r-- | src/macos.zig | 64 |
1 files changed, 56 insertions, 8 deletions
diff --git a/src/macos.zig b/src/macos.zig index 97dab9a8..1327b064 100644 --- a/src/macos.zig +++ b/src/macos.zig @@ -126,8 +126,39 @@ const Pty = struct { pid: posix.pid_t, gen: u32, reader: std.Io.Future(anyerror!void), + /// A command pane's child, watched to its exit (host_io.watchExit); + /// the pty stays open until both its end of file and its exit. + cmd: host_io.CommandWatch = .{}, }; +/// A watched child exited: wake the host, which takes it (`takeExits`). +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; +} + const WatchedFile = struct { path: []u8, serial: u32, @@ -783,6 +814,7 @@ fn initCore(runtime: ?*const Runtime, cols_arg: u16, rows_arg: u16) !void { for (&st.ptys, 0..) |*slot, id| if (slot.*) |*pt| startReader(st, pt, @intCast(id)); pardes.lsp.setStatusSink(st, lspStatusSink); + host_io.setExitWake(st, wakeForExit); // From here the core is touched only inside the entry points above, // each of which takes the turn for its own duration; between them the // 9P connections have it. @@ -802,6 +834,7 @@ export fn pardes_deinit() void { st.ninep = null; } pardes.lsp.setStatusSink(null, null); + host_io.setExitWake(null, null); if (st.lsp_task) |*t| { t.future.cancel(st.io) catch {}; st.lsp_task = null; @@ -1269,7 +1302,7 @@ export fn pardes_watch_changed(pane: u8, generation: u32) void { fn drainInbox(st: *State) bool { var batch = st.inbox.take(st.io); - var did = batch.len > 0; + var did = takeExits(st) or batch.len > 0; for (batch.slice()) |msg| { defer msg.free(st.gpa); switch (msg) { @@ -1280,14 +1313,28 @@ fn drainInbox(st: *State) bool { }, .eof => |e| { if (st.gens[e.pane] != e.gen) continue; - // Reaped here, its status kept, it is no shell to retire. - const status = if (st.ptys[e.pane]) |*pt| status: { - const got = host_io.exitStatus(pt.pid, 100); - if (got != null) pt.pid = 0; - break :status got; - } else null; + if (st.ptys[e.pane]) |*pt| { + // A command's pty stays open until its child has exited: + // 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); + continue; + } + // Unwatched, a command's exit is read here, as its end. + if (st.core.panes[e.pane]) |pane| if (pane.command != null) { + const status = host_io.exitStatus(pt.pid, 100); + if (status != null) pt.pid = 0; // reaped: no shell to retire + reap(st, e.pane); + st.core.update(.{ .exited = .{ .pane = e.pane, .status = status } }); + st.core.update(.{ .eof = .{ .pane = e.pane } }); + continue; + }; + } reap(st, e.pane); - st.core.update(.{ .eof = .{ .pane = e.pane, .status = status } }); + st.core.update(.{ .eof = .{ .pane = e.pane } }); }, .lsp_done => |d| { st.core.update(.{ .lsp_resp = .{ .id = d.id, .rows = d.rows } }); @@ -2413,6 +2460,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) }, }; var lbuf: [1024]u8 = undefined; if (host_io.shellCwd(child.pid, &lbuf)) |wd| core.setCwd(pane, wd); |
