From 4a08f5c48a66d51d97ebe60efac5404e7629b7c6 Mon Sep 17 00:00:00 2001 From: Samuel Elgozi Date: Mon, 17 Aug 2026 00:21:50 +0300 Subject: [PATCH] mem: Preserve alignment when reusing Dynamic_Arena blocks --- core/mem/allocators.odin | 6 +-- tests/core/mem/test_mem_dynamic_arena.odin | 53 ++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index dcd126230..92e95ea2c 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1813,7 +1813,7 @@ dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, align } memory := align_forward(a.current_pos, uintptr(actual_alignment)) margin := int(uintptr(memory) - uintptr(a.current_pos)) - if a.bytes_left < margin + n { + for a.bytes_left < margin + n { err := _dynamic_arena_cycle_new_block(a, alignment, loc) if err != nil { return nil, err @@ -1821,8 +1821,8 @@ dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, align if a.current_block == nil { return nil, .Out_Of_Memory } - margin = 0 - memory = a.current_pos + memory = align_forward(a.current_pos, uintptr(actual_alignment)) + margin = int(uintptr(memory) - uintptr(a.current_pos)) } a.current_pos = ([^]byte)(memory)[n:] a.bytes_left -= margin + n diff --git a/tests/core/mem/test_mem_dynamic_arena.odin b/tests/core/mem/test_mem_dynamic_arena.odin index 7d2b32f1a..81059cb5e 100644 --- a/tests/core/mem/test_mem_dynamic_arena.odin +++ b/tests/core/mem/test_mem_dynamic_arena.odin @@ -74,6 +74,59 @@ test_dynamic_arena_alloc_unaligned :: proc(t: ^testing.T) { expect_arena_allocation(t, expected_used_bytes = 16, num_bytes = 9, alignment = 8) } +expect_reused_block_alignment :: proc( + t: ^testing.T, + backing: []byte, + block_size: int, + should_reuse: bool, +) { + block_arena: mem.Arena + mem.arena_init(&block_arena, backing) + + arena: mem.Dynamic_Arena + mem.dynamic_arena_init( + &arena, + block_allocator = mem.arena_allocator(&block_arena), + block_size = block_size, + out_band_size = block_size + 1, + ) + defer mem.dynamic_arena_destroy(&arena) + + allocator := mem.dynamic_arena_allocator(&arena) + first, first_err := mem.alloc(1, 16, allocator) + testing.expect_value(t, first_err, mem.Allocator_Error.None) + testing.expect(t, uintptr(first) % 64 == 16) + + mem.dynamic_arena_reset(&arena) + + aligned, aligned_err := mem.alloc(1, 64, allocator) + testing.expect_value(t, aligned_err, mem.Allocator_Error.None) + testing.expect(t, uintptr(aligned) % 64 == 0) + testing.expect_value(t, arena.current_block == first, should_reuse) +} + +@(test) +test_dynamic_arena_realigns_reused_block :: proc(t: ^testing.T) { + Backing :: struct #align(64) { + _: [16]byte, + data: [128]byte, + } + + backing: Backing + expect_reused_block_alignment(t, backing.data[:], 128, true) +} + +@(test) +test_dynamic_arena_skips_incompatible_reused_block :: proc(t: ^testing.T) { + Backing :: struct #align(64) { + _: [16]byte, + data: [176]byte, + } + + backing: Backing + expect_reused_block_alignment(t, backing.data[:], 64, false) +} + @(test) test_dynamic_arena_alloc_out_of_band :: proc(t: ^testing.T) { expect_arena_allocation_out_of_band(t, num_bytes = 128, block_size = 512, out_band_size = 128)