summaryrefslogtreecommitdiff
path: root/src/detached/server.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/detached/server.zig')
-rw-r--r--src/detached/server.zig17
1 files changed, 16 insertions, 1 deletions
diff --git a/src/detached/server.zig b/src/detached/server.zig
index 2d7a5238..7d286177 100644
--- a/src/detached/server.zig
+++ b/src/detached/server.zig
@@ -1056,6 +1056,9 @@ pub const Session = struct {
// `Twrite` to `ctl` can `push_spawn`, and `dispatch` is mid-iteration
// over a descriptor snapshot when it runs.
if (s.ninep) |l| {
+ // Before the drain, so a slot held by silence is taken back on the
+ // same frame it expires rather than one drain later.
+ l.expire();
var pending = false;
for (0..fs9_service.max_conns) |i| {
const t = l.transport(@intCast(i)) orelse continue;
@@ -1431,7 +1434,11 @@ pub const Session = struct {
// a pty's — asking for it unconditionally makes every idle socket a
// ready descriptor and turns the poll into a spin.
if (s.ninep) |l| {
- if (l.fd >= 0) {
+ // `accepting`, not `fd >= 0`: a listener paused after an EMFILE
+ // must leave the set, or the backlog it could not drain reports
+ // ready on every poll and spins the core. `nextDue` carries the
+ // moment it comes back.
+ if (l.accepting()) {
fds[n] = .{ .fd = l.fd, .events = poll_in, .revents = 0 };
src[n] = .ninep_listener;
n += 1;
@@ -1600,6 +1607,14 @@ pub const Session = struct {
}
if (s.listener >= 0 and s.accept_paused_ms > now)
due = if (due) |d| @min(d, s.accept_paused_ms) else s.accept_paused_ms;
+ // ...and the 9P listener's own greet deadline, for the reason its
+ // `expire` gives: four slots is a cheaper denial than thirty-two.
+ if (s.ninep) |l| {
+ if (l.nextDue()) |ms| {
+ const at9 = now + ms;
+ due = if (due) |d| @min(d, at9) else at9;
+ }
+ }
const at = due orelse return null;
return @intCast(@max(0, @min(at - now, std.math.maxInt(c_int))));
}