Browse Source

fix wrong out of memory in edge cases, just try allocate from block for one source of truth

Laytan Laats 10 tháng trước cách đây
mục cha
commit
7df5be2131

+ 7 - 6
base/runtime/default_temp_allocator_arena.odin

@@ -104,13 +104,15 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
 	if size == 0 {
 		return
 	}
-	
-	needed := align_forward_uint(size, alignment)
-	if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.capacity {
+
+	prev_used := 0 if arena.curr_block == nil else arena.curr_block.used
+	data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
+	if err == .Out_Of_Memory {
 		if arena.minimum_block_size == 0 {
 			arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
 		}
 
+		needed := align_forward_uint(size, alignment)
 		block_size := max(needed, arena.minimum_block_size)
 
 		if arena.backing_allocator.procedure == nil {
@@ -121,10 +123,9 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
 		new_block.prev = arena.curr_block
 		arena.curr_block = new_block
 		arena.total_capacity += new_block.capacity
+		prev_used = 0
+		data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
 	}
-
-	prev_used := arena.curr_block.used
-	data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
 	arena.total_used += arena.curr_block.used - prev_used
 	return
 }

+ 7 - 5
core/mem/virtual/arena.odin

@@ -107,8 +107,9 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
 
 	switch arena.kind {
 	case .Growing:
-		needed := mem.align_forward_uint(size, alignment)
-		if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.reserved {
+		prev_used := 0 if arena.curr_block == nil else arena.curr_block.used
+		data, err = alloc_from_memory_block(arena.curr_block, size, alignment, default_commit_size=arena.default_commit_size)
+		if err == .Out_Of_Memory {
 			if arena.minimum_block_size == 0 {
 				arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
 				arena.minimum_block_size = mem.align_forward_uint(arena.minimum_block_size, DEFAULT_PAGE_SIZE)
@@ -124,6 +125,7 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
 					max(arena.default_commit_size, arena.minimum_block_size)
 			}
 
+			needed := mem.align_forward_uint(size, alignment)
 			needed = max(needed, arena.default_commit_size)
 			block_size := max(needed, arena.minimum_block_size)
 
@@ -131,10 +133,10 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
 			new_block.prev = arena.curr_block
 			arena.curr_block = new_block
 			arena.total_reserved += new_block.reserved
-		}
 
-		prev_used := arena.curr_block.used
-		data, err = alloc_from_memory_block(arena.curr_block, size, alignment, default_commit_size=arena.default_commit_size)
+			prev_used = 0
+			data, err = alloc_from_memory_block(arena.curr_block, size, alignment, default_commit_size=arena.default_commit_size)
+		}
 		arena.total_used += arena.curr_block.used - prev_used
 	case .Static:
 		if arena.curr_block == nil {

+ 15 - 1
tests/core/mem/test_core_mem.odin

@@ -1,6 +1,7 @@
 package test_core_mem
 
 import "core:mem/tlsf"
+import "core:mem/virtual"
 import "core:testing"
 
 @test
@@ -38,4 +39,17 @@ test_tlsf_bitscan :: proc(t: ^testing.T) {
 			testing.expectf(t, res == test.exp, "Expected tlsf.fls_uint(0x%16x) == %v, got %v", test.v, test.exp, res)
 		}
 	}
-}
+}
+
+@(test)
+test_align_bumping_block_limit :: proc(t: ^testing.T) {
+	a: virtual.Arena
+
+	data, err := virtual.arena_alloc(&a, 4193371, 1)
+	testing.expect_value(t, err, nil)
+	testing.expect(t, len(data) == 4193371)
+
+	data, err = virtual.arena_alloc(&a, 896, 64)
+	testing.expect_value(t, err, nil)
+	testing.expect(t, len(data) == 896)
+}

+ 14 - 0
tests/core/runtime/test_core_runtime.odin

@@ -31,6 +31,20 @@ test_temp_allocator_big_alloc_and_alignment :: proc(t: ^testing.T) {
 	testing.expect(t, err == nil)
 }
 
+@(test)
+test_align_bumping_block_limit :: proc(t: ^testing.T) {
+	a: runtime.Arena
+	a.minimum_block_size = 8*mem.Megabyte
+
+	data, err := runtime.arena_alloc(&a, 4193371, 1)
+	testing.expect_value(t, err, nil)
+	testing.expect(t, len(data) == 4193371)
+
+	data, err = runtime.arena_alloc(&a, 896, 64)
+	testing.expect_value(t, err, nil)
+	testing.expect(t, len(data) == 896)
+}
+
 @(test)
 test_temp_allocator_returns_correct_size :: proc(t: ^testing.T) {
 	arena: runtime.Arena