diff options
| author | Taylan Kammer <taylan.kammer@gmail.com> | 2026-08-16 17:34:32 +0200 |
|---|---|---|
| committer | Taylan Kammer <taylan.kammer@gmail.com> | 2026-08-16 17:34:32 +0200 |
| commit | 9f709f293eabbb5ab22768cf429535dd39784562 (patch) | |
| tree | c6e3933162b6bf14c89151677db2bafc380caf69 /src | |
| parent | e872d4d20ad3ba94f2a63122a361bbc877945cbd (diff) | |
Pair of fixes in meta alloc.
Diffstat (limited to 'src')
| -rw-r--r-- | src/zisp/gc/meta_alloc.zig | 33 |
1 files changed, 22 insertions, 11 deletions
diff --git a/src/zisp/gc/meta_alloc.zig b/src/zisp/gc/meta_alloc.zig index da9074a..d3b6be1 100644 --- a/src/zisp/gc/meta_alloc.zig +++ b/src/zisp/gc/meta_alloc.zig @@ -210,13 +210,12 @@ fn alloc_size_class(sci: u8, size: u32) []u8 { return ptr[0..size]; } - const info = &slab_infos[sci]; - const res_n = reserveCountForSizeClassIndex(sci); - // Try to transfer a chunk from the global free-list. + const info = &slab_infos[sci]; if (alloc_from_fl(slab, info, tl)) |p| return p[0..size]; - return alloc_fresh(slab, size, res_n * size, info, tl); + const res_n = reserveCountForSizeClassIndex(sci); + return alloc_fresh(slab, size, info, tl, res_n); } fn alloc_from_fl( @@ -230,7 +229,9 @@ fn alloc_from_fl( const ptr = slab + fl_head.idx * 8; const arr: [*]u32 = @ptrCast(@alignCast(ptr)); + // Load this atomically since it's still globally accessible memory. const next_head = @atomicLoad(u32, &arr[0], .unordered); + if (@cmpxchgWeak( FlHead, &info.free_list, @@ -244,7 +245,9 @@ fn alloc_from_fl( continue; } + // Now detached from global; no need for atomics anymore. tl.fc_count = arr[1]; + // We can safely copy more than needed since we set fc_count; this way // it should compile neatly into SIMD instructions. for (0..RESERVE_MAX / 2) |i| tl.fc[i] = arr[4 + i]; @@ -257,12 +260,13 @@ fn alloc_from_fl( fn alloc_fresh( slab: [*]u8, size: u32, - bump: u32, info: *SlabInfo, tl: *TlSlabInfo, + res_n: u32, ) []u8 { // Have to use fresh memory from the top of the slab; bump it by a chunk, // atomically, to reserve memory for this thread. + const bump = res_n * size / 8; const old_wm = @atomicRmw(u32, &info.watermark, .Add, bump, .monotonic); const new_wm = old_wm + bump; @@ -273,14 +277,14 @@ fn alloc_fresh( } const ptr = slab + old_wm * 8; - tl.wm_lo = old_wm + size; + tl.wm_lo = old_wm + size / 8; tl.wm_hi = new_wm; return ptr[0..size]; } /// Free memory that was returned by alloc(). pub fn free(slot: []u8) void { - // Can only happen if the slice doesn't originate from our alloc(). + std.debug.assert(@popCount(slot.len) == 1); std.debug.assert(slot.len >= SIZES[0]); const sci = getSizeClassIndex(slot.len); @@ -301,12 +305,14 @@ fn free_size_class(sci: u8, slot: []u8) void { const res_n = reserveCountForSizeClassIndex(sci); + // Still some space in the local free slot cache. if (tl.fc_count != res_n) { tl.fc[tl.fc_count] = idx; tl.fc_count += 1; return; } + // Local cache full; need to flush some to global free-list. const info = &slab_infos[sci]; free_into_fl(info, tl, slot, idx, res_n / 2); } @@ -316,18 +322,23 @@ fn free_into_fl( tl: *TlSlabInfo, slot: []u8, idx: u32, - n: u32, + num: u32, ) void { const arr: [*]u32 = @ptrCast(@alignCast(slot.ptr)); - tl.fc_count = n; + // Intuitively, this belongs to the end, but it doesn't matter; the rest of + // this function is not allowed to fail anyway. + tl.fc_count = num; - arr[1] = n; + // No need for atomic stores; we own this memory exclusively right now, + // unless another thread calls free() on the same slot which is a bug. + arr[1] = num; // Safe to copy more than needed; should compile into SIMD. - for (0..RESERVE_MAX / 2) |i| arr[4 + i] = tl.fc[n + i]; + for (0..RESERVE_MAX / 2) |i| arr[4 + i] = tl.fc[num + i]; var fl_head = @atomicLoad(FlHead, &info.free_list, .acquire); while (true) { + // Still owned exclusively; no need for atomic store. arr[0] = fl_head.idx; if (@cmpxchgWeak( FlHead, |
