feature. See also
. The project being documented here (as the example) is the Zig library itself.
SafeAllocator.free
fn free(ctx: *anyopaque, memory: []u8, alignment: Alignment, ra: usize) void
File
Code
fn free(ctx: *anyopaque, memory: []u8, alignment: Alignment, ra: usize) void {
const s: *SafeAllocator = @ptrCast(@alignCast(ctx));
const f: *AllocFooter = .of(memory);
f.startModify(.freeing, s, .{ .memory = memory, .alignment = alignment });
if (s.isLarge(memory.len, alignment)) {
@branchHint(.unlikely);
const t = s.acquireThread();
freeAllocEntry(t, f.extended().container.large_entry);
t.mutex.unlock();
s.backing.rawFree(
memory.ptr[0..AllocFooter.allocLenLarge(s, memory.len)],
AllocFooter.allocAlign(alignment),
ra,
);
return;
}
const b: *Bucket = .of(s, memory.ptr);
s.overwriteFreed(memory);
captureStackTrace(f.freeTrace(s), ra);
// This way, if another thread is waiting for the trace to become
// available, it will not be racing with us to see this `.release`.
//
// The below alloc count update can not be moved up here instead
// since that would allow another thread to see the `.freeing` state.
b.alloc_count.fenceAcqRel();
// is in the process of panicing. So, just ignore it. (This is also
// the reasoning for several other places.)
_ = @atomicRmw(
AllocFooter.Modify,
&f.modify,
.Xchg,
.storedXor(.freed, &f.modify),
.monotonic,
);
const prev_count = @atomicRmw(
Bucket.AllocCount,
&b.alloc_count,
.Sub,
.{ .filling = false, .n = 1 },
.acq_rel,
);
if (prev_count.n - 1 == 0 and !prev_count.filling) {
@branchHint(.unlikely);
const t = s.acquireThread();
freeAllocEntry(t, b.entry);
t.mutex.unlock();
b.check(s);
s.backing.rawFree(b.bytes(s), @fromBackingInt(@intCast(s.bucket_size_log2)), ra);
}
}