diff options
Diffstat (limited to 'src/macos.zig')
| -rw-r--r-- | src/macos.zig | 41 |
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); |
