From dfee7c103e1e24076fe35e1aeaffc6d0a7573c10 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 28 Feb 2023 13:07:52 +0000 Subject: [PATCH] Document `virtual.Arena` --- core/mem/virtual/arena.odin | 41 +++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index 579490489..027a6ce6e 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -9,6 +9,13 @@ Arena_Kind :: enum uint { Buffer = 2, // Uses a fixed sized buffer. } +/* + Arena is a generalized arena allocator that supports 3 different variants. + + Growing: A linked list of `Memory_Block`s allocated with virtual memory. + Static: A single `Memory_Block` allocated with virtual memory. + Buffer: A single `Memory_Block` created from a user provided []byte. +*/ Arena :: struct { kind: Arena_Kind, curr_block: ^Memory_Block, @@ -29,6 +36,8 @@ DEFAULT_ARENA_STATIC_RESERVE_SIZE :: mem.Gigabyte when size_of(uintptr) == 8 els +// Initialization of an `Arena` to be a `.Growing` variant. +// A growing arena is a linked list of `Memory_Block`s allocated with virtual memory. @(require_results) arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) { arena.kind = .Growing @@ -39,6 +48,8 @@ arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING } +// Initialization of an `Arena` to be a `.Static` variant. +// A static arena contains a single `Memory_Block` allocated with virtual memory. @(require_results) arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) { arena.kind = .Static @@ -48,6 +59,8 @@ arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEF return } +// Initialization of an `Arena` to be a `.Buffer` variant. +// A buffer arena contains single `Memory_Block` created from a user provided []byte. @(require_results) arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) { if len(buffer) < size_of(Memory_Block) { @@ -71,6 +84,7 @@ arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Erro return } +// Allocates memory from the provided arena. @(require_results) arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc) @@ -119,6 +133,7 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l return } +// Resets the memory of a Static or Buffer arena to a specific `pos`ition (offset) and zeroes the previously used memory. arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool { sync.mutex_guard(&arena.mutex) @@ -140,6 +155,7 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) return false } +// Frees the last memory block of a Growing Arena arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) { if free_block := arena.curr_block; free_block != nil { assert(arena.kind == .Growing, "expected a .Growing arena", loc) @@ -151,6 +167,7 @@ arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_locat } } +// Deallocates all but the first memory block of the arena and resets the allocator's usage to 0. arena_free_all :: proc(arena: ^Arena, loc := #caller_location) { switch arena.kind { case .Growing: @@ -171,12 +188,19 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) { arena.total_used = 0 } +// Frees all of the memory allocated by the arena and zeros all of the values of an arena. +// A buffer based arena does not `delete` the provided `[]byte` bufffer. arena_destroy :: proc(arena: ^Arena, loc := #caller_location) { sync.mutex_guard(&arena.mutex) - if arena.kind != .Buffer { + switch arena.kind { + case .Growing: for arena.curr_block != nil { arena_growing_free_last_memory_block(arena, loc) } + case .Static: + memory_block_dealloc(arena.curr_block) + case .Buffer: + // nothing } arena.curr_block = nil arena.total_used = 0 @@ -184,16 +208,19 @@ arena_destroy :: proc(arena: ^Arena, loc := #caller_location) { arena.temp_count = 0 } +// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. arena_growing_bootstrap_new :: proc{ arena_growing_bootstrap_new_by_offset, arena_growing_bootstrap_new_by_name, } +// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy. arena_static_bootstrap_new :: proc{ arena_static_bootstrap_new_by_offset, arena_static_bootstrap_new_by_name, } +// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. @(require_results) arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) { bootstrap: Arena @@ -209,11 +236,13 @@ arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintp return } +// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. @(require_results) arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) { return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size) } +// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. @(require_results) arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) { bootstrap: Arena @@ -229,17 +258,20 @@ arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintpt return } +// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy. @(require_results) arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) { return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved) } +// Create an `Allocator` from the provided `Arena` @(require_results) arena_allocator :: proc(arena: ^Arena) -> mem.Allocator { return mem.Allocator{arena_allocator_proc, arena} } +// The allocator procedured by an `Allocator` produced by `arena_allocator` arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, @@ -296,12 +328,15 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, +// An `Arena_Temp` is a way to produce temporary watermarks to reset a arena to a previous state. +// All uses of an `Arena_Temp` must be handled by ending them with `arena_temp_end` or ignoring them with `arena_temp_ignore`. Arena_Temp :: struct { arena: ^Arena, block: ^Memory_Block, used: uint, } +// Begins the section of temporary arena memory. @(require_results) arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) { assert(arena != nil, "nil arena", loc) @@ -316,6 +351,7 @@ arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena return } +// Ends the section of temporary arena memory by resetting the memory to the stored position. arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) { assert(temp.arena != nil, "nil arena", loc) arena := temp.arena @@ -349,7 +385,7 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) { arena.temp_count -= 1 } -// Ignore the use of a `arena_temp_begin` entirely +// Ignore the use of a `arena_temp_begin` entirely by __not__ resetting to the stored position. arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) { assert(temp.arena != nil, "nil arena", loc) arena := temp.arena @@ -359,6 +395,7 @@ arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) { arena.temp_count -= 1 } +// Asserts that all uses of `Arena_Temp` has been used by an `Arena` arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) { assert(arena.temp_count == 0, "Arena_Temp not been ended", loc) }