diff options
Diffstat (limited to 'src/detached/server.zig')
| -rw-r--r-- | src/detached/server.zig | 70 |
1 files changed, 43 insertions, 27 deletions
diff --git a/src/detached/server.zig b/src/detached/server.zig index 5bf2384b..5374bf73 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -150,6 +150,8 @@ pub const Session = struct { worker_gpa: std.mem.Allocator, io: std.Io, core: *pardes.Pardes, + /// PARDES_TEST_CLOCK's virtual time (host_io.testClock). + test_clock: ?u64 = null, listener: c_int = -1, path_buf: [sun_path_len]u8 = undefined, path_len: usize = 0, @@ -355,8 +357,14 @@ pub const Session = struct { return @ptrCast(@alignCast(ctx.?)); } + fn clockNow(ctx: ?*anyopaque) u64 { + const s = of(ctx); + return s.test_clock orelse host_io.monotonicNs(); + } + const vtable: host_io.Host.VTable = .{ .wait_input = waitInput, + .now = clockNow, .present = present, .poll_frame = pollFrame, .spawn = spawn, @@ -484,7 +492,7 @@ pub const Session = struct { s.ninep, ) catch |err| return s.core.reportError(pane, "shell", err); const command = if (s.core.panes[pane]) |pn| pn.command != null else false; - s.ptys[pane] = .{ .fd = child.file.handle, .pid = child.pid, .cmd = .{ .watched = command and host_io.watchExit(child.pid) } }; + s.ptys[pane] = .{ .fd = child.file.handle, .pid = child.pid, .cmd = .{ .watched = command and host_io.watchExit(child.pid), .fd = child.file.handle } }; setNonblock(child.file.handle); var lbuf: [pardes.memory.limits.host_path_cap + 1]u8 = undefined; if (host_io.shellCwd(child.pid, &lbuf)) |wd| s.core.setCwd(pane, wd); @@ -697,11 +705,7 @@ pub const Session = struct { // 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; - s.core.update(.{ .eof = .{ .pane = pane } }); - return s.takeExits(); - } + if (pt.cmd.watched) return host_io.commandEof(s.core, &s.ptys, pane, s, closeWatched); // Unwatched, a command's exit is read here, as its end. const unwatched = if (s.core.panes[pane]) |pn| pn.command != null else false; var status: ?u8 = null; @@ -715,21 +719,13 @@ pub const Session = struct { s.core.update(.{ .eof = .{ .pane = pane } }); } - /// Each watched child that exited: reaped, the core told, and its pty - /// closed if its end of file came first. + /// The command panes' exits, told once their output is in (host_io). fn takeExits(s: *Session) void { - while (host_io.takeExited()) |pid| for (&s.ptys) |*pt| { - if (pt.fd < 0 or 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 (&s.ptys, 0..) |*pt, id| { - if (pt.fd < 0 or !pt.cmd.due()) continue; - pt.cmd.told = true; - s.core.update(.{ .exited = .{ .pane = @intCast(id), .status = pt.cmd.status } }); - if (pt.cmd.eof) s.closePty(@intCast(id)); - } + host_io.takeExits(s.core, &s.ptys, s, closeWatched); + } + + fn closeWatched(s: *Session, id: usize) void { + s.closePty(@intCast(id)); } fn harvest(s: *Session) void { @@ -779,7 +775,7 @@ pub const Session = struct { fn sendFrame(s: *Session, c: *Client, surface: *const pardes.Surface) void { const cells = surface.cells; - const want = wire.frameBound(surface.cols, surface.rows) + wire.layersBound(&surface.body_layers, &surface.tag_layers); + const want = wire.frameBound(surface.cols, surface.rows) + wire.layersBound(&surface.body_layers, &surface.tag_layers, surface.regionList()); s.scratch.ensureTotalCapacity(s.gpa, want) catch return s.close(c, .oom); const prev: []const pardes.Cell = if (c.need_full or c.mirror.items.len != cells.len) &.{} @@ -799,6 +795,8 @@ pub const Session = struct { prev, &surface.body_layers, &surface.tag_layers, + surface.regionList(), + surface.chrome, ) catch |err| { log.debug("frame {d}x{d} not encodable: {t}", .{ surface.cols, surface.rows, err }); return; @@ -879,8 +877,9 @@ pub const Session = struct { pardes.turn.rest(); nap(if (timeout_ms == 0) 16 else timeout_ms); pardes.turn.wake(); - // An animating frame's wait is the frame: tick it. - if (timeout_ms != 0) s.core.update(.tick); + if (timeout_ms != 0) if (s.test_clock) |*virtual| { + virtual.* = @max(virtual.*, s.core.nextWake() orelse virtual.*); + }; return; } var timeout: c_int = if (timeout_ms == 0) -1 else @intCast(@min(timeout_ms, std.math.maxInt(c_int))); @@ -890,9 +889,10 @@ pub const Session = struct { const ready = libc.poll(&fds, @intCast(n), timeout); pardes.turn.wake(); if (ready > 0) s.dispatch(fds[0..n], src[0..n]); - // Animating, the frame's time is up: advance it. There is no other - // tick in a detached session, and without it an animation never ends. - if (timeout_ms != 0 and monotonicMs() -| now >= timeout_ms) s.core.update(.tick); + // The core's wake ran out; the pump advances it from `now`. + if (timeout_ms != 0 and monotonicMs() -| now >= timeout_ms) if (s.test_clock) |*virtual| { + virtual.* = @max(virtual.*, s.core.nextWake() orelse virtual.*); + }; _ = s.drainCompletions(true); s.takeExits(); s.expire(monotonicMs()); @@ -1181,6 +1181,17 @@ pub const Session = struct { } }; +/// SIGTERM, SIGINT or SIGHUP: quit as Exit does, through the loop, so its +/// sockets are unlinked on the way out rather than left for the next +/// session to trip on. The handler only sets the flag and wakes the loop. +var quit_signalled = std.atomic.Value(bool).init(false); +var quit_wake: c_int = -1; + +fn onQuitSignal(_: std.posix.SIG) callconv(.c) void { + quit_signalled.store(true, .release); + if (quit_wake >= 0) _ = libc.send(quit_wake, "q", 1, nosignal); +} + pub fn run(init: std.process.Init, opts: pardes.Options, name: []const u8) !void { const gpa = init.gpa; const allocs = pardes.memory.init(gpa); @@ -1213,6 +1224,7 @@ pub fn run(init: std.process.Init, opts: pardes.Options, name: []const u8) !void .cols = options.cols, .rows = options.rows, .prompt_rcs = host_io.Shell.prepare(), + .test_clock = host_io.testClock(), }; defer session.core.deinit(); defer session.deinit(); @@ -1231,7 +1243,11 @@ pub fn run(init: std.process.Init, opts: pardes.Options, name: []const u8) !void core.host = h; while (core.nextEffect()) |effect| core.perform(effect); session.in_loop = true; - while (!session.core.quit) { + quit_wake = session.mailbox.wake[1]; + const on_quit: std.posix.Sigaction = .{ .handler = .{ .handler = onQuitSignal }, .mask = std.posix.sigemptyset(), .flags = 0 }; + for ([_]std.posix.SIG{ .HUP, .INT, .TERM }) |sig| std.posix.sigaction(sig, &on_quit, null); + defer quit_wake = -1; + while (!session.core.quit and !quit_signalled.load(.acquire)) { pardes.turn.restoreSettled(); try session.core.pump(h); if (session.core.quit) break; |
