| author | |
| committer | |
| log | 36e9b0f0261a5421f22dbf17f513a8c2f685c5d3 |
| tree | 62e7d47f13e388f4db799b597a7617b80c2d9d48 |
| parent | 2c5113f6d1ee9d5041d9d79cfbe13e6bea2aff56 |
Reversal on the decision: the Allocator interface is the correct place
for the memset to undefined because it allows Allocator implementations
to bypass the interface and use a backing allocator directly, skipping
the performance penalty of memsetting the entire allocation, which may
be very large, as well as having valuable zeroes on them.
closes #42982 files changed, 27 insertions(+), 19 deletions(-)
lib/std/heap/general_purpose_allocator.zig+23-12| ... | ... | @@ -157,6 +157,9 @@ pub const Config = struct { |
| 157 | 157 | |
| 158 | 158 | /// Enables emitting info messages with the size and address of every allocation. |
| 159 | 159 | verbose_log: bool = false, |
| 160 | ||
| 161 | /// Tell whether the backing allocator returns already-zeroed memory. | |
| 162 | backing_allocator_zeroes: bool = true, | |
| 160 | 163 | }; |
| 161 | 164 | |
| 162 | 165 | pub const Check = enum { ok, leak }; |
| ... | ... | @@ -179,7 +182,8 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 179 | 182 | |
| 180 | 183 | const Self = @This(); |
| 181 | 184 | |
| 182 | /// The initial state of a `GeneralPurposeAllocator`, containing no allocations and backed by the system page allocator. | |
| 185 | /// The initial state of a `GeneralPurposeAllocator`, containing no | |
| 186 | /// allocations and backed by the system page allocator. | |
| 183 | 187 | pub const init: Self = .{ |
| 184 | 188 | .backing_allocator = std.heap.page_allocator, |
| 185 | 189 | .buckets = [1]Buckets{.{}} ** small_bucket_count, |
| ... | ... | @@ -508,7 +512,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 508 | 512 | fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void { |
| 509 | 513 | if (stack_n == 0) return; |
| 510 | 514 | @memset(addresses, 0); |
| 511 | var stack_trace = StackTrace{ | |
| 515 | var stack_trace: StackTrace = .{ | |
| 512 | 516 | .instruction_addresses = addresses, |
| 513 | 517 | .index = 0, |
| 514 | 518 | }; |
| ... | ... | @@ -1092,22 +1096,29 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 1092 | 1096 | } |
| 1093 | 1097 | |
| 1094 | 1098 | fn createBucket(self: *Self, size_class: usize) Error!*BucketHeader { |
| 1095 | const page = try self.backing_allocator.alignedAlloc(u8, page_size, page_size); | |
| 1096 | errdefer self.backing_allocator.free(page); | |
| 1099 | const alignment: mem.Alignment = .fromByteUnits(page_size); | |
| 1100 | const page = self.backing_allocator.rawAlloc(page_size, alignment, @returnAddress()) orelse | |
| 1101 | return error.OutOfMemory; | |
| 1102 | errdefer self.backing_allocator.rawFree(page[0..page_size], alignment, @returnAddress()); | |
| 1097 | 1103 | |
| 1098 | 1104 | const bucket_size = bucketSize(size_class); |
| 1099 | const bucket_bytes = try self.backing_allocator.alignedAlloc(u8, @alignOf(BucketHeader), bucket_size); | |
| 1100 | const ptr: *BucketHeader = @ptrCast(bucket_bytes.ptr); | |
| 1105 | const header_align: mem.Alignment = .fromByteUnits(@alignOf(BucketHeader)); | |
| 1106 | const ptr: *BucketHeader = @alignCast(@ptrCast(self.backing_allocator.rawAlloc( | |
| 1107 | bucket_size, | |
| 1108 | header_align, | |
| 1109 | @returnAddress(), | |
| 1110 | ) orelse return error.OutOfMemory)); | |
| 1101 | 1111 | ptr.* = .{ |
| 1102 | .page = page.ptr, | |
| 1112 | .page = @alignCast(page), | |
| 1103 | 1113 | .alloc_cursor = 0, |
| 1104 | 1114 | .used_count = 0, |
| 1105 | 1115 | }; |
| 1106 | // Set the used bits to all zeroes | |
| 1107 | @memset(@as([*]u8, @as(*[1]u8, ptr.usedBits(0)))[0..usedBitsCount(size_class)], 0); | |
| 1108 | if (config.safety) { | |
| 1109 | // Set the requested sizes to zeroes | |
| 1110 | @memset(mem.sliceAsBytes(ptr.requestedSizes(size_class)), 0); | |
| 1116 | if (!config.backing_allocator_zeroes) { | |
| 1117 | @memset(@as([*]u8, @as(*[1]u8, ptr.usedBits(0)))[0..usedBitsCount(size_class)], 0); | |
| 1118 | if (config.safety) { | |
| 1119 | // Set the requested sizes to zeroes | |
| 1120 | @memset(mem.sliceAsBytes(ptr.requestedSizes(size_class)), 0); | |
| 1121 | } | |
| 1111 | 1122 | } |
| 1112 | 1123 | return ptr; |
| 1113 | 1124 | } |
lib/std/mem/Allocator.zig+4-7| ... | ... | @@ -128,25 +128,25 @@ pub fn noFree( |
| 128 | 128 | } |
| 129 | 129 | |
| 130 | 130 | /// This function is not intended to be called except from within the |
| 131 | /// implementation of an Allocator | |
| 131 | /// implementation of an `Allocator`. | |
| 132 | 132 | pub inline fn rawAlloc(a: Allocator, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 { |
| 133 | 133 | return a.vtable.alloc(a.ptr, len, alignment, ret_addr); |
| 134 | 134 | } |
| 135 | 135 | |
| 136 | 136 | /// This function is not intended to be called except from within the |
| 137 | /// implementation of an Allocator. | |
| 137 | /// implementation of an `Allocator`. | |
| 138 | 138 | pub inline fn rawResize(a: Allocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool { |
| 139 | 139 | return a.vtable.resize(a.ptr, memory, alignment, new_len, ret_addr); |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | 142 | /// This function is not intended to be called except from within the |
| 143 | /// implementation of an Allocator. | |
| 143 | /// implementation of an `Allocator`. | |
| 144 | 144 | pub inline fn rawRemap(a: Allocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { |
| 145 | 145 | return a.vtable.remap(a.ptr, memory, alignment, new_len, ret_addr); |
| 146 | 146 | } |
| 147 | 147 | |
| 148 | 148 | /// This function is not intended to be called except from within the |
| 149 | /// implementation of an Allocator | |
| 149 | /// implementation of an `Allocator`. | |
| 150 | 150 | pub inline fn rawFree(a: Allocator, memory: []u8, alignment: Alignment, ret_addr: usize) void { |
| 151 | 151 | return a.vtable.free(a.ptr, memory, alignment, ret_addr); |
| 152 | 152 | } |
| ... | ... | @@ -271,7 +271,6 @@ fn allocBytesWithAlignment(self: Allocator, comptime alignment: u29, byte_count: |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | 273 | const byte_ptr = self.rawAlloc(byte_count, .fromByteUnits(alignment), return_address) orelse return Error.OutOfMemory; |
| 274 | // TODO: https://github.com/ziglang/zig/issues/4298 | |
| 275 | 274 | @memset(byte_ptr[0..byte_count], undefined); |
| 276 | 275 | return @alignCast(byte_ptr); |
| 277 | 276 | } |
| ... | ... | @@ -391,7 +390,6 @@ pub fn reallocAdvanced( |
| 391 | 390 | return error.OutOfMemory; |
| 392 | 391 | const copy_len = @min(byte_count, old_byte_slice.len); |
| 393 | 392 | @memcpy(new_mem[0..copy_len], old_byte_slice[0..copy_len]); |
| 394 | // TODO https://github.com/ziglang/zig/issues/4298 | |
| 395 | 393 | @memset(old_byte_slice, undefined); |
| 396 | 394 | self.rawFree(old_byte_slice, .fromByteUnits(Slice.alignment), return_address); |
| 397 | 395 | |
| ... | ... | @@ -408,7 +406,6 @@ pub fn free(self: Allocator, memory: anytype) void { |
| 408 | 406 | const bytes_len = bytes.len + if (Slice.sentinel() != null) @sizeOf(Slice.child) else 0; |
| 409 | 407 | if (bytes_len == 0) return; |
| 410 | 408 | const non_const_ptr = @constCast(bytes.ptr); |
| 411 | // TODO: https://github.com/ziglang/zig/issues/4298 | |
| 412 | 409 | @memset(non_const_ptr[0..bytes_len], undefined); |
| 413 | 410 | self.rawFree(non_const_ptr[0..bytes_len], .fromByteUnits(Slice.alignment), @returnAddress()); |
| 414 | 411 | } |