diff options
Diffstat (limited to 'src/detached')
| -rw-r--r-- | src/detached/client.zig | 17 | ||||
| -rw-r--r-- | src/detached/server.zig | 18 |
2 files changed, 20 insertions, 15 deletions
diff --git a/src/detached/client.zig b/src/detached/client.zig index 4769cf25..fc39164b 100644 --- a/src/detached/client.zig +++ b/src/detached/client.zig @@ -1129,9 +1129,20 @@ test "detached session: a frontend that stops reading is dropped, not waited for for (0..24) |_| { if (!h.session.clients[1].attached) break; host.vtable.push_set_clipboard.?(host.ctx, text); - try h.pump(); - try good.wait(5); - while (try good.next()) |_| {} + // Read `good` back to EMPTY before the next mirror, rather than + // pumping once and taking whatever one write fitted. One pump moves at + // most one socket buffer, and that buffer is 8 KiB here + // (`net.local.stream.sendspace` on Darwin) against Linux's 208 KiB — + // so a single pump per 256 KiB mirror leaves the READING frontend + // falling behind by a quarter megabyte a round and closes it for a + // backlog it never caused. The claim under test is that a frontend + // that reads survives, so it has to actually finish reading. + for (0..256) |_| { + try h.pump(); + try good.wait(5); + while (try good.next()) |_| {} + if (h.session.clients[0].out.items.len == 0) break; + } } // Dropped rather than queued without bound, and rather than the core // blocking on it. diff --git a/src/detached/server.zig b/src/detached/server.zig index 7d286177..2c4df87b 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -141,7 +141,6 @@ const std = @import("std"); const builtin = @import("builtin"); const libc = std.c; -const linux = std.os.linux; const posix = std.posix; const pardes = @import("../pardes.zig"); const host_api = @import("../host.zig"); @@ -1221,17 +1220,13 @@ pub const Session = struct { /// then keeps its own record of what was asked and simply never gets a /// reload, which is what a host with no watcher has always done. /// - /// NONBLOCK because this descriptor is drained from `poll`, not from a - /// thread parked in `read` (tty.zig `watchFiles`): `drainInotify` must be - /// able to stop. + /// `polled` is true because this descriptor is drained from `poll`, not + /// from a thread parked in a wait (tty.zig `watchFiles`): `drainInotify` + /// must be able to stop. On linux that is IN_NONBLOCK; on macos a kqueue + /// needs nothing, since the timeout argument to `kevent(2)` decides. fn inotify(s: *Session) c_int { if (s.inotify_fd >= 0) return s.inotify_fd; - // The whole body is inside the comptime branch so that neither - // `inotify_init1` nor `linux.IN` is even analysed on a platform that has - // no inotify — the same shape file_watch.zig's `watchPath` uses. - if (comptime builtin.os.tag == .linux) { - s.inotify_fd = libc.inotify_init1(linux.IN.CLOEXEC | linux.IN.NONBLOCK); - } + s.inotify_fd = file_watch.init(true); return s.inotify_fd; } @@ -1252,8 +1247,7 @@ pub const Session = struct { /// frontend got a frame per round it could not use. The fd is IN_NONBLOCK /// (`inotify`), so the loop ends on EAGAIN. fn drainInotify(s: *Session) void { - var buf: [4096]u8 = undefined; - while (libc.read(s.inotify_fd, &buf, buf.len) > 0) s.check_files = true; + if (file_watch.drain(s.inotify_fd)) s.check_files = true; } /// Reconcile every marked pane and the theme file. Called at the END of |
