Finally fixed memory allocations for slabs/pools/virtual arenas
This commit is contained in:
parent
ca66f517e9
commit
e3ff1ce70f
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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) )
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 )
|
||||
}
|
||||
|
@ -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" )
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user