std: GPA deinit return an enum instead of a bool

This commit is contained in:
Borja Clemente 2023-04-22 13:09:44 +02:00 committed by GitHub
parent 3f259d3550
commit bd801dc489
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 28 additions and 25 deletions

View File

@ -16,6 +16,7 @@ pub const LogToWriterAllocator = @import("heap/log_to_writer_allocator.zig").Log
pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator; pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator;
pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator; pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator; pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
pub const Check = @import("heap/general_purpose_allocator.zig").Check;
pub const WasmAllocator = @import("heap/WasmAllocator.zig"); pub const WasmAllocator = @import("heap/WasmAllocator.zig");
pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig"); pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig");
pub const PageAllocator = @import("heap/PageAllocator.zig"); pub const PageAllocator = @import("heap/PageAllocator.zig");

View File

@ -155,6 +155,8 @@ pub const Config = struct {
verbose_log: bool = false, verbose_log: bool = false,
}; };
pub const Check = enum { ok, leak };
pub fn GeneralPurposeAllocator(comptime config: Config) type { pub fn GeneralPurposeAllocator(comptime config: Config) type {
return struct { return struct {
backing_allocator: Allocator = std.heap.page_allocator, backing_allocator: Allocator = std.heap.page_allocator,
@ -431,7 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
} else struct {}; } else struct {};
/// Returns true if there were leaks; false otherwise. /// Returns true if there were leaks; false otherwise.
pub fn deinit(self: *Self) bool { pub fn deinit(self: *Self) Check {
const leaks = if (config.safety) self.detectLeaks() else false; const leaks = if (config.safety) self.detectLeaks() else false;
if (config.retain_metadata) { if (config.retain_metadata) {
self.freeRetainedMetadata(); self.freeRetainedMetadata();
@ -441,7 +443,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
self.small_allocations.deinit(self.backing_allocator); self.small_allocations.deinit(self.backing_allocator);
} }
self.* = undefined; self.* = undefined;
return leaks; return @intToEnum(Check, @boolToInt(leaks));
} }
fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void { fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void {
@ -1024,7 +1026,7 @@ const test_config = Config{};
test "small allocations - free in same order" { test "small allocations - free in same order" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var list = std.ArrayList(*u64).init(std.testing.allocator); var list = std.ArrayList(*u64).init(std.testing.allocator);
@ -1043,7 +1045,7 @@ test "small allocations - free in same order" {
test "small allocations - free in reverse order" { test "small allocations - free in reverse order" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var list = std.ArrayList(*u64).init(std.testing.allocator); var list = std.ArrayList(*u64).init(std.testing.allocator);
@ -1062,7 +1064,7 @@ test "small allocations - free in reverse order" {
test "large allocations" { test "large allocations" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const ptr1 = try allocator.alloc(u64, 42768); const ptr1 = try allocator.alloc(u64, 42768);
@ -1075,7 +1077,7 @@ test "large allocations" {
test "very large allocation" { test "very large allocation" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
try std.testing.expectError(error.OutOfMemory, allocator.alloc(u8, math.maxInt(usize))); try std.testing.expectError(error.OutOfMemory, allocator.alloc(u8, math.maxInt(usize)));
@ -1083,7 +1085,7 @@ test "very large allocation" {
test "realloc" { test "realloc" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var slice = try allocator.alignedAlloc(u8, @alignOf(u32), 1); var slice = try allocator.alignedAlloc(u8, @alignOf(u32), 1);
@ -1105,7 +1107,7 @@ test "realloc" {
test "shrink" { test "shrink" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var slice = try allocator.alloc(u8, 20); var slice = try allocator.alloc(u8, 20);
@ -1130,7 +1132,7 @@ test "shrink" {
test "large object - grow" { test "large object - grow" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var slice1 = try allocator.alloc(u8, page_size * 2 - 20); var slice1 = try allocator.alloc(u8, page_size * 2 - 20);
@ -1148,7 +1150,7 @@ test "large object - grow" {
test "realloc small object to large object" { test "realloc small object to large object" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var slice = try allocator.alloc(u8, 70); var slice = try allocator.alloc(u8, 70);
@ -1165,7 +1167,7 @@ test "realloc small object to large object" {
test "shrink large object to large object" { test "shrink large object to large object" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var slice = try allocator.alloc(u8, page_size * 2 + 50); var slice = try allocator.alloc(u8, page_size * 2 + 50);
@ -1190,7 +1192,7 @@ test "shrink large object to large object" {
test "shrink large object to large object with larger alignment" { test "shrink large object to large object with larger alignment" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var debug_buffer: [1000]u8 = undefined; var debug_buffer: [1000]u8 = undefined;
@ -1226,7 +1228,7 @@ test "shrink large object to large object with larger alignment" {
test "realloc large object to small object" { test "realloc large object to small object" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var slice = try allocator.alloc(u8, page_size * 2 + 50); var slice = try allocator.alloc(u8, page_size * 2 + 50);
@ -1244,7 +1246,7 @@ test "overrideable mutexes" {
.backing_allocator = std.testing.allocator, .backing_allocator = std.testing.allocator,
.mutex = std.Thread.Mutex{}, .mutex = std.Thread.Mutex{},
}; };
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const ptr = try allocator.create(i32); const ptr = try allocator.create(i32);
@ -1253,7 +1255,7 @@ test "overrideable mutexes" {
test "non-page-allocator backing allocator" { test "non-page-allocator backing allocator" {
var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = std.testing.allocator }; var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = std.testing.allocator };
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const ptr = try allocator.create(i32); const ptr = try allocator.create(i32);
@ -1262,7 +1264,7 @@ test "non-page-allocator backing allocator" {
test "realloc large object to larger alignment" { test "realloc large object to larger alignment" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var debug_buffer: [1000]u8 = undefined; var debug_buffer: [1000]u8 = undefined;
@ -1304,7 +1306,7 @@ test "realloc large object to larger alignment" {
test "large object shrinks to small but allocation fails during shrink" { test "large object shrinks to small but allocation fails during shrink" {
var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, 3); var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, 3);
var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = failing_allocator.allocator() }; var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = failing_allocator.allocator() };
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var slice = try allocator.alloc(u8, page_size * 2 + 50); var slice = try allocator.alloc(u8, page_size * 2 + 50);
@ -1322,7 +1324,7 @@ test "large object shrinks to small but allocation fails during shrink" {
test "objects of size 1024 and 2048" { test "objects of size 1024 and 2048" {
var gpa = GeneralPurposeAllocator(test_config){}; var gpa = GeneralPurposeAllocator(test_config){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const slice = try allocator.alloc(u8, 1025); const slice = try allocator.alloc(u8, 1025);
@ -1334,7 +1336,7 @@ test "objects of size 1024 and 2048" {
test "setting a memory cap" { test "setting a memory cap" {
var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){}; var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){};
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
gpa.setRequestedMemoryLimit(1010); gpa.setRequestedMemoryLimit(1010);
@ -1361,11 +1363,11 @@ test "setting a memory cap" {
test "double frees" { test "double frees" {
// use a GPA to back a GPA to check for leaks of the latter's metadata // use a GPA to back a GPA to check for leaks of the latter's metadata
var backing_gpa = GeneralPurposeAllocator(.{ .safety = true }){}; var backing_gpa = GeneralPurposeAllocator(.{ .safety = true }){};
defer std.testing.expect(!backing_gpa.deinit()) catch @panic("leak"); defer std.testing.expect(backing_gpa.deinit() == .ok) catch @panic("leak");
const GPA = GeneralPurposeAllocator(.{ .safety = true, .never_unmap = true, .retain_metadata = true }); const GPA = GeneralPurposeAllocator(.{ .safety = true, .never_unmap = true, .retain_metadata = true });
var gpa = GPA{ .backing_allocator = backing_gpa.allocator() }; var gpa = GPA{ .backing_allocator = backing_gpa.allocator() };
defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
// detect a small allocation double free, even though bucket is emptied // detect a small allocation double free, even though bucket is emptied

View File

@ -56,7 +56,7 @@ fn mainServer() !void {
}, },
.query_test_metadata => { .query_test_metadata => {
std.testing.allocator_instance = .{}; std.testing.allocator_instance = .{};
defer if (std.testing.allocator_instance.deinit()) { defer if (std.testing.allocator_instance.deinit() == .leak) {
@panic("internal test runner memory leak"); @panic("internal test runner memory leak");
}; };
@ -108,7 +108,7 @@ fn mainServer() !void {
} }
}, },
}; };
leak = std.testing.allocator_instance.deinit(); leak = std.testing.allocator_instance.deinit() == .leak;
try server.serveTestResults(.{ try server.serveTestResults(.{
.index = index, .index = index,
.flags = .{ .flags = .{
@ -148,7 +148,7 @@ fn mainTerminal() void {
for (test_fn_list, 0..) |test_fn, i| { for (test_fn_list, 0..) |test_fn, i| {
std.testing.allocator_instance = .{}; std.testing.allocator_instance = .{};
defer { defer {
if (std.testing.allocator_instance.deinit()) { if (std.testing.allocator_instance.deinit() == .leak) {
leaks += 1; leaks += 1;
} }
} }

View File

@ -4,7 +4,7 @@ const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;
pub fn main() anyerror!void { pub fn main() anyerror!void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer if (gpa.deinit()) @panic("found memory leaks"); defer if (gpa.deinit() == .leak) @panic("found memory leaks");
const allocator = gpa.allocator(); const allocator = gpa.allocator();
var it = try std.process.argsWithAllocator(allocator); var it = try std.process.argsWithAllocator(allocator);