Returns the true count of leaks
fn deinitLeakedBucket(s: *SafeAllocator, b: *Bucket, log: bool) usize
fn deinitLeakedBucket(s: *SafeAllocator, b: *Bucket, log: bool) usize {
var leaks: usize = 0;
const expected = @atomicLoad(Bucket.AllocCount, &b.alloc_count, .acquire);
if (expected.n == 0) assert(expected.filling);
var footer = b.lastAlloc(s, b.fill).?;
while (true) {
const modify = @atomicRmw(
AllocFooter.Modify,
&footer.modify,
.Xchg,
undefined,
.monotonic,
).storedXor(&footer.modify);
const bad_modify = modify != .none and modify != .freed;
if (bad_modify or footer.actualChecksum(s) != footer.checksum ^ s.canary) {
panic("corrupted footer metadata in bucket at *{x}", .{@intFromPtr(&footer)});
}
switch (modify) {
.none => {
leaks += 1;
if (log) scoped_log.err("leaked {f} allocated at: {f}", .{ FormatMemory{
.memory = footer.userMemory(),
.alignment = footer.userAlign(),
}, formatStackTrace(footer.allocTrace(s)) });
},
.freed => s.checkFreed(footer),
else => unreachable,
}
footer = footer.bucketPrev(b, s) orelse break;
}
s.backing.rawFree(b.bytes(s), @fromBackingInt(@intCast(s.bucket_size_log2)), 0);
assert(leaks == expected.n);
return leaks;
}