From e3ff1ce70f671c1556fc405db0b012198cb8bf20 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Wed, 20 Mar 2024 22:34:18 -0400 Subject: [PATCH] Finally fixed memory allocations for slabs/pools/virtual arenas --- code/env.odin | 22 ++++++++++++------ code/grime_arena.odin | 2 +- code/grime_array.odin | 4 ++-- code/grime_linked_list.odin | 10 ++++---- code/grime_memory.odin | 4 ++-- code/grime_pool_allocator.odin | 40 +++++++++++++++----------------- code/grime_slab_allocator.odin | 22 ++++++++++++------ code/grime_string_interning.odin | 3 +++ code/grime_virtual_arena.odin | 6 ++--- code/grime_virtual_memory.odin | 4 ++-- code/grime_windows.odin | 8 +++---- code/logger.odin | 2 +- code/ui.odin | 2 +- 13 files changed, 73 insertions(+), 56 deletions(-) diff --git a/code/env.odin b/code/env.odin index 80a01a0..d1e3d60 100644 --- a/code/env.odin +++ b/code/env.odin @@ -50,31 +50,39 @@ Memory :: struct { } persistent_allocator :: proc() -> Allocator { - return varena_allocator( Memory_App.persistent ) + result := varena_allocator( Memory_App.persistent ) + return result } frame_allocator :: proc() -> Allocator { - return varena_allocator( Memory_App.frame ) + result := varena_allocator( Memory_App.frame ) + return result } transient_allocator :: proc() -> Allocator { - return varena_allocator( Memory_App.transient ) + result := varena_allocator( Memory_App.transient ) + return result } files_buffer_allocator :: proc() -> Allocator { - return varena_allocator( Memory_App.files_buffer ) + result := varena_allocator( Memory_App.files_buffer ) + return result } persistent_slab_allocator :: proc() -> Allocator { - return slab_allocator( get_state().persistent_slab ) + state := get_state() + result := slab_allocator( state.persistent_slab ) + return result } frame_slab_allocator :: proc() -> Allocator { - return slab_allocator( get_state().frame_slab ) + result := slab_allocator( get_state().frame_slab ) + return result } transient_slab_allocator :: proc() -> Allocator { - return slab_allocator( get_state().transient_slab ) + result := slab_allocator( get_state().transient_slab ) + return result } // TODO(Ed) : Implment host memory mapping api diff --git a/code/grime_arena.odin b/code/grime_arena.odin index 7825494..6660853 100644 --- a/code/grime_arena.odin +++ b/code/grime_arena.odin @@ -7,7 +7,7 @@ package sectr import "core:mem" // Initialize a sub-section of our virtual memory as a sub-arena -sub_arena_init :: proc( address : ^ byte, size : int ) -> ( ^ Arena) { +sub_arena_init :: proc( address : ^byte, size : int ) -> ( ^ Arena) { Arena :: mem.Arena arena_size :: size_of( Arena) diff --git a/code/grime_array.odin b/code/grime_array.odin index d9233f4..a7998f4 100644 --- a/code/grime_array.odin +++ b/code/grime_array.odin @@ -45,7 +45,8 @@ array_to_slice :: proc( using self : Array($ Type) ) -> []Type { } array_grow_formula :: proc( value : u64 ) -> u64 { - return 2 * value + 8 + result := (2 * value) + 8 + return result } array_init :: proc( $ Type : typeid, allocator : Allocator ) -> ( Array(Type), AllocatorError ) { @@ -273,7 +274,6 @@ array_set_capacity :: proc( self : ^Array( $ Type ), new_capacity : u64 ) -> All new_size := header_size + (cast(int) new_capacity ) * size_of(Type) old_size := header_size + (cast(int) self.capacity) * size_of(Type) - // new_mem, result_code := resize( self.header, old_size, new_size, allocator = self.backing ) new_mem, result_code := resize_non_zeroed( self.header, old_size, new_size, mem.DEFAULT_ALIGNMENT, allocator = self.backing ) if result_code != AllocatorError.None { diff --git a/code/grime_linked_list.odin b/code/grime_linked_list.odin index 68cbf24..ef2653a 100644 --- a/code/grime_linked_list.odin +++ b/code/grime_linked_list.odin @@ -6,13 +6,13 @@ LL_Node :: struct ( $ Type : typeid ) { // ll_push :: proc( list_ptr : ^(^ ($ Type)), node : ^Type ) { ll_push :: #force_inline proc "contextless" ( list_ptr : ^(^ ($ Type)), node : ^Type ) { - list := (list_ptr^) + list : ^Type = (list_ptr^) node.next = list (list_ptr^) = node } ll_pop :: #force_inline proc "contextless" ( list_ptr : ^(^ ($ Type)) ) -> ( node : ^Type ) { - list := (list_ptr^) + list : ^Type = (list_ptr^) (list_ptr^) = list.next return list } @@ -37,7 +37,7 @@ DLL_Node :: struct ( $ Type : typeid ) #raw_union { DLL_NodeFull :: struct ( $ Type : typeid ) { // using _ : DLL_NodeFL(Type), first, last : ^Type, - prev, next : ^Type, + prev, next : ^Type, } DLL_NodePN :: struct ( $ Type : typeid ) { @@ -80,7 +80,7 @@ dll_fl_append :: proc ( list : ^( $TypeList), node : ^( $TypeNode) ) dll_push_back :: proc "contextless" ( current_ptr : ^(^ ($ TypeCurr)), node : ^$TypeNode ) { - current := (current_ptr ^) + current : ^TypeCurr = (current_ptr ^) if current == nil { @@ -99,7 +99,7 @@ dll_push_back :: proc "contextless" ( current_ptr : ^(^ ($ TypeCurr)), node : ^$ dll_pop_back :: #force_inline proc "contextless" ( current_ptr : ^(^ ($ Type)) ) { - to_remove := (current_ptr ^) + to_remove : ^Type = (current_ptr ^) if to_remove == nil { return } diff --git a/code/grime_memory.odin b/code/grime_memory.odin index b55fc0b..d9eb45d 100644 --- a/code/grime_memory.odin +++ b/code/grime_memory.odin @@ -56,8 +56,8 @@ memory_after :: #force_inline proc "contextless" ( slice : []byte ) -> ( ^ byte) } memory_after_header :: #force_inline proc "contextless" ( header : ^($ Type) ) -> ( [^]byte) { - // return cast( [^]byte) (cast( [^]Type) header)[ 1:] - result := cast( [^]byte) ptr_offset( header, size_of(Type) ) + result := cast( [^]byte) ptr_offset( header, 1 ) + // result := cast( [^]byte) (cast( [^]Type) header)[ 1:] return result } diff --git a/code/grime_pool_allocator.odin b/code/grime_pool_allocator.odin index 07d3558..29ed2a2 100644 --- a/code/grime_pool_allocator.odin +++ b/code/grime_pool_allocator.odin @@ -100,9 +100,14 @@ pool_allocate_buckets :: proc( pool : Pool, num_buckets : uint ) -> AllocatorErr bucket_size := header_size + pool.bucket_capacity to_allocate := cast(int) (bucket_size * num_buckets) + log(str_fmt_tmp("Allocating %d bytes for %d buckets with header_size %d bytes & bucket_size %d", to_allocate, num_buckets, header_size, bucket_size )) + pool_validate( pool ) bucket_memory, alloc_error := alloc_bytes_non_zeroed( to_allocate, int(pool.alignment), pool.backing ) pool_validate( pool ) + + log(str_fmt_tmp("Bucket memory size: %d bytes, without header: %d", len(bucket_memory), len(bucket_memory) - int(header_size))) + if alloc_error != .None { return alloc_error } @@ -114,16 +119,14 @@ pool_allocate_buckets :: proc( pool : Pool, num_buckets : uint ) -> AllocatorErr bucket := cast( ^PoolBucket) next_bucket_ptr bucket.blocks = memory_after_header(bucket) bucket.next_block = 0 - log( str_fmt_tmp("Pool (%d) allocated bucket: %p capacity: %d", + log( str_fmt_tmp("\tPool (%d) allocated bucket: %p start %p capacity: %d (raw: %d)", pool.block_size, raw_data(bucket_memory), - pool.bucket_capacity / pool.block_size + bucket.blocks, + pool.bucket_capacity / pool.block_size, + pool.bucket_capacity )) - if bucket == cast(rawptr) uintptr(0x100017740D0) { - runtime.debug_trap() - } - if pool.bucket_list.first == nil { pool.bucket_list.first = bucket pool.bucket_list.last = bucket @@ -142,26 +145,25 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo { pool := pool if pool.current_bucket != nil { - verify( pool.current_bucket.blocks != nil, str_fmt_tmp("current_bucket was wiped %p", pool.current_bucket) ) + verify( pool.current_bucket.blocks != nil, str_fmt_tmp("(corruption) current_bucket was wiped %p", pool.current_bucket) ) } // profile(#procedure) alloc_error = .None // Check the free-list first for a block - // if pool.free_list_head != nil && false + // if pool.free_list_head != nil if false { head := & pool.free_list_head // Compiler Bug? Fails to compile // last_free := ll_pop( & pool.free_list_head ) - last_free : ^Pool_FreeBlock = pool.free_list_head pool.free_list_head = pool.free_list_head.next block = byte_slice( cast([^]byte) last_free, int(pool.block_size) ) - log( str_fmt_tmp("Returning free block: %p %d", raw_data(block), pool.block_size)) + log( str_fmt_tmp("\tReturning free block: %p %d", raw_data(block), pool.block_size)) if zero_memory { slice.zero(block) } @@ -169,8 +171,6 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo return } - // Compiler Fail Bug ? using current_bucket directly instead of with pool.. - // if current_bucket == nil if pool.current_bucket == nil { alloc_error = pool_allocate_buckets( pool, 1 ) @@ -182,9 +182,6 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo // log( "First bucket allocation") } - // Compiler Bug ? (Won't work without "pool."") - // next := uintptr(current_bucket.blocks) + uintptr(current_bucket.next_block) - // end := uintptr(current_bucket.blocks) + uintptr(bucket_capacity) next := uintptr(pool.current_bucket.blocks) + uintptr(pool.current_bucket.next_block) end := uintptr(pool.current_bucket.blocks) + uintptr(pool.bucket_capacity) @@ -195,20 +192,20 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo // if current_bucket.next != nil { if pool.current_bucket.next != nil { // current_bucket = current_bucket.next - // log( str_fmt_tmp("Bucket %p exhausted using %p", pool.current_bucket, pool.current_bucket.next)) + log( str_fmt_tmp("\tBucket %p exhausted using %p", pool.current_bucket, pool.current_bucket.next)) pool.current_bucket = pool.current_bucket.next - verify( pool.current_bucket.blocks != nil, "Next's blocks are null?" ) + verify( pool.current_bucket.blocks != nil, "New current_bucket's blocks are null (new current_bucket is corrupted)" ) } else { - log( "All previous buckets exhausted, allocating new bucket") + log( "\tAll previous buckets exhausted, allocating new bucket") alloc_error := pool_allocate_buckets( pool, 1 ) if alloc_error != .None { ensure(false, "Failed to allocate bucket") return } pool.current_bucket = pool.current_bucket.next - verify( pool.current_bucket.blocks != nil, "Next's blocks are null (Post new bucket alloc)?" ) + verify( pool.current_bucket.blocks != nil, "Next's blocks are null (Post new bucket alloc)" ) } } @@ -224,10 +221,11 @@ pool_grab :: proc( pool : Pool, zero_memory := false ) -> ( block : []byte, allo pool.current_bucket.next_block += pool.block_size next = uintptr(pool.current_bucket.blocks) + uintptr(pool.current_bucket.next_block) - // log( str_fmt_tmp("grabbing block: %p blocks left: %d", raw_data(block), (end - next) / uintptr(pool.block_size) )) + log( str_fmt_tmp("\tgrabbing block: %p from %p blocks left: %d", raw_data(block), pool.current_bucket.blocks, (end - next) / uintptr(pool.block_size) )) if zero_memory { slice.zero(block) + log( str_fmt_tmp("Zeroed memory - Range(%p to %p)", block_ptr, cast(rawptr) (uintptr(block_ptr) + uintptr(pool.block_size)))) } return } @@ -274,7 +272,7 @@ pool_validate :: proc( pool : Pool ) // Compiler bug ^^ same as pool_reset for ; bucket != nil; bucket = bucket.next { - verify( bucket.blocks != nil, "Found corrupted bucket" ) + verify( bucket.blocks != nil, str_fmt_tmp("Found corrupted bucket %p", bucket) ) } } diff --git a/code/grime_slab_allocator.odin b/code/grime_slab_allocator.odin index cd28bb0..5cb0641 100644 --- a/code/grime_slab_allocator.odin +++ b/code/grime_slab_allocator.odin @@ -28,6 +28,7 @@ which each contain the ratio of bucket to block size. */ package sectr +import "base:runtime" import "core:mem" import "core:slice" @@ -123,14 +124,14 @@ slab_alloc :: proc( self : Slab, break } } - verify( id < self.pools.idx, "There is not a size class in the slab's policy to satisfy the requested allocation" ) - - verify( pool.header != nil, "Requested alloc not supported by the slab allocator", location = loc ) + verify( id < self.pools.idx, "There is not a size class in the slab's policy to satisfy the requested allocation", location = loc ) + verify( pool.header != nil, "Requested alloc not supported by the slab allocator", location = loc ) block : []byte slab_validate_pools( self ) block, alloc_error = pool_grab(pool) slab_validate_pools( self ) + if block == nil || alloc_error != .None { ensure(false, "Bad block from pool") return nil, alloc_error @@ -198,9 +199,11 @@ slab_resize :: proc( using self : Slab, if zero_memory && new_size > old_size { to_zero := byte_slice( new_data_ptr, int(new_size - old_size) ) + slab_validate_pools( self ) slice.zero( to_zero ) slab_validate_pools( self ) + log( str_fmt_tmp("Zeroed memory - Range(%p to %p)", new_data_ptr, cast(rawptr) (uintptr(new_data_ptr) + uintptr(new_size - old_size)))) } return @@ -208,23 +211,28 @@ slab_resize :: proc( using self : Slab, // We'll need to provide an entirely new block, so the data will need to be copied over. new_block : []byte + slab_validate_pools( self ) new_block, alloc_error = pool_grab( pool_resize ) slab_validate_pools( self ) + if new_block == nil { ensure(false, "Retreived a null block") return } if alloc_error != .None do return - // if zero_memory { - // slice.zero( new_block ) - // } + + // TODO(Ed): Reapply this when safe. + if zero_memory { + slice.zero( new_block ) + log( str_fmt_tmp("Zeroed memory - Range(%p to %p)", raw_data(new_block), cast(rawptr) (uintptr(raw_data(new_block)) + uintptr(new_size)))) + } // log( str_fmt_tmp("Resize via new block: %p %d (old : %p $d )", raw_data(new_block), len(new_block), raw_data(data), old_size )) if raw_data(data) != raw_data(new_block) { - log( str_fmt_tmp("%v: Resize view new block, copying from old data block to new block: (%p %d), (%p %d)", dbg_name, raw_data(data), len(data), raw_data(new_block), len(new_block))) + log( str_fmt_tmp("%v: Resize via new block, copying from old data block to new block: (%p %d), (%p %d)", dbg_name, raw_data(data), len(data), raw_data(new_block), len(new_block))) copy_non_overlapping( raw_data(new_block), raw_data(data), int(old_size) ) pool_release( pool_old, data ) } diff --git a/code/grime_string_interning.odin b/code/grime_string_interning.odin index 495142f..e967d0e 100644 --- a/code/grime_string_interning.odin +++ b/code/grime_string_interning.odin @@ -11,6 +11,7 @@ If open addressing, we just keep the open addressed array of node slots in the g */ package sectr +import "base:runtime" import "core:mem" import "core:slice" import "core:strings" @@ -54,6 +55,8 @@ str_cache_init :: proc( /*allocator : Allocator*/ ) -> ( cache : StringCache ) { @static dbg_name := "StringCache slab" + state := get_state() + alloc_error : AllocatorError cache.slab, alloc_error = slab_init( & policy, allocator = persistent_allocator(), dbg_name = dbg_name ) verify(alloc_error == .None, "Failed to initialize the string cache" ) diff --git a/code/grime_virtual_arena.odin b/code/grime_virtual_arena.odin index a0505d5..784e4ac 100644 --- a/code/grime_virtual_arena.odin +++ b/code/grime_virtual_arena.odin @@ -15,6 +15,7 @@ The host application as well ideally (although this may not be the case for a wh package sectr import "base:intrinsics" +import "base:runtime" import "core:mem" import "core:os" import "core:slice" @@ -148,9 +149,8 @@ varena_alloc :: proc( using self : ^VArena, self.commit_used += size_to_allocate alloc_error = .None - log_backing : [Kilobyte * 16]byte - backing_slice := byte_slice( & log_backing[0], len(log_backing)) - + // log_backing : [Kilobyte * 16]byte + // backing_slice := byte_slice( & log_backing[0], len(log_backing)) // log( str_fmt_buffer( backing_slice, "varena alloc - BASE: %p PTR: %X, SIZE: %d", cast(rawptr) self.base_address, & data[0], requested_size) ) if zero_memory diff --git a/code/grime_virtual_memory.odin b/code/grime_virtual_memory.odin index 396cdc7..7b8aa02 100644 --- a/code/grime_virtual_memory.odin +++ b/code/grime_virtual_memory.odin @@ -99,7 +99,7 @@ when ODIN_OS != OS_Type.Windows { virtual__reserve :: proc "contextless" ( base_address : uintptr, size : uint ) -> ( vmem : VirtualMemoryRegion, alloc_error : AllocatorError ) { - header_size := size_of(VirtualMemoryRegionHeader) + header_size := memory_align_formula(size_of(VirtualMemoryRegionHeader), mem.DEFAULT_ALIGNMENT) // Ignoring the base address, add an os specific impl if you want it. data : []byte @@ -107,7 +107,7 @@ virtual__reserve :: proc "contextless" ( base_address : uintptr, size : uint ) - alloc_error := core_virtual.commit( header_size ) vmem.base_address := cast( ^VirtualMemoryRegionHeader ) raw_data(data) - vmem.reserve_start = memory_after_header(vmem.base_address) + vmem.reserve_start = cast([^]byte) (uintptr(vmem.base_address) + uintptr(header_size)) vmem.reserved = len(data) vmem.committed = header_size return diff --git a/code/grime_windows.odin b/code/grime_windows.odin index e27235f..8f21f31 100644 --- a/code/grime_windows.odin +++ b/code/grime_windows.odin @@ -3,6 +3,7 @@ package sectr import "core:c" import "core:c/libc" import "core:fmt" +import "core:mem" import core_virtual "core:mem/virtual" import "core:strings" import win32 "core:sys/windows" @@ -73,10 +74,9 @@ WIN32_ERROR_INVALID_ADDRESS :: 487 WIN32_ERROR_COMMITMENT_LIMIT :: 1455 @(require_results) -virtual__reserve :: -proc "contextless" ( base_address : uintptr, size : uint ) -> ( vmem : VirtualMemoryRegion, alloc_error : AllocatorError ) +virtual__reserve :: proc "contextless" ( base_address : uintptr, size : uint ) -> ( vmem : VirtualMemoryRegion, alloc_error : AllocatorError ) { - header_size :: cast(uint) size_of(VirtualMemoryRegion) + header_size := cast(uint) memory_align_formula(size_of(VirtualMemoryRegionHeader), mem.DEFAULT_ALIGNMENT) result := win32.VirtualAlloc( rawptr(base_address), header_size + size, win32.MEM_RESERVE, win32.PAGE_READWRITE ) if result == nil { @@ -102,7 +102,7 @@ proc "contextless" ( base_address : uintptr, size : uint ) -> ( vmem : VirtualMe } vmem.base_address = cast(^VirtualMemoryRegionHeader) result - vmem.reserve_start = memory_after_header(vmem.base_address) + vmem.reserve_start = cast([^]byte) (uintptr(vmem.base_address) + uintptr(header_size)) vmem.reserved = size vmem.committed = header_size alloc_error = .None diff --git a/code/logger.odin b/code/logger.odin index 9fe5e2e..c775bd3 100644 --- a/code/logger.odin +++ b/code/logger.odin @@ -8,7 +8,7 @@ import str "core:strings" import "core:time" import core_log "core:log" -Max_Logger_Message_Width :: 180 +Max_Logger_Message_Width :: 120 LogLevel :: core_log.Level diff --git a/code/ui.odin b/code/ui.odin index 09ba704..0d65d90 100644 --- a/code/ui.odin +++ b/code/ui.odin @@ -274,7 +274,7 @@ UI_Box :: struct { UI_Layout_Stack_Size :: 512 UI_Style_Stack_Size :: 512 UI_Parent_Stack_Size :: 512 -UI_Built_Boxes_Array_Size :: 8 * Kilobyte +UI_Built_Boxes_Array_Size :: 8 UI_State :: struct { // TODO(Ed) : Use these