| ... | ... | @@ -155,6 +155,8 @@ pub const Config = struct { |
| 155 | 155 | verbose_log: bool = false, |
| 156 | 156 | }; |
| 157 | 157 | |
| 158 | pub const Check = enum { ok, leak }; |
| 159 | |
| 158 | 160 | pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 159 | 161 | return struct { |
| 160 | 162 | backing_allocator: Allocator = std.heap.page_allocator, |
| ... | ... | @@ -431,7 +433,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 431 | 433 | } else struct {}; |
| 432 | 434 | |
| 433 | 435 | /// Returns true if there were leaks; false otherwise. |
| 434 | | pub fn deinit(self: *Self) bool { |
| 436 | pub fn deinit(self: *Self) Check { |
| 435 | 437 | const leaks = if (config.safety) self.detectLeaks() else false; |
| 436 | 438 | if (config.retain_metadata) { |
| 437 | 439 | self.freeRetainedMetadata(); |
| ... | ... | @@ -441,7 +443,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 441 | 443 | self.small_allocations.deinit(self.backing_allocator); |
| 442 | 444 | } |
| 443 | 445 | self.* = undefined; |
| 444 | | return leaks; |
| 446 | return @intToEnum(Check, @boolToInt(leaks)); |
| 445 | 447 | } |
| 446 | 448 | |
| 447 | 449 | fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void { |
| ... | ... | @@ -1024,7 +1026,7 @@ const test_config = Config{}; |
| 1024 | 1026 | |
| 1025 | 1027 | test "small allocations - free in same order" { |
| 1026 | 1028 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1027 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1029 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1028 | 1030 | const allocator = gpa.allocator(); |
| 1029 | 1031 | |
| 1030 | 1032 | var list = std.ArrayList(*u64).init(std.testing.allocator); |
| ... | ... | @@ -1043,7 +1045,7 @@ test "small allocations - free in same order" { |
| 1043 | 1045 | |
| 1044 | 1046 | test "small allocations - free in reverse order" { |
| 1045 | 1047 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1046 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1048 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1047 | 1049 | const allocator = gpa.allocator(); |
| 1048 | 1050 | |
| 1049 | 1051 | var list = std.ArrayList(*u64).init(std.testing.allocator); |
| ... | ... | @@ -1062,7 +1064,7 @@ test "small allocations - free in reverse order" { |
| 1062 | 1064 | |
| 1063 | 1065 | test "large allocations" { |
| 1064 | 1066 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1065 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1067 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1066 | 1068 | const allocator = gpa.allocator(); |
| 1067 | 1069 | |
| 1068 | 1070 | const ptr1 = try allocator.alloc(u64, 42768); |
| ... | ... | @@ -1075,7 +1077,7 @@ test "large allocations" { |
| 1075 | 1077 | |
| 1076 | 1078 | test "very large allocation" { |
| 1077 | 1079 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1078 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1080 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1079 | 1081 | const allocator = gpa.allocator(); |
| 1080 | 1082 | |
| 1081 | 1083 | try std.testing.expectError(error.OutOfMemory, allocator.alloc(u8, math.maxInt(usize))); |
| ... | ... | @@ -1083,7 +1085,7 @@ test "very large allocation" { |
| 1083 | 1085 | |
| 1084 | 1086 | test "realloc" { |
| 1085 | 1087 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1086 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1088 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1087 | 1089 | const allocator = gpa.allocator(); |
| 1088 | 1090 | |
| 1089 | 1091 | var slice = try allocator.alignedAlloc(u8, @alignOf(u32), 1); |
| ... | ... | @@ -1105,7 +1107,7 @@ test "realloc" { |
| 1105 | 1107 | |
| 1106 | 1108 | test "shrink" { |
| 1107 | 1109 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1108 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1110 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1109 | 1111 | const allocator = gpa.allocator(); |
| 1110 | 1112 | |
| 1111 | 1113 | var slice = try allocator.alloc(u8, 20); |
| ... | ... | @@ -1130,7 +1132,7 @@ test "shrink" { |
| 1130 | 1132 | |
| 1131 | 1133 | test "large object - grow" { |
| 1132 | 1134 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1133 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1135 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1134 | 1136 | const allocator = gpa.allocator(); |
| 1135 | 1137 | |
| 1136 | 1138 | var slice1 = try allocator.alloc(u8, page_size * 2 - 20); |
| ... | ... | @@ -1148,7 +1150,7 @@ test "large object - grow" { |
| 1148 | 1150 | |
| 1149 | 1151 | test "realloc small object to large object" { |
| 1150 | 1152 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1151 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1153 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1152 | 1154 | const allocator = gpa.allocator(); |
| 1153 | 1155 | |
| 1154 | 1156 | var slice = try allocator.alloc(u8, 70); |
| ... | ... | @@ -1165,7 +1167,7 @@ test "realloc small object to large object" { |
| 1165 | 1167 | |
| 1166 | 1168 | test "shrink large object to large object" { |
| 1167 | 1169 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1168 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1170 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1169 | 1171 | const allocator = gpa.allocator(); |
| 1170 | 1172 | |
| 1171 | 1173 | var slice = try allocator.alloc(u8, page_size * 2 + 50); |
| ... | ... | @@ -1190,7 +1192,7 @@ test "shrink large object to large object" { |
| 1190 | 1192 | |
| 1191 | 1193 | test "shrink large object to large object with larger alignment" { |
| 1192 | 1194 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1193 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1195 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1194 | 1196 | const allocator = gpa.allocator(); |
| 1195 | 1197 | |
| 1196 | 1198 | var debug_buffer: [1000]u8 = undefined; |
| ... | ... | @@ -1226,7 +1228,7 @@ test "shrink large object to large object with larger alignment" { |
| 1226 | 1228 | |
| 1227 | 1229 | test "realloc large object to small object" { |
| 1228 | 1230 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1229 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1231 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1230 | 1232 | const allocator = gpa.allocator(); |
| 1231 | 1233 | |
| 1232 | 1234 | var slice = try allocator.alloc(u8, page_size * 2 + 50); |
| ... | ... | @@ -1244,7 +1246,7 @@ test "overrideable mutexes" { |
| 1244 | 1246 | .backing_allocator = std.testing.allocator, |
| 1245 | 1247 | .mutex = std.Thread.Mutex{}, |
| 1246 | 1248 | }; |
| 1247 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1249 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1248 | 1250 | const allocator = gpa.allocator(); |
| 1249 | 1251 | |
| 1250 | 1252 | const ptr = try allocator.create(i32); |
| ... | ... | @@ -1253,7 +1255,7 @@ test "overrideable mutexes" { |
| 1253 | 1255 | |
| 1254 | 1256 | test "non-page-allocator backing allocator" { |
| 1255 | 1257 | var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = std.testing.allocator }; |
| 1256 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1258 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1257 | 1259 | const allocator = gpa.allocator(); |
| 1258 | 1260 | |
| 1259 | 1261 | const ptr = try allocator.create(i32); |
| ... | ... | @@ -1262,7 +1264,7 @@ test "non-page-allocator backing allocator" { |
| 1262 | 1264 | |
| 1263 | 1265 | test "realloc large object to larger alignment" { |
| 1264 | 1266 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1265 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1267 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1266 | 1268 | const allocator = gpa.allocator(); |
| 1267 | 1269 | |
| 1268 | 1270 | var debug_buffer: [1000]u8 = undefined; |
| ... | ... | @@ -1304,7 +1306,7 @@ test "realloc large object to larger alignment" { |
| 1304 | 1306 | test "large object shrinks to small but allocation fails during shrink" { |
| 1305 | 1307 | var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, 3); |
| 1306 | 1308 | var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = failing_allocator.allocator() }; |
| 1307 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1309 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1308 | 1310 | const allocator = gpa.allocator(); |
| 1309 | 1311 | |
| 1310 | 1312 | 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" { |
| 1322 | 1324 | |
| 1323 | 1325 | test "objects of size 1024 and 2048" { |
| 1324 | 1326 | var gpa = GeneralPurposeAllocator(test_config){}; |
| 1325 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1327 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1326 | 1328 | const allocator = gpa.allocator(); |
| 1327 | 1329 | |
| 1328 | 1330 | const slice = try allocator.alloc(u8, 1025); |
| ... | ... | @@ -1334,7 +1336,7 @@ test "objects of size 1024 and 2048" { |
| 1334 | 1336 | |
| 1335 | 1337 | test "setting a memory cap" { |
| 1336 | 1338 | var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){}; |
| 1337 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1339 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1338 | 1340 | const allocator = gpa.allocator(); |
| 1339 | 1341 | |
| 1340 | 1342 | gpa.setRequestedMemoryLimit(1010); |
| ... | ... | @@ -1361,11 +1363,11 @@ test "setting a memory cap" { |
| 1361 | 1363 | test "double frees" { |
| 1362 | 1364 | // use a GPA to back a GPA to check for leaks of the latter's metadata |
| 1363 | 1365 | var backing_gpa = GeneralPurposeAllocator(.{ .safety = true }){}; |
| 1364 | | defer std.testing.expect(!backing_gpa.deinit()) catch @panic("leak"); |
| 1366 | defer std.testing.expect(backing_gpa.deinit() == .ok) catch @panic("leak"); |
| 1365 | 1367 | |
| 1366 | 1368 | const GPA = GeneralPurposeAllocator(.{ .safety = true, .never_unmap = true, .retain_metadata = true }); |
| 1367 | 1369 | var gpa = GPA{ .backing_allocator = backing_gpa.allocator() }; |
| 1368 | | defer std.testing.expect(!gpa.deinit()) catch @panic("leak"); |
| 1370 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 1369 | 1371 | const allocator = gpa.allocator(); |
| 1370 | 1372 | |
| 1371 | 1373 | // detect a small allocation double free, even though bucket is emptied |