diff options
Diffstat (limited to '9agents/src/main.zig')
| -rw-r--r-- | 9agents/src/main.zig | 43 |
1 files changed, 34 insertions, 9 deletions
diff --git a/9agents/src/main.zig b/9agents/src/main.zig index 2ac9078..3e7c290 100644 --- a/9agents/src/main.zig +++ b/9agents/src/main.zig @@ -43,6 +43,7 @@ const usage_text = \\ /omp /hermes /dsh full mirrors, raw \\ /skills/{claude,codex,omp} the union skills view \\ /active/<harness>/<pid>/ what is running right now + \\ /active/<harness>/<pid>/chat/<id>-<kind> its conversation (claude, codex) \\ \\By default the daemon posts itself under the name `agents`, so it is \\dialable at $XDG_RUNTIME_DIR/9p/agents and mountable by 9ns --mntgen @@ -71,10 +72,27 @@ const Runner = serve.Runner(tree.Harness, tree.opts, .{ .listeners = 2, // the posted name plus one of --unix/--tcp }); -// Static memory, the 9proc-demo way: the harness and the runner are large -// (the path table, the per-connection buffers) and live in .bss. +// Static memory, the 9proc-demo way: the harness (the path table) lives in +// .bss. The runner and the chat pool are larger — every connection's fid +// table, every transcript index — and each gets a mapping of its own +// (`mapped`), backed only as far as it is used. var harness_mem: tree.Harness = undefined; -var runner_mem: Runner = undefined; + +/// A `T` in its own anonymous `MAP_NORESERVE` mapping: address space until +/// written, and never written just to initialize it (a Debug build fills +/// an `undefined` global with 0xaa, which for these is hundreds of MB). +fn mapped(comptime T: type) error{OutOfMemory}!*T { + const rc = linux.mmap(null, @sizeOf(T), .{ .READ = true, .WRITE = true }, .{ + .TYPE = .PRIVATE, + .ANONYMOUS = true, + .NORESERVE = true, + }, -1, 0); + if (linux.errno(rc) != .SUCCESS) return error.OutOfMemory; + // Small pages: with transparent huge pages on, touching each + // connection's few header fields would back 2 MB apiece. + _ = linux.madvise(@ptrFromInt(rc), @sizeOf(T), linux.MADV.NOHUGEPAGE); + return @ptrFromInt(rc); +} var stop_requested = std.atomic.Value(bool).init(false); fn onSignal(sig: linux.SIG) callconv(.c) void { @@ -243,6 +261,9 @@ fn run(init: std.process.Init) !void { return error.Usage; } + // The chat indexes live in their own lazily backed mapping: ~100 MB of + // address space, none of it memory until a chat is read. + const chats = try tree.chat.Pool.create(); harness_mem.init(.{ .io = io, .pid = @intCast(linux.getpid()), @@ -253,6 +274,7 @@ fn run(init: std.process.Init) !void { .runtime = post.getenv(envp, "XDG_RUNTIME_DIR") orelse "", .allow_move = allow_move, .envp = envp, + .chats = chats, }); if (allow_move) { std.debug.print("9agents: moves allowed — a write to /active/<h>/<pid>/zmx re-execs that agent under {s}\n", .{zmx_abs}); @@ -273,12 +295,13 @@ fn run(init: std.process.Init) !void { .posted, .unix, .tcp => {}, } - runner_mem.init(.{ .io = io, .root = tree.root, .handler = .{ .ctx = &harness_mem, .serve = serveReq } }); - defer runner_mem.stop(); + const runner = try mapped(Runner); + runner.init(.{ .io = io, .root = tree.root, .handler = .{ .ctx = &harness_mem, .serve = serveReq } }); + defer runner.stop(); var posted_something = false; if (!no_post) { - runner_mem.listenPosted(envp, post_name, 16) catch |err| { + runner.listenPosted(envp, post_name, 16) catch |err| { if (err == error.AlreadyPosted) { std.debug.print("9agents: the name `{s}` is already posted by a live server\n", .{post_name}); return err; @@ -293,7 +316,7 @@ fn run(init: std.process.Init) !void { } switch (mode) { .unix => { - _ = try runner_mem.listen(.{ .unix = try arena.dupeZ(u8, unix_path) }, 16); + _ = try runner.listen(.{ .unix = try arena.dupeZ(u8, unix_path) }, 16); std.debug.print("9agents: listening on {s}\n", .{unix_path}); }, .tcp => { @@ -301,7 +324,7 @@ fn run(init: std.process.Init) !void { std.debug.print("9agents: bad --tcp address: {s}\n", .{tcp_addr}); return error.Usage; }; - const bound = try runner_mem.listen(.{ .tcp = addr }, 16); + const bound = try runner.listen(.{ .tcp = addr }, 16); std.debug.print("9agents: listening on tcp!{f}\n", .{bound}); }, else => {}, @@ -336,7 +359,9 @@ fn serveFd(io: Io, n: i32) !void { var rbuf: [tree.msize]u8 = undefined; var wbuf: [2 * tree.msize]u8 = undefined; var stage: [tree.msize]u8 = undefined; - var engine = Engine.init(.{ .in = &in, .out = &out, .root = tree.root }); + // Far too big for the stack at this fid capacity. + const engine = try mapped(Engine); + engine.initIn(.{ .in = &in, .out = &out, .root = tree.root }); var reader = stream.reader(io, &rbuf); var writer = stream.writer(io, &wbuf); while (true) { |
