mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-07 16:18:52 +00:00
Clean up Memory_Block logic
This commit is contained in:
@@ -3,64 +3,58 @@ package mem_virtual
|
|||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
Growing_Arena :: struct {
|
Growing_Arena :: struct {
|
||||||
curr_block: ^Memory_Block,
|
curr_block: ^Memory_Block,
|
||||||
total_used: int,
|
total_used: uint,
|
||||||
total_allocated: int,
|
total_reserved: uint,
|
||||||
|
|
||||||
minimum_block_size: int,
|
minimum_block_size: uint,
|
||||||
temp_count: int,
|
temp_count: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_MINIMUM_BLOCK_SIZE :: 1024*1024 // 1 KiB should be enough
|
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) {
|
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 {
|
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
|
||||||
alignment_offset := 0
|
alignment_offset := uint(0)
|
||||||
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
|
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
|
||||||
mask := uintptr(alignment-1)
|
mask := uintptr(alignment-1)
|
||||||
if ptr & mask != 0 {
|
if ptr & mask != 0 {
|
||||||
alignment_offset = alignment - int(ptr & mask)
|
alignment_offset = uint(alignment) - uint(ptr & mask)
|
||||||
}
|
}
|
||||||
return alignment_offset
|
return alignment_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(mem.is_power_of_two(uintptr(alignment)))
|
assert(mem.is_power_of_two(uintptr(alignment)))
|
||||||
|
|
||||||
|
size := uint(0)
|
||||||
size := 0
|
|
||||||
if arena.curr_block != nil {
|
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 {
|
if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
|
||||||
size = mem.align_forward_int(min_size, alignment)
|
size = uint(mem.align_forward_int(min_size, alignment))
|
||||||
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
|
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
|
||||||
|
|
||||||
block_size := max(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
|
new_block.prev = arena.curr_block
|
||||||
arena.curr_block = new_block
|
arena.curr_block = new_block
|
||||||
arena.total_allocated += new_block.size
|
arena.total_reserved += new_block.reserved
|
||||||
}
|
}
|
||||||
|
|
||||||
curr_block := arena.curr_block
|
|
||||||
assert(curr_block.used + size <= curr_block.size)
|
|
||||||
|
|
||||||
ptr := curr_block.base[curr_block.used:]
|
data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
|
||||||
ptr = ptr[uintptr(align_forward_offset(arena, alignment)):]
|
if err == nil {
|
||||||
|
arena.total_used += size
|
||||||
curr_block.used += size
|
}
|
||||||
assert(curr_block.used <= curr_block.size)
|
return
|
||||||
arena.total_used += size
|
|
||||||
|
|
||||||
return ptr[:min_size], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
|
growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
|
||||||
free_block := arena.curr_block
|
free_block := arena.curr_block
|
||||||
arena.curr_block = free_block.prev
|
arena.curr_block = free_block.prev
|
||||||
memory_dealloc(free_block)
|
memory_block_dealloc(free_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
growing_arena_free_all :: proc(arena: ^Growing_Arena) {
|
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)
|
growing_arena_free_last_memory_block(arena)
|
||||||
}
|
}
|
||||||
arena.total_used = 0
|
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) {
|
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) {
|
||||||
@@ -126,7 +120,7 @@ growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator
|
|||||||
Growing_Arena_Temp :: struct {
|
Growing_Arena_Temp :: struct {
|
||||||
arena: ^Growing_Arena,
|
arena: ^Growing_Arena,
|
||||||
block: ^Memory_Block,
|
block: ^Memory_Block,
|
||||||
used: int,
|
used: uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
|
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 {
|
if block := arena.curr_block; block != nil {
|
||||||
assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc)
|
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])
|
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
|
||||||
block.used = temp.used
|
block.used = temp.used
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package mem_virtual
|
package mem_virtual
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import sync "core:sync/sync2"
|
|
||||||
|
|
||||||
DEFAULT_PAGE_SIZE := uint(4096)
|
DEFAULT_PAGE_SIZE := uint(4096)
|
||||||
|
|
||||||
@@ -46,106 +45,104 @@ protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
|||||||
|
|
||||||
Memory_Block :: struct {
|
Memory_Block :: struct {
|
||||||
prev: ^Memory_Block,
|
prev: ^Memory_Block,
|
||||||
base: [^]byte,
|
base: [^]byte,
|
||||||
size: int,
|
used: uint,
|
||||||
used: int,
|
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 {
|
align_formula :: proc "contextless" (size, align: uint) -> uint {
|
||||||
result := size + align-1
|
result := size + align-1
|
||||||
return result - result%align
|
return result - result%align
|
||||||
}
|
}
|
||||||
|
|
||||||
page_size := DEFAULT_PAGE_SIZE
|
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))
|
base_offset := uintptr(size_of(Platform_Memory_Block))
|
||||||
protect_offset := uintptr(0)
|
protect_offset := uintptr(0)
|
||||||
|
|
||||||
do_protection := false
|
do_protection := false
|
||||||
{ // overflow protection
|
if .Overflow_Protection in flags { // overflow protection
|
||||||
rounded_size := align_formula(uint(size), page_size)
|
rounded_size := align_formula(uint(reserved), page_size)
|
||||||
total_size = uint(rounded_size + 2*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)
|
protect_offset = uintptr(page_size + rounded_size)
|
||||||
do_protection = true
|
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)
|
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
|
||||||
|
commit(pmblock.block.base, committed)
|
||||||
// Should be zeroed
|
// Should be zeroed
|
||||||
assert(pmblock.block.used == 0)
|
assert(pmblock.block.used == 0)
|
||||||
assert(pmblock.block.prev == nil)
|
assert(pmblock.block.prev == nil)
|
||||||
|
|
||||||
if (do_protection) {
|
if (do_protection) {
|
||||||
protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
|
protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
|
||||||
}
|
}
|
||||||
|
|
||||||
pmblock.block.size = size
|
pmblock.block.committed = committed
|
||||||
pmblock.total_size = total_size
|
pmblock.block.reserved = reserved
|
||||||
|
|
||||||
sentinel := &global_platform_memory_block_sentinel
|
sentinel := &global_platform_memory_block_sentinel
|
||||||
sync.mutex_lock(&global_memory_block_mutex)
|
platform_mutex_lock()
|
||||||
pmblock.next = sentinel
|
pmblock.next = sentinel
|
||||||
pmblock.prev = sentinel.prev
|
pmblock.prev = sentinel.prev
|
||||||
pmblock.prev.next = pmblock
|
pmblock.prev.next = pmblock
|
||||||
pmblock.next.prev = pmblock
|
pmblock.next.prev = pmblock
|
||||||
sync.mutex_unlock(&global_memory_block_mutex)
|
platform_mutex_unlock()
|
||||||
|
|
||||||
return &pmblock.block, nil
|
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
|
||||||
|
|
||||||
memory_dealloc :: proc(block_to_free: ^Memory_Block) {
|
}
|
||||||
block := (^Platform_Memory_Block)(block_to_free)
|
|
||||||
if block != nil {
|
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
||||||
sync.mutex_lock(&global_memory_block_mutex)
|
|
||||||
|
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_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.prev.next = block.next
|
||||||
block.next.prev = block.prev
|
block.next.prev = block.prev
|
||||||
sync.mutex_unlock(&global_memory_block_mutex)
|
platform_mutex_unlock()
|
||||||
|
|
||||||
platform_memory_free(block)
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,7 +59,7 @@ foreign Kernel32 {
|
|||||||
|
|
||||||
|
|
||||||
_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
_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 {
|
if result == nil {
|
||||||
err = .Out_Of_Memory
|
err = .Out_Of_Memory
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user