Browse Source

Clean up `Memory_Block` logic

gingerBill 3 years ago
parent
commit
ccc4c641c4

+ 24 - 30
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
-	}
-	
-	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)):]
+		arena.total_reserved += new_block.reserved	
+	}	
 	
-	curr_block.used += size
-	assert(curr_block.used <= curr_block.size)
-	arena.total_used += size
 	
-	return ptr[:min_size], nil
+	data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
+	if err == nil {
+		arena.total_used += size
+	}
+	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
 	}

+ 56 - 59
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
 }
 
-
-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)
-		block.prev.next = block.next
-		block.next.prev = block.prev
-		sync.mutex_unlock(&global_memory_block_mutex)
+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
 		
-		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
+	
+	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	
 }
 
 
-platform_memory_free :: proc(block: ^Platform_Memory_Block) {
-	if block != nil {
-		release(block, block.total_size)
+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
+		platform_mutex_unlock()
+		
+		platform_memory_free(block)
 	}
 }
 
-@(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
-	}
-}

+ 54 - 0
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
+	}
+}

+ 1 - 1
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