diff --git a/code2/grime/allocator_interface.odin b/code2/grime/allocator_interface.odin index 26b3f0a..0e0a095 100644 --- a/code2/grime/allocator_interface.odin +++ b/code2/grime/allocator_interface.odin @@ -39,6 +39,11 @@ AllocatorOp :: enum u32 { Rewind, SavePoint, Query, // Must always be implemented + Is_Owner, + Startup, + Shutdown, + Thread_Start, + Thread_Stop, } AllocatorQueryFlag :: enum u64 { Alloc, @@ -47,27 +52,29 @@ AllocatorQueryFlag :: enum u64 { Shrink, Grow, - Resize, // Supports both grow and shrink Rewind, // Ability to rewind to a save point (ex: arenas, stack), must also be able to save such a point - // Actually_Resize, - // Is_This_Yours, + Actually_Resize, + Multiple_Threads, + Is_Owner, Hint_Fast_Bump, Hint_General_Heap, Hint_Per_Frame_Temporary, Hint_Debug_Support, } -AllocatorError :: Odin_AllocatorError -// AllocatorError :: enum i32 { -// None = 0, -// Out_Of_Memory = 1, -// Invalid_Pointer = 2, -// Invalid_Argument = 3, -// Mode_Not_Implemented = 4, -// } AllocatorQueryFlags :: bit_set[AllocatorQueryFlag; u64] + +// AllocatorError :: Odin_AllocatorError +AllocatorError :: enum byte { + None = 0, + Out_Of_Memory = 1, + Invalid_Pointer = 2, + Invalid_Argument = 3, + Mode_Not_Implemented = 4, + Owner = 5, +} AllocatorSP :: struct { type_sig: AllocatorProc, slot: int, diff --git a/code2/grime/assert.odin b/code2/grime/assert.odin index 492523e..b3b8c8b 100644 --- a/code2/grime/assert.odin +++ b/code2/grime/assert.odin @@ -2,13 +2,13 @@ package grime // TODO(Ed): Below should be defined per-package? -ensure :: #force_inline proc(condition: bool, msg: string, location := #caller_location) -> bool { - if condition do return true +ensure :: #force_inline proc(condition: bool, msg := #caller_expression, location := #caller_location) -> bool { + if condition == false do return false log_print( msg, LoggerLevel.Warning, location ) - when ODIN_DEBUG == false do return false + when ODIN_DEBUG == false do return true else { debug_trap() - return false + return true } } // TODO(Ed) : Setup exit codes! diff --git a/code2/grime/dynamic_array.odin b/code2/grime/dynamic_array.odin index 2074901..a37c765 100644 --- a/code2/grime/dynamic_array.odin +++ b/code2/grime/dynamic_array.odin @@ -80,7 +80,7 @@ array_set_capacity :: proc( self : ^Array( $ Type ), new_capacity: int) -> Alloc new_size := header_size + new_capacity * size_of(Type) old_size := header_size + self.capacity * size_of(Type) new_mem, result_code := mem_resize( slice(transmute(^u8)self.header, old_size), new_size, DEFAULT_ALIGNMENT, ainfo = self.backing ) - if ensure( result_code != AllocatorError.None, "Failed to allocate for new array capacity" ) { + if ensure( result_code == AllocatorError.None, "Failed to allocate for new array capacity" ) { log_print( "Failed to allocate for new array capacity", level = LoggerLevel.Warning ) return result_code } @@ -175,7 +175,7 @@ array_fill :: proc(self: Array($Type), begin, end: u64, value: Type) -> bool { } // Will push value into the array (will not grow if at capacity, use append instead for when that matters) -array_push_back :: #force_inline proc "contextless" (self: Array($Type)) -> bool { +array_push :: #force_inline proc "contextless" (self: Array($Type)) -> bool { if self.num == self.capacity { return false } self.data[self.num] = value self.num += 1 diff --git a/code2/grime/fixed_arena.odin b/code2/grime/fixed_arena.odin index b399268..a79bea5 100644 --- a/code2/grime/fixed_arena.odin +++ b/code2/grime/fixed_arena.odin @@ -85,6 +85,13 @@ farena_rewind :: #force_inline proc "contextless" (arena: ^FArena, save_point: A arena.used = save_point.slot } farena_save :: #force_inline proc "contextless" (arena: FArena) -> AllocatorSP { return AllocatorSP { type_sig = farena_allocator_proc, slot = arena.used } } +farena_is_owner :: #force_inline proc "contextless" (arena: FArena, memory: []byte) -> bool { + p0 := transmute(uintptr) cursor(memory) + p1 := transmute(uintptr) end(memory) + arena_p0 := transmute(uintptr) cursor(arena.mem) + arena_p1 := cast(uintptr) arena.used + return arena_p0 <= p0 && arena_p1 >= p1 +} farena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Out) { assert_contextless(output != nil) assert_contextless(input.data != nil) @@ -115,12 +122,16 @@ farena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Ou output.save_point = farena_save(arena^) return case .Query: - output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind} + output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind, .Actually_Resize, .Is_Owner, .Hint_Fast_Bump} output.max_alloc = len(arena.mem) - arena.used output.min_alloc = 0 output.left = output.max_alloc output.save_point = farena_save(arena^) return + case .Is_Owner: + output.error = farena_is_owner(arena ^, input.old_allocation) ? .Owner : .None + case .Startup, .Shutdown, .Thread_Start, .Thread_Stop: + output.error = .Mode_Not_Implemented } panic_contextless("Impossible path") } @@ -132,32 +143,29 @@ farena_odin_allocator_proc :: proc( old_memory : rawptr, old_size : int, location : SourceCodeLocation = #caller_location -) -> ( data : []byte, alloc_error : AllocatorError) +) -> ( data : []byte, error : Odin_AllocatorError) { + error_: AllocatorError assert_contextless(allocator_data != nil) arena := transmute(^FArena) allocator_data switch mode { case .Alloc, .Alloc_Non_Zeroed: - data, alloc_error = farena_push(arena, byte, size, alignment, location) + data, error_ = farena_push(arena, byte, size, alignment, location) if mode == .Alloc { zero(data) } - return case .Free: return {}, .Mode_Not_Implemented case .Free_All: farena_reset(arena) - return case .Resize, .Resize_Non_Zeroed: - if (size > old_size) do data, alloc_error = farena_grow (arena, slice(cursor(old_memory), old_size), size, alignment, mode == .Resize) - else do data, alloc_error = farena_shirnk(arena, slice(cursor(old_memory), old_size), size, alignment) - return + if (size > old_size) do data, error_ = farena_grow (arena, slice(cursor(old_memory), old_size), size, alignment, mode == .Resize) + else do data, error_ = farena_shirnk(arena, slice(cursor(old_memory), old_size), size, alignment) case .Query_Features: set := (^Odin_AllocatorModeSet)(old_memory) if set != nil { set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features, .Query_Info} } - return case .Query_Info: info := (^Odin_AllocatorQueryInfo)(old_memory) info.pointer = transmute(rawptr) farena_save(arena^).slot @@ -165,7 +173,8 @@ farena_odin_allocator_proc :: proc( info.alignment = DEFAULT_ALIGNMENT return to_bytes(info), nil } - panic_contextless("Impossible path") + error = transmute(Odin_AllocatorError) error_ + return } when ODIN_DEBUG { farena_ainfo :: #force_inline proc "contextless" (arena: ^FArena) -> AllocatorInfo { return AllocatorInfo{proc_id = .FArena, data = arena} } diff --git a/code2/grime/virtual_arena.odin b/code2/grime/virtual_arena.odin index 1f207b3..6ea90f0 100644 --- a/code2/grime/virtual_arena.odin +++ b/code2/grime/virtual_arena.odin @@ -40,7 +40,7 @@ varena_make :: proc(to_reserve, commit_size: int, base_address: uintptr, flags: verify( to_reserve >= commit_size, "Attempted to commit more than there is to reserve" ) vmem : VirtualMemoryRegion vmem, alloc_error = virtual_reserve_and_commit( base_address, uint(to_reserve), uint(commit_size) ) - if ensure(vmem.base_address == nil || alloc_error != .None, "Failed to allocate requested virtual memory for virtual arena") { + if ensure(vmem.base_address != nil && alloc_error == .None, "Failed to allocate requested virtual memory for virtual arena") { return } arena = transmute(^VArena) vmem.base_address; @@ -59,7 +59,7 @@ varena_alloc :: proc(self: ^VArena, verify( alignment & (alignment - 1) == 0, "Non-power of two alignment", location = location ) page_size := uint(virtual_get_page_size()) requested_size := uint(size) - if ensure(requested_size == 0, "Requested 0 size") do return nil, .Invalid_Argument + if ensure(requested_size > 0, "Requested 0 size") do return nil, .Invalid_Argument // ensure( requested_size > page_size, "Requested less than a page size, going to allocate a page size") // requested_size = max(requested_size, page_size) @@ -106,11 +106,11 @@ varena_alloc :: proc(self: ^VArena, varena_grow :: #force_inline proc(self: ^VArena, old_memory: []byte, requested_size: int, alignment: int = DEFAULT_ALIGNMENT, zero_memory := true, loc := #caller_location ) -> (data: []byte, error: AllocatorError) { - if ensure(old_memory == nil, "Growing without old_memory?") { + if ensure(old_memory != nil, "Growing without old_memory?") { data, error = varena_alloc(self, requested_size, alignment, zero_memory, loc) return } - if ensure(requested_size == len(old_memory), "Requested grow when none needed") { + if ensure(requested_size != len(old_memory), "Requested grow when none needed") { data = old_memory return } @@ -137,7 +137,7 @@ varena_grow :: #force_inline proc(self: ^VArena, old_memory: []byte, requested_s // Give it new memory and copy the old over. Old memory is unrecoverable until clear. new_region : []byte new_region, error = varena_alloc( self, requested_size, alignment, zero_memory, loc ) - if ensure(new_region == nil || error != .None, "Failed to grab new region") { + if ensure(new_region != nil && error == .None, "Failed to grab new region") { data = old_memory return } @@ -148,7 +148,7 @@ varena_grow :: #force_inline proc(self: ^VArena, old_memory: []byte, requested_s } new_region : []byte new_region, error = varena_alloc( self, requested_size - len(old_memory), alignment, zero_memory, loc) - if ensure(new_region == nil || error != .None, "Failed to grab new region") { + if ensure(new_region != nil && error == .None, "Failed to grab new region") { data = old_memory return } @@ -158,7 +158,7 @@ varena_grow :: #force_inline proc(self: ^VArena, old_memory: []byte, requested_s } varena_shrink :: proc(self: ^VArena, memory: []byte, requested_size: int, loc := #caller_location) -> (data: []byte, error: AllocatorError) { if requested_size == len(memory) { return memory, .None } - if ensure(memory == nil, "Shrinking without old_memory?") do return memory, .Invalid_Argument + if ensure(memory != nil, "Shrinking without old_memory?") do return memory, .Invalid_Argument current_offset := self.reserve_start[self.commit_used:] shrink_amount := len(memory) - requested_size if shrink_amount < 0 { return memory, .None } @@ -204,12 +204,16 @@ varena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Ou varena_rewind(arena, input.save_point) case .SavePoint: output.save_point = varena_save(arena) + case .Is_Owner: + output.error = .Mode_Not_Implemented case .Query: - output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind} + output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind, .Actually_Resize, .Hint_Fast_Bump, .Is_Owner} output.max_alloc = int(arena.reserved) - arena.commit_used output.min_alloc = 0 output.left = output.max_alloc output.save_point = varena_save(arena) + case .Startup, .Shutdown, .Thread_Start, .Thread_Stop: + output.error = .Mode_Not_Implemented } } varena_odin_allocator_proc :: proc( @@ -220,21 +224,21 @@ varena_odin_allocator_proc :: proc( old_memory : rawptr, old_size : int, location : SourceCodeLocation = #caller_location -) -> (data: []byte, alloc_error: AllocatorError) +) -> (data: []byte, error: Odin_AllocatorError) { + error_: AllocatorError arena := transmute( ^VArena) allocator_data page_size := uint(virtual_get_page_size()) switch mode { case .Alloc, .Alloc_Non_Zeroed: - data, alloc_error = varena_alloc( arena, size, alignment, (mode == .Alloc), location ) - return + data, error_ = varena_alloc( arena, size, alignment, (mode == .Alloc), location ) case .Free: - alloc_error = .Mode_Not_Implemented + error = .Mode_Not_Implemented case .Free_All: varena_reset( arena ) case .Resize, .Resize_Non_Zeroed: - if size > old_size do varena_grow (arena, slice(cursor(old_memory), old_size), size, alignment, (mode == .Alloc), location) - else do varena_shrink(arena, slice(cursor(old_memory), old_size), size, location) + if size > old_size do data, error_ = varena_grow (arena, slice(cursor(old_memory), old_size), size, alignment, (mode == .Alloc), location) + else do data, error_ = varena_shrink(arena, slice(cursor(old_memory), old_size), size, location) case .Query_Features: set := cast( ^Odin_AllocatorModeSet) old_memory if set != nil do (set ^) = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features} @@ -245,6 +249,7 @@ varena_odin_allocator_proc :: proc( info.alignment = DEFAULT_ALIGNMENT return to_bytes(info), nil } + error = transmute(Odin_AllocatorError) error_ return } diff --git a/code2/grime/virtual_chained_arena.odin b/code2/grime/virtual_chained_arena.odin index 610a33a..dcd6e36 100644 --- a/code2/grime/virtual_chained_arena.odin +++ b/code2/grime/virtual_chained_arena.odin @@ -18,12 +18,12 @@ Arena :: struct { flags: ArenaFlags, } -arena_make :: proc(reserve_size : int = Mega * 64, commit_size : int = Mega * 64, base_addr: uintptr = 0, flags: ArenaFlags = {}) -> ^Arena { +arena_make :: proc(reserve_size : int = Mega * 64, commit_size : int = Mega * 64, base_addr: uintptr = 0, flags: ArenaFlags = {}) -> (^Arena, AllocatorError) { header_size := align_pow2(size_of(Arena), DEFAULT_ALIGNMENT) current, error := varena_make(reserve_size, commit_size, base_addr, transmute(VArenaFlags) flags) - assert(error == .None) + if ensure(error == .None) do return nil, error arena: ^Arena; arena, error = varena_push_item(current, Arena, 1) - assert(error == .None) + if ensure(error == .None) do return nil, error arena^ = Arena { backing = current, prev = nil, @@ -32,9 +32,9 @@ arena_make :: proc(reserve_size : int = Mega * 64, commit_size : int = Mega * 64 pos = header_size, flags = flags, } - return arena + return arena, .None } -arena_alloc :: proc(arena: ^Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, should_zero := true) -> []byte { +arena_alloc :: proc(arena: ^Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, should_zero := true, loc := #caller_location) -> (allocation: []byte, error: AllocatorError) { assert(arena != nil) active := arena.current size_requested := size @@ -44,20 +44,24 @@ arena_alloc :: proc(arena: ^Arena, size: int, alignment: int = DEFAULT_ALIGNMENT reserved := int(active.backing.reserved) should_chain := (.No_Chaining not_in arena.flags) && (reserved < pos_pst) if should_chain { - new_arena := arena_make(reserved, active.backing.commit_size, 0, transmute(ArenaFlags) active.backing.flags) + new_arena: ^Arena; new_arena, error = arena_make(reserved, active.backing.commit_size, 0, transmute(ArenaFlags) active.backing.flags) + if ensure(error == .None) do return new_arena.base_pos = active.base_pos + reserved sll_stack_push_n(& arena.current, & new_arena, & new_arena.prev) new_arena.prev = active active = arena.current } result_ptr := transmute([^]byte) (uintptr(active) + uintptr(pos_pre)) - vresult, error := varena_alloc(active.backing, size_aligned, alignment, should_zero) - assert(error == .None) + vresult: []byte; vresult, error = varena_alloc(active.backing, size_aligned, alignment, should_zero) + if ensure(error == .None) do return assert(cursor(vresult) == result_ptr) active.pos = pos_pst - return slice(result_ptr, size) + allocation = slice(result_ptr, size) + return } -arena_grow :: proc(arena: ^Arena, old_allocation: []byte, requested_size: int, alignment: int = DEFAULT_ALIGNMENT, zero_memory := true) -> (allocation: []byte) { +arena_grow :: proc(arena: ^Arena, old_allocation: []byte, requested_size: int, alignment: int = DEFAULT_ALIGNMENT, zero_memory := true, loc := #caller_location +) -> (allocation: []byte, error: AllocatorError) +{ active := arena.current if len(old_allocation) == 0 { allocation = {}; return } alloc_end := end(old_allocation) @@ -69,22 +73,37 @@ arena_grow :: proc(arena: ^Arena, old_allocation: []byte, requested_size: int, a aligned_grow := align_pow2(grow_amount, alignment) if active.pos + aligned_grow <= cast(int) active.backing.reserved { - vresult, error := varena_alloc(active.backing, aligned_grow, alignment, zero_memory); - assert(error == .None) - if len(vresult) > 0 { - active.pos += aligned_grow - allocation = slice(cursor(old_allocation), requested_size) - return - } + vresult: []byte; vresult, error = varena_alloc(active.backing, aligned_grow, alignment, zero_memory) + if ensure(error == .None) do return + active.pos += aligned_grow + allocation = slice(cursor(old_allocation), requested_size) + return } } // Can't grow in place, allocate new - allocation = arena_alloc(arena, requested_size, alignment, false) - if len(allocation) == 0 { allocation = {}; return } + allocation, error = arena_alloc(arena, requested_size, alignment, false) + if ensure(error == .None) do return copy(allocation, old_allocation) zero(cursor(allocation)[len(old_allocation):], (requested_size - len(old_allocation)) * int(zero_memory)) return } +arena_shrink :: proc(arena: ^Arena, old_allocation: []byte, requested_size, alignment: int, loc := #caller_location) -> (result: []byte, error: AllocatorError) { + active := arena.current + if ensure(len(old_allocation) != 0) { return } + alloc_end := end(old_allocation) + arena_end := transmute([^]byte) (uintptr(active) + uintptr(active.pos)) + if alloc_end != arena_end { + // Not at the end, can't shrink but return adjusted size + result = old_allocation[:requested_size] + } + // Calculate shrinkage + aligned_original := align_pow2(len(old_allocation), DEFAULT_ALIGNMENT) + aligned_new := align_pow2(requested_size, alignment) + pos_reduction := aligned_original - aligned_new + active.pos -= pos_reduction + result, error = varena_shrink(active.backing, old_allocation, aligned_new) + return +} arena_release :: proc(arena: ^Arena) { assert(arena != nil) curr := arena.current @@ -97,7 +116,7 @@ arena_release :: proc(arena: ^Arena) { arena_reset :: proc(arena: ^Arena) { arena_rewind(arena, AllocatorSP { type_sig = arena_allocator_proc, slot = 0 }) } -arena_rewind :: proc(arena: ^Arena, save_point: AllocatorSP) { +arena_rewind :: proc(arena: ^Arena, save_point: AllocatorSP, loc := #caller_location) { assert(arena != nil) assert(save_point.type_sig == arena_allocator_proc) header_size := align_pow2(size_of(Arena), DEFAULT_ALIGNMENT) @@ -117,7 +136,36 @@ arena_rewind :: proc(arena: ^Arena, save_point: AllocatorSP) { arena_save :: #force_inline proc(arena: ^Arena) -> AllocatorSP { return { type_sig = arena_allocator_proc, slot = arena.base_pos + arena.current.pos } } arena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Out) { - panic("not implemented") + assert(output != nil) + assert(input.data != nil) + arena := transmute(^Arena) input.data + switch input.op { + case .Alloc, .Alloc_NoZero: + output.allocation, output.error = arena_alloc(arena, input.requested_size, input.alignment, input.op == .Alloc, input.loc) + return + case .Free: + output.error = .Mode_Not_Implemented + case .Reset: + arena_reset(arena) + case .Grow, .Grow_NoZero: + output.allocation, output.error = arena_grow(arena, input.old_allocation, input.requested_size, input.alignment, input.op == .Grow, input.loc) + case .Shrink: + output.allocation, output.error = arena_shrink(arena, input.old_allocation, input.requested_size, input.alignment, input.loc) + case .Rewind: + arena_rewind(arena, input.save_point, input.loc) + case .SavePoint: + output.save_point = arena_save(arena) + case .Query: + output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind, .Actually_Resize, .Is_Owner, .Hint_Fast_Bump } + output.max_alloc = int(arena.backing.reserved) + output.min_alloc = 0 + output.left = output.max_alloc + output.save_point = arena_save(arena) + case .Is_Owner: + output.error = .Mode_Not_Implemented + case .Startup, .Shutdown, .Thread_Start, .Thread_Stop: + output.error = .Mode_Not_Implemented + } } arena_odin_allocator_proc :: proc( allocator_data : rawptr, @@ -127,7 +175,7 @@ arena_odin_allocator_proc :: proc( old_memory : rawptr, old_size : int, location : SourceCodeLocation = #caller_location -) -> (data: []byte, alloc_error: AllocatorError) +) -> (data: []byte, alloc_error: Odin_AllocatorError) { panic("not implemented") } diff --git a/code2/grime/virtual_memory.odin b/code2/grime/virtual_memory.odin index ff0355a..597e6ba 100644 --- a/code2/grime/virtual_memory.odin +++ b/code2/grime/virtual_memory.odin @@ -40,7 +40,7 @@ virtual_commit :: proc "contextless" ( using vmem : VirtualMemoryRegion, size : page_size := uint(virtual_get_page_size()) to_commit := memory_align_formula( size, page_size ) - alloc_error = core_virtual.commit( base_address, to_commit ) + alloc_error = cast(AllocatorError) core_virtual.commit( base_address, to_commit ) if alloc_error != .None { return alloc_error }