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 zocket = @import("zocket");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
@@ -7,6 +12,14 @@ const options = @import("build_options");
|
|||||||
|
|
||||||
const args = zocket.deps.args;
|
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
|
pub fn main(init: std.process.Init) !void
|
||||||
{
|
{
|
||||||
const debug = builtin.mode == .Debug;
|
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;
|
const allocator = if (debug) gpa.allocator() else std.heap.c_allocator;
|
||||||
|
|
||||||
var tmpArena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
var tmp_arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
const tmpAllocator = tmpArena.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",
|
.name = "zocket",
|
||||||
.version = options.version,
|
.version = options.version,
|
||||||
.description = "A lightweight Zig WebSocket server and mesh routing daemon.",
|
.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 path = if (debug) path:
|
||||||
{
|
{
|
||||||
const tmpPath = result.getString("path") orelse "config/config.toml";
|
const tmp_path = result.getString("path") orelse "config/config.toml";
|
||||||
break :path try allocator.dupe(u8, tmpPath);
|
break :path try allocator.dupe(u8, tmp_path);
|
||||||
}
|
}
|
||||||
else path:
|
else path:
|
||||||
{
|
{
|
||||||
const tmpPath = result.getString("path") orelse blk: {
|
const tmp_path = result.getString("path") orelse blk: {
|
||||||
const exe = try std.process.executableDirPathAlloc(io, tmpAllocator);
|
const exe = try std.process.executableDirPathAlloc(io, tmp_allocator);
|
||||||
break :blk try std.Io.Dir.path.join(tmpAllocator, &.{ exe, "config/config.toml" });
|
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);
|
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
|
fn run(io: std.Io, allocator: std.mem.Allocator, path: []const u8) !void
|
||||||
{
|
{
|
||||||
const parsed = zocket.config.parse(io, allocator, path) catch |err|
|
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 config = @import("config/config.zig");
|
||||||
|
pub const memory = @import("memory/util.zig");
|
||||||
|
|
||||||
test {
|
test {
|
||||||
_ = config;
|
_ = config;
|
||||||
|
_ = memory;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user