Introduce deinitIfLive utility and integrate enhanced memory management
This commit is contained in:
43
src/main.zig
43
src/main.zig
@@ -1,3 +1,8 @@
|
||||
//! Entry point for the zocket daemon.
|
||||
//!
|
||||
//! Handles allocator selection (debug-checked vs. release), CLI argument
|
||||
//! parsing, and configuration path resolution before handing off to `run`.
|
||||
|
||||
const zocket = @import("zocket");
|
||||
|
||||
const std = @import("std");
|
||||
@@ -7,6 +12,14 @@ const options = @import("build_options");
|
||||
|
||||
const args = zocket.deps.args;
|
||||
|
||||
// TODO: switch vars/consts/params to snake_case
|
||||
|
||||
/// Resolves the config path, then delegates to `run`.
|
||||
///
|
||||
/// Allocator strategy:
|
||||
/// - Debug builds use `DebugAllocator`, which tracks every allocation and
|
||||
/// panics/exits on leaks or double-frees.
|
||||
/// - Release builds use `c_allocator` for C ABI interop.
|
||||
pub fn main(init: std.process.Init) !void
|
||||
{
|
||||
const debug = builtin.mode == .Debug;
|
||||
@@ -21,10 +34,13 @@ pub fn main(init: std.process.Init) !void
|
||||
|
||||
const allocator = if (debug) gpa.allocator() else std.heap.c_allocator;
|
||||
|
||||
var tmpArena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
||||
const tmpAllocator = tmpArena.allocator();
|
||||
var tmp_arena = std.heap.ArenaAllocator.init(allocator);
|
||||
var arena_live = true;
|
||||
|
||||
var parser = try args.ArgumentParser.init(tmpAllocator, .{
|
||||
const tmp_allocator = tmp_arena.allocator();
|
||||
errdefer zocket.memory.deinitIfLive(&tmp_arena, &arena_live);
|
||||
|
||||
var parser = try args.ArgumentParser.init(tmp_allocator, .{
|
||||
.name = "zocket",
|
||||
.version = options.version,
|
||||
.description = "A lightweight Zig WebSocket server and mesh routing daemon.",
|
||||
@@ -39,23 +55,30 @@ pub fn main(init: std.process.Init) !void
|
||||
|
||||
const path = if (debug) path:
|
||||
{
|
||||
const tmpPath = result.getString("path") orelse "config/config.toml";
|
||||
break :path try allocator.dupe(u8, tmpPath);
|
||||
const tmp_path = result.getString("path") orelse "config/config.toml";
|
||||
break :path try allocator.dupe(u8, tmp_path);
|
||||
}
|
||||
else path:
|
||||
{
|
||||
const tmpPath = result.getString("path") orelse blk: {
|
||||
const exe = try std.process.executableDirPathAlloc(io, tmpAllocator);
|
||||
break :blk try std.Io.Dir.path.join(tmpAllocator, &.{ exe, "config/config.toml" });
|
||||
const tmp_path = result.getString("path") orelse blk: {
|
||||
const exe = try std.process.executableDirPathAlloc(io, tmp_allocator);
|
||||
break :blk try std.Io.Dir.path.join(tmp_allocator, &.{ exe, "config/config.toml" });
|
||||
};
|
||||
break :path try allocator.dupe(u8, tmpPath);
|
||||
break :path try allocator.dupe(u8, tmp_path);
|
||||
};
|
||||
|
||||
tmpArena.deinit();
|
||||
zocket.memory.deinitIfLive(&tmp_arena, &arena_live);
|
||||
|
||||
try run(io, allocator, path);
|
||||
}
|
||||
|
||||
/// Loads and parses the configuration file at `path`, then hands off to
|
||||
/// the rest of the application.
|
||||
///
|
||||
/// Owns `path` and frees it once it's no longer needed; Callers must not free `path` themselves!
|
||||
///
|
||||
/// On parse failure, logs a human-readable reason and terminates the
|
||||
/// process immediately via `std.process.exit`.
|
||||
fn run(io: std.Io, allocator: std.mem.Allocator, path: []const u8) !void
|
||||
{
|
||||
const parsed = zocket.config.parse(io, allocator, path) catch |err|
|
||||
|
||||
27
src/memory/util.zig
Normal file
27
src/memory/util.zig
Normal file
@@ -0,0 +1,27 @@
|
||||
const std = @import("std");
|
||||
|
||||
/// Deinitializes `allocator` only if it is still live.
|
||||
///
|
||||
/// Pass a pointer to the original allocator owner, such as an
|
||||
/// `ArenaAllocator` or `DebugAllocator`.
|
||||
///
|
||||
/// If `live` is `true`, calls `allocator.deinit()` and then sets
|
||||
/// `live` to `false`, preventing a later cleanup path from attempting
|
||||
/// to deinitialize the same allocator again.
|
||||
///
|
||||
/// The return value of `deinit()` is discarded.
|
||||
pub fn deinitIfLive(allocator: anytype, live: *bool) void
|
||||
{
|
||||
comptime
|
||||
{
|
||||
const T = @TypeOf(allocator);
|
||||
|
||||
if (@typeInfo(T) != .pointer) @compileError("deinitIfLive expects a pointer!");
|
||||
if (T == std.mem.Allocator) @compileError("deinitIfLive expects a pointer to an allocator owner, not std.mem.Allocator!");
|
||||
}
|
||||
|
||||
if (live.*) {
|
||||
_ = allocator.deinit();
|
||||
live.* = false;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,9 @@ pub const deps = struct {
|
||||
|
||||
|
||||
pub const config = @import("config/config.zig");
|
||||
pub const memory = @import("memory/util.zig");
|
||||
|
||||
test {
|
||||
_ = config;
|
||||
_ = memory;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user