Something went wrong. Try again.
zig utility library forked from karlseguin.tngl.sh/zul
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312const std = @import("std");const Io = std.Io;
const Allocator = std.mem.Allocator;const ArenaAllocator = std.heap.ArenaAllocator;
pub fn LockRefArenaArc(comptime T: type) type { // FullArgs is a tuple that represents the arguments to T.init(...) // The first argument to T.init must always be an Allocator (something we enforce here) // But, when the init(...) and setValue(...) methods of this struct are called // we don't expect FullArgs. We expect something like FullArgs[1..], that is, // the arguments without the allocator. This is because we'll inject an arena // allocator into the args within init/setValue. // Thus, Args, more or less, FullArgs[1..], but we can't just do FullArgs[1..] // we need to build a new Struct. const FullArgs = std.meta.ArgsTuple(@TypeOf(T.init)); const args_info: std.lang.Type = @typeInfo(FullArgs); const info = args_info.@"struct"; const ARG_COUNT = info.field_types.len;
if (ARG_COUNT == 0 or info.field_types[0] != std.mem.Allocator) { @compileError("The first argument to " ++ @typeName(T) ++ ".init must be an std.mem.Allocator"); }
var arg_fields: [info.field_types.len - 1]type = undefined; inline for (info.field_types[1..], 0..) |field, index| { arg_fields[index] = field; }
const Args = @Tuple( &arg_fields, );
return struct { arc: *Arc, allocator: Allocator, mutex: Io.Mutex, io: Io,
const Self = @This();
pub fn init(io: Io, allocator: Allocator, args: Args) !Self { return .{ .mutex = .init, .allocator = allocator, .arc = try createArc(allocator, args), .io = io, }; }
pub fn initWithValue(io: Io, allocator: Allocator, value: T) !Self { const arena = try allocator.create(ArenaAllocator); errdefer allocator.destroy(arena);
arena.* = std.heap.ArenaAllocator.init(allocator); errdefer arena.deinit();
const arc = try arena.allocator().create(Self.Arc); arc.* = .{ ._rc = 1, .arena = arena, .value = value, };
return .{ .arc = arc, .mutex = .init, .allocator = allocator, .io = io, }; }
pub fn deinit(self: *Self) void { self.mutex.lockUncancelable(self.io); self.arc.release(); self.mutex.unlock(self.io); }
pub fn acquire(self: *Self) *Arc { self.mutex.lockUncancelable(self.io); defer self.mutex.unlock(self.io); var arc = self.arc; arc.acquire(); return arc; }
pub fn setValue(self: *Self, args: Args) !void { const arc = try createArc(self.allocator, args); try self.mutex.lock(self.io); var existing = self.arc; self.arc = arc; self.mutex.unlock(self.io); existing.release(); }
fn createArc(allocator: Allocator, args: Args) !*Arc { const arena = try allocator.create(ArenaAllocator); errdefer allocator.destroy(arena);
arena.* = std.heap.ArenaAllocator.init(allocator); errdefer arena.deinit();
const aa = arena.allocator(); // args doesn't contain our allocator // we're going to push the arc.arena.allocator at the head of args // which means creating a new args and copying the values over var full_args: FullArgs = undefined; full_args[0] = aa; inline for (1..ARG_COUNT) |i| { full_args[i] = args[i - 1]; }
const arc = try aa.create(Arc); arc.* = .{ ._rc = 1, .arena = arena, .value = try @call(.auto, T.init, full_args), }; return arc; }
pub fn jsonStringify(self: *Self, jws: anytype) !void { var arc = self.acquire(); defer arc.release(); return jws.write(arc.value); }
pub const Arc = struct { value: T, arena: *ArenaAllocator, _rc: usize,
pub fn acquire(self: *Arc) void { _ = @atomicRmw(usize, &self._rc, .Add, 1, .monotonic); }
pub fn release(self: *Arc) void { // returns the value before the sub, so if the value before the sub was 1, // it means we no longer have anything referencing this if (@atomicRmw(usize, &self._rc, .Sub, 1, .monotonic) == 1) { const arena = self.arena; const allocator = self.arena.child_allocator; arena.deinit(); allocator.destroy(arena); } }
pub fn jsonStringify(self: *const Arc, jws: anytype) !void { return jws.write(self.value); } }; };}
pub fn LockRefArc(comptime T: type) type { return struct { arc: *Arc, allocator: Allocator, mutex: Io.Mutex, io: Io,
const Self = @This();
pub fn init(io: Io, allocator: Allocator, value: T) !Self { return .{ .mutex = .init, .allocator = allocator, .arc = try createArc(allocator, value), .io = io, }; }
pub fn deinit(self: *Self) void { self.mutex.lockUncancelable(self.io); self.arc.release(); self.mutex.unlock(self.io); }
pub fn acquire(self: *Self) *Arc { self.mutex.lockUncancelable(self.io); defer self.mutex.unlock(self.io); var arc = self.arc; arc.acquire(); return arc; }
pub fn setValue(self: *Self, value: T) !void { const arc = try createArc(self.allocator, value); try self.mutex.lock(self.io); var existing = self.arc; self.arc = arc; self.mutex.unlock(self.io); existing.release(); }
fn createArc(allocator: Allocator, value: T) !*Self.Arc { const arc = try allocator.create(Self.Arc); arc.* = .{ ._rc = 1, .value = value, .allocator = allocator, }; return arc; }
pub fn jsonStringify(self: *Self, jws: anytype) !void { var arc = self.acquire(); defer arc.release(); return jws.write(arc.value); }
pub const Arc = struct { value: T, allocator: Allocator, _rc: usize,
pub fn acquire(self: *Arc) void { _ = @atomicRmw(usize, &self._rc, .Add, 1, .monotonic); }
pub fn release(self: *Arc) void { // returns the value before the sub, so if the value before the sub was 1, // it means we no longer have anything referencing this if (@atomicRmw(usize, &self._rc, .Sub, 1, .monotonic) == 1) { const allocator = self.allocator; allocator.destroy(self); } }
pub fn jsonStringify(self: *const Arc, jws: anytype) !void { return jws.write(self.value); } }; };}
const t = @import("zul.zig").testing;test "LockRefArenaArc" { { var ref = try LockRefArenaArc(TestArenaValue).init(t.io, t.allocator, .{"test"}); ref.deinit(); }
var ref = try LockRefArenaArc(TestArenaValue).init(t.io, t.allocator, .{"hello"}); defer ref.deinit();
// keep this one around and re-test it at the end, it should still be valid // and still be the same value const arc1 = ref.acquire(); defer arc1.release(); try t.expectEqual("hello", arc1.value.str);
try ref.setValue(.{"world"});
{ const arc2 = ref.acquire(); defer arc2.release(); try t.expectEqual("world", arc2.value.str); }
// this reference should still be valid try t.expectEqual("hello", arc1.value.str);}
test "LockRefArenaArc: initWithValue" { var ref = try LockRefArenaArc(TestArenaValue).initWithValue(t.io, t.allocator, .{ .str = "teg" }); defer ref.deinit();
const arc1 = ref.acquire(); defer arc1.release(); try t.expectEqual("teg", arc1.value.str);}
test "LockRefArc" { { var ref = try LockRefArc(TestValue).init(t.io, t.allocator, .{ .str = "test" }); ref.deinit(); }
var ref = try LockRefArc(TestValue).init(t.io, t.allocator, .{ .str = "hello" }); defer ref.deinit();
// keep this one around and re-test it at the end, it should still be valid // and still be the same value const arc1 = ref.acquire(); defer arc1.release(); try t.expectEqual("hello", arc1.value.str);
try ref.setValue(.{ .str = "world" });
{ const arc2 = ref.acquire(); defer arc2.release(); try t.expectEqual("world", arc2.value.str); }
// this reference should still be valid try t.expectEqual("hello", arc1.value.str);}
const TestArenaValue = struct { str: []const u8,
fn init(allocator: Allocator, original: []const u8) !TestArenaValue { return .{ .str = try allocator.dupe(u8, original) }; }};
const TestValue = struct { str: []const u8,};