diff --git a/core/mem/virtual/growing_arena.odin b/core/mem/virtual/growing_arena.odin index dbb32149e..d15b61eed 100644 --- a/core/mem/virtual/growing_arena.odin +++ b/core/mem/virtual/growing_arena.odin @@ -3,64 +3,58 @@ package mem_virtual import "core:mem" Growing_Arena :: struct { - curr_block: ^Memory_Block, - total_used: int, - total_allocated: int, + curr_block: ^Memory_Block, + total_used: uint, + total_reserved: uint, - minimum_block_size: int, + minimum_block_size: uint, temp_count: int, } DEFAULT_MINIMUM_BLOCK_SIZE :: 1024*1024 // 1 KiB should be enough growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) { - align_forward_offset :: proc(arena: ^Growing_Arena, alignment: int) -> int #no_bounds_check { - alignment_offset := 0 + align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check { + alignment_offset := uint(0) ptr := uintptr(arena.curr_block.base[arena.curr_block.used:]) mask := uintptr(alignment-1) if ptr & mask != 0 { - alignment_offset = alignment - int(ptr & mask) + alignment_offset = uint(alignment) - uint(ptr & mask) } return alignment_offset } assert(mem.is_power_of_two(uintptr(alignment))) - - size := 0 + size := uint(0) if arena.curr_block != nil { - size = min_size + align_forward_offset(arena, alignment) + size = uint(min_size) + align_forward_offset(arena, alignment) } - if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.size { - size = mem.align_forward_int(min_size, alignment) + if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved { + size = uint(mem.align_forward_int(min_size, alignment)) arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size) block_size := max(size, arena.minimum_block_size) - new_block := memory_alloc(block_size) or_return + new_block := memory_block_alloc(block_size, block_size, {}) or_return new_block.prev = arena.curr_block arena.curr_block = new_block - arena.total_allocated += new_block.size + arena.total_reserved += new_block.reserved + } + + + data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment) + if err == nil { + arena.total_used += size } - - curr_block := arena.curr_block - assert(curr_block.used + size <= curr_block.size) - - ptr := curr_block.base[curr_block.used:] - ptr = ptr[uintptr(align_forward_offset(arena, alignment)):] - - curr_block.used += size - assert(curr_block.used <= curr_block.size) - arena.total_used += size - - return ptr[:min_size], nil + return } growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) { free_block := arena.curr_block arena.curr_block = free_block.prev - memory_dealloc(free_block) + memory_block_dealloc(free_block) } growing_arena_free_all :: proc(arena: ^Growing_Arena) { @@ -68,7 +62,7 @@ growing_arena_free_all :: proc(arena: ^Growing_Arena) { growing_arena_free_last_memory_block(arena) } arena.total_used = 0 - arena.total_allocated = 0 + arena.total_reserved = 0 } growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) { @@ -101,7 +95,7 @@ growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator old_memory: rawptr, old_size: int, location := #caller_location) -> (data: []byte, err: Allocator_Error) { arena := (^Growing_Arena)(allocator_data) - + switch mode { case .Alloc: return growing_arena_alloc(arena, size, alignment) @@ -126,7 +120,7 @@ growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator Growing_Arena_Temp :: struct { arena: ^Growing_Arena, block: ^Memory_Block, - used: int, + used: uint, } growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) { @@ -149,7 +143,7 @@ growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location if block := arena.curr_block; block != nil { assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc) - amount_to_zero := min(block.used-temp.used, block.size-block.used) + amount_to_zero := min(block.used-temp.used, block.reserved-block.used) mem.zero_slice(block.base[temp.used:][:amount_to_zero]) block.used = temp.used } diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index ee314589f..97466b8e6 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -1,7 +1,6 @@ package mem_virtual import "core:mem" -import sync "core:sync/sync2" DEFAULT_PAGE_SIZE := uint(4096) @@ -46,106 +45,104 @@ protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool { Memory_Block :: struct { prev: ^Memory_Block, - base: [^]byte, - size: int, - used: int, + base: [^]byte, + used: uint, + committed: uint, + reserved: uint, } +Memory_Block_Flag :: enum u32 { + Overflow_Protection, +} +Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32] -memory_alloc :: proc(size: int) -> (block: ^Memory_Block, err: Allocator_Error) { +memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) -> (block: ^Memory_Block, err: Allocator_Error) { align_formula :: proc "contextless" (size, align: uint) -> uint { result := size + align-1 return result - result%align } page_size := DEFAULT_PAGE_SIZE + committed := committed + committed = clamp(committed, 0, reserved) - total_size := uint(size + size_of(Platform_Memory_Block)) + total_size := uint(reserved + size_of(Platform_Memory_Block)) base_offset := uintptr(size_of(Platform_Memory_Block)) protect_offset := uintptr(0) do_protection := false - { // overflow protection - rounded_size := align_formula(uint(size), page_size) + if .Overflow_Protection in flags { // overflow protection + rounded_size := align_formula(uint(reserved), page_size) total_size = uint(rounded_size + 2*page_size) - base_offset = uintptr(page_size + rounded_size - uint(size)) + base_offset = uintptr(page_size + rounded_size - uint(reserved)) protect_offset = uintptr(page_size + rounded_size) do_protection = true } - pmblock := platform_memory_alloc(total_size) or_return + pmblock := platform_memory_alloc(0, total_size) or_return pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset) + commit(pmblock.block.base, committed) // Should be zeroed assert(pmblock.block.used == 0) - assert(pmblock.block.prev == nil) - + assert(pmblock.block.prev == nil) if (do_protection) { protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access) } - pmblock.block.size = size - pmblock.total_size = total_size + pmblock.block.committed = committed + pmblock.block.reserved = reserved sentinel := &global_platform_memory_block_sentinel - sync.mutex_lock(&global_memory_block_mutex) + platform_mutex_lock() pmblock.next = sentinel pmblock.prev = sentinel.prev pmblock.prev.next = pmblock pmblock.next.prev = pmblock - sync.mutex_unlock(&global_memory_block_mutex) + platform_mutex_unlock() return &pmblock.block, nil } +alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) { + calc_alignment_offset :: proc(block: ^Memory_Block, alignment: uintptr) -> uint { + alignment_offset := uint(0) + ptr := uintptr(block.base[block.used:]) + mask := alignment-1 + if ptr & mask != 0 { + alignment_offset = uint(alignment - (ptr & mask)) + } + return alignment_offset + + } + + alignment_offset := calc_alignment_offset(block, uintptr(alignment)) + + size := uint(min_size) + alignment_offset + + if block.used + size > block.reserved { + err = .Out_Of_Memory + return + } + + ptr := block.base[block.used:] + ptr = ptr[alignment_offset:] + + block.used += size + assert(block.used <= block.reserved) + + return ptr[:min_size], nil +} -memory_dealloc :: proc(block_to_free: ^Memory_Block) { - block := (^Platform_Memory_Block)(block_to_free) - if block != nil { - sync.mutex_lock(&global_memory_block_mutex) + +memory_block_dealloc :: proc(block_to_free: ^Memory_Block) { + if block := (^Platform_Memory_Block)(block_to_free); block != nil { + platform_mutex_lock() block.prev.next = block.next block.next.prev = block.prev - sync.mutex_unlock(&global_memory_block_mutex) + platform_mutex_unlock() platform_memory_free(block) } } -Platform_Memory_Block :: struct { - block: Memory_Block, - total_size: uint, - prev, next: ^Platform_Memory_Block, -} - -platform_memory_alloc :: proc(total_size: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) { - total_size := total_size - total_size = max(total_size, size_of(Platform_Memory_Block)) - data := reserve_and_commit(total_size) or_return - block = (^Platform_Memory_Block)(raw_data(data)) - block.total_size = total_size - return -} - - -platform_memory_free :: proc(block: ^Platform_Memory_Block) { - if block != nil { - release(block, block.total_size) - } -} - -@(private) -global_memory_block_mutex: sync.Mutex -@(private) -global_platform_memory_block_sentinel: Platform_Memory_Block -@(private) -global_platform_memory_block_sentinel_set: bool - -@(private, init) -platform_memory_init :: proc() { - if !global_platform_memory_block_sentinel_set { - _platform_memory_init() - global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel - global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel - global_platform_memory_block_sentinel_set = true - } -} diff --git a/core/mem/virtual/virtual_platform.odin b/core/mem/virtual/virtual_platform.odin new file mode 100644 index 000000000..c4211ba5e --- /dev/null +++ b/core/mem/virtual/virtual_platform.odin @@ -0,0 +1,54 @@ +//+private +package mem_virtual + +import sync "core:sync/sync2" + +Platform_Memory_Block :: struct { + block: Memory_Block, + reserved: uint, + prev, next: ^Platform_Memory_Block, +} + +platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) { + to_commit, to_reserve := to_commit, to_reserve + to_reserve = max(to_commit, to_reserve) + + total_to_reserved := max(to_reserve, size_of(Platform_Memory_Block)) + to_commit = clamp(to_commit, size_of(Platform_Memory_Block), total_to_reserved) + + data := reserve(total_to_reserved) or_return + commit(raw_data(data), to_commit) + + block = (^Platform_Memory_Block)(raw_data(data)) + block.reserved = to_reserve + return +} + + +platform_memory_free :: proc(block: ^Platform_Memory_Block) { + if block != nil { + release(block, block.reserved) + } +} + +platform_mutex_lock :: proc() { + sync.mutex_lock(&global_memory_block_mutex) +} + +platform_mutex_unlock :: proc() { + sync.mutex_unlock(&global_memory_block_mutex) +} + +global_memory_block_mutex: sync.Mutex +global_platform_memory_block_sentinel: Platform_Memory_Block +global_platform_memory_block_sentinel_set: bool + +@(init) +platform_memory_init :: proc() { + if !global_platform_memory_block_sentinel_set { + _platform_memory_init() + global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel + global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel + global_platform_memory_block_sentinel_set = true + } +} diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index b17e396c8..7d05cfe7b 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -59,7 +59,7 @@ foreign Kernel32 { _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) { - result := VirtualAlloc(nil, size, MEM_RELEASE, PAGE_READWRITE) + result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE) if result == nil { err = .Out_Of_Memory return