const Bucket = struct entry: *Allocs.Entryalloc_count: AllocCountAccesed atomically with .acquire / .release ordering to provide memory ordering for allocation footers, expect for the modify field. This needs .acquire fenced every time footer data is updated (AllocCount.fenceAcqRel).
fill: FillAccesed atomically with .monotonic ordering. Alternatively, this is also synchronized by alloc_count.
_: void = {}So that @sizeOf(Bucket) is the start of first allocation if it is 8-byte aligned or less.
fn of(s: *SafeAllocator, ptr: [*]u8) *Bucketfn fillAt(s: *SafeAllocator, ptr: [*]const u8) u32fn bytes(b: *Bucket, s: *SafeAllocator) []u8fn lastAlloc(b: *Bucket, s: *SafeAllocator, fill: Fill) ?*AllocFooterfn allocFooterBefore(b: *Bucket, s: *SafeAllocator, at: u32, is_extended: bool) ?*AllocFooterfn check(b: *Bucket, s: *SafeAllocator) voidChecks that no writes after frees were performed.
Assumes b.alloc_count has been loaded with .acquire ordering.
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260const Bucket = struct {
entry: *Allocs.Entry,
/// Accesed atomically with `.acquire` / `.release` ordering to
/// provide memory ordering for allocation footers, **expect for
/// the `modify` field**. This needs `.acquire` fenced every time
/// footer data is updated (`AllocCount.fenceAcqRel`).
alloc_count: AllocCount,
/// Accesed atomically with `.monotonic` ordering. Alternatively,
/// this is also synchronized by `alloc_count`.
fill: Fill,
/// So that `@sizeOf(Bucket)` is the start of first allocation if it is 8-byte aligned or less.
_: void align(8) = {},
comptime {
assert(@alignOf(@This()) >= 8);
}
const AllocCount = packed struct(u32) {
n: u31,
/// If `true`, this bucket cannot be freed yet.
filling: bool,
fn fenceAcqRel(a: *AllocCount) void {
_ = @atomicRmw(AllocCount, a, .Or, .{ .n = 0, .filling = false }, .acq_rel);
}
};
const Fill = packed struct(u32) {
at: u31,
last_is_extended: bool,
};
fn of(s: *SafeAllocator, ptr: [*]u8) *Bucket {
const size_log2 = s.bucket_size_log2;
return @ptrFromInt(@intFromPtr(ptr) >> @intCast(size_log2) << @intCast(size_log2));
}
fn fillAt(s: *SafeAllocator, ptr: [*]const u8) u32 {
return @intCast(@intFromPtr(ptr) & s.bucketMask());
}
fn bytes(b: *Bucket, s: *SafeAllocator) []u8 {
assert(@intFromPtr(b) & s.bucketMask() == 0);
return @as([*]u8, @ptrCast(b))[0..s.bucketSize()];
}
fn lastAlloc(b: *Bucket, s: *SafeAllocator, fill: Fill) ?*AllocFooter {
return b.allocFooterBefore(s, fill.at, fill.last_is_extended);
}
fn allocFooterBefore(b: *Bucket, s: *SafeAllocator, at: u32, is_extended: bool) ?*AllocFooter {
if (at - @sizeOf(Bucket) == 0) return null;
const off = at - AllocFooter.lenBucket(s, is_extended);
assert(off >= @sizeOf(Bucket));
return @ptrCast(@alignCast(b.bytes(s)[off..]));
}
/// Checks that no writes after frees were performed.
///
/// Assumes `b.alloc_count` has been loaded with `.acquire` ordering.
fn check(b: *Bucket, s: *SafeAllocator) void {
var footer = b.lastAlloc(s, b.fill).?;
while (true) {
const modify = @atomicLoad(
AllocFooter.Modify,
&footer.modify,
// The only possible value should be `.freed` since `b.alloc_count`
// has been loaded with `.acquire`. However, another thread may be trying to
// modify the allocation after it is freed and so the other thread is going
// to panic even if this thread still sees `.freed`.
.unordered,
).storedXor(&footer.modify);
if (modify != .freed or footer.actualChecksum(s) != footer.checksum ^ s.canary) {
panic("corrupted footer metadata in bucket at *{x}", .{@intFromPtr(&footer)});
}
s.checkFreed(footer);
footer = footer.bucketPrev(b, s) orelse break;
}
}
}
This struct contains the header for a bucket. It is always part of a larger allocations of length and alignment
bucketSize().All allocations inside buckets have a minimum of 8-byte alignment (including length) so that allocations with 8-byte alignment or less do not need to store the location of the previous footer since it is directly before it. This property is used by non- extended footers to omit the offset of the previous footer.