summaryrefslogtreecommitdiff
path: root/src/9p_io.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/9p_io.zig')
-rw-r--r--src/9p_io.zig40
1 files changed, 34 insertions, 6 deletions
diff --git a/src/9p_io.zig b/src/9p_io.zig
index 2ca11a87..651b3d0a 100644
--- a/src/9p_io.zig
+++ b/src/9p_io.zig
@@ -64,7 +64,9 @@ const prefix = "pardes-9p-";
pub const msize = ninep.msize;
-pub const max_conns = 4;
+/// Scripts, a mount and an agent or two at once; each slot holds its
+/// buffers (a few msize) whether used or not.
+pub const max_conns = 16;
extern "c" fn inet_pton(family: c_int, src: [*:0]const u8, dst: *anyopaque) c_int;
@@ -176,6 +178,8 @@ pub const Listener = struct {
stopping: std.atomic.Value(bool) = .init(false),
/// A connection wrote the Restore and has yet to answer it.
restore_writer: std.atomic.Value(bool) = .init(false),
+ /// Clients turned away with every slot taken, since the log last said so.
+ refused: std.atomic.Value(u32) = .init(0),
/// Connections accepted so far, and the count each slot's connection
/// was accepted at: a Restore cuts those accepted before it (`reset`),
/// and a new client in a freed slot has a later stamp. Written on the
@@ -217,6 +221,14 @@ pub const Listener = struct {
/// answers. A request that would change a pane while the editor is out
/// in a syscall mid-step is parked in the engine instead, and retried
/// when the turn is next given up between steps (`wakeParked`).
+ /// On the accepting task, without the turn: counted, and logged with
+ /// the turn (`answerHeld`).
+ fn onRefused(ctx: ?*anyopaque) void {
+ const l = of(ctx);
+ _ = l.refused.fetchAdd(1, .acq_rel);
+ l.kick();
+ }
+
fn onOpened(ctx: ?*anyopaque, conn: *Runner.Conn) void {
const l = of(ctx);
l.accepted[conn.index].store(l.accepts.fetchAdd(1, .acq_rel) + 1, .release);
@@ -296,7 +308,15 @@ pub const Listener = struct {
// Set with the turn still held, so `reset` sees it and waits.
if (restoring) l.restore_writer.store(true, .release);
defer if (restoring) l.restore_writer.store(false, .release);
+ core.fs.late_failure_len = 0;
if (core.effects_len != 0) pardes.turn.awaitSettled(epoch);
+ // ponytail: one slot, so a failure of another client's effects that
+ // settle in the same wait is told to this write too.
+ if (l.core == core and core.fs.late_failure_len != 0) {
+ const failed = pardes.ctlfs.failText(req.tag, pardes.ctlfs.E.IO, core.fs.late_failure[0..core.fs.late_failure_len]);
+ pardes.ctlfs.events.noteError(core, req, failed);
+ return conn.reply(&failed, "");
+ }
// The Restore's own write is answered once it is done, and before
// `reset` hangs every connection up, this one too, so the writer
// hears that it happened rather than a cut; a failed one leaves the
@@ -323,6 +343,11 @@ pub const Listener = struct {
const l = of(ctx);
if (l.stopping.load(.acquire)) return;
const core = l.core;
+ const refused = l.refused.swap(0, .acq_rel);
+ if (refused != 0) {
+ var why: [64]u8 = undefined;
+ pardes.ctlfs.events.notePath(core, "err -", std.fmt.bufPrint(&why, "9p: too many connections ({d} turned away)", .{refused}) catch "9p: too many connections");
+ }
if (!core.fs.news) return;
core.fs.news = false;
for (&core.fs.opens) |*o| {
@@ -816,7 +841,7 @@ pub fn listen(io: std.Io, gpa: std.mem.Allocator, core: *pardes.Pardes, named: [
l.runner.init(.{
.io = io,
.root = pardes.ctlfs.root,
- .handler = .{ .ctx = l, .serve = Listener.onServe, .opened = Listener.onOpened },
+ .handler = .{ .ctx = l, .serve = Listener.onServe, .opened = Listener.onOpened, .refused = Listener.onRefused },
.greet_timeout_ms = Listener.greet_deadline_ms,
});
const entry_name = if (named.len != 0) named else fallback;
@@ -1278,7 +1303,8 @@ test "Unix TCP and QUIC share one listener through reads writes reconnects and r
var directory: [64:0]u8 = undefined;
_ = try std.fmt.bufPrintSentinel(&directory, "/tmp/pardes-tcp-XXXXXX", .{}, 0);
if (mkdtemp(&directory) == null) return error.TempDirectoryFailed;
- defer _ = rmdir(&directory);
+ // What the listener posted and left inside goes with it.
+ defer std.Io.Dir.cwd().deleteTree(testing.io, std.mem.sliceTo(&directory, 0)) catch |err| std.debug.print("deleteTree: {s}\n", .{@errorName(err)});
const old_runtime = if (libc.getenv("XDG_RUNTIME_DIR")) |v| try gpa.dupeZ(u8, std.mem.span(v)) else null;
defer {
if (old_runtime) |v| {
@@ -1348,7 +1374,7 @@ test "Unix TCP and QUIC share one listener through reads writes reconnects and r
}
try testing.expect(l.path().len != 0);
try testing.expectEqual(tcp, l.tcp_address != null);
- try testing.expectEqual(@as(usize, 4), l.runner.conns.len);
+ try testing.expectEqual(@as(usize, max_conns), l.runner.conns.len);
try testing.expect(l.watcher == null);
const port = (if (tcp) l.tcp_address else l.quic_address).?.getPort();
try testing.expect(port != 0);
@@ -1389,7 +1415,8 @@ test "a change waits while the editor is out mid-step, a read does not, and the
var directory: [64:0]u8 = undefined;
_ = try std.fmt.bufPrintSentinel(&directory, "/tmp/pardes-park-XXXXXX", .{}, 0);
if (mkdtemp(&directory) == null) return error.TempDirectoryFailed;
- defer _ = rmdir(&directory);
+ // What the listener posted and left inside goes with it.
+ defer std.Io.Dir.cwd().deleteTree(testing.io, std.mem.sliceTo(&directory, 0)) catch |err| std.debug.print("deleteTree: {s}\n", .{@errorName(err)});
const old_runtime = if (libc.getenv("XDG_RUNTIME_DIR")) |v| try gpa.dupeZ(u8, std.mem.span(v)) else null;
defer {
if (old_runtime) |v| {
@@ -1458,7 +1485,8 @@ test "a held read is answered when the log has news, and a flushed one spends no
var directory: [64:0]u8 = undefined;
_ = try std.fmt.bufPrintSentinel(&directory, "/tmp/pardes-held-XXXXXX", .{}, 0);
if (mkdtemp(&directory) == null) return error.TempDirectoryFailed;
- defer _ = rmdir(&directory);
+ // What the listener posted and left inside goes with it.
+ defer std.Io.Dir.cwd().deleteTree(testing.io, std.mem.sliceTo(&directory, 0)) catch |err| std.debug.print("deleteTree: {s}\n", .{@errorName(err)});
const old_runtime = if (libc.getenv("XDG_RUNTIME_DIR")) |v| try gpa.dupeZ(u8, std.mem.span(v)) else null;
defer {
if (old_runtime) |v| {