Refactor core:mem/tlsf, add free_all support.

TODO: Allow the TLSF allocator to add additional pools when it would ordinarily OOM
      by calling its backing allocator.
This commit is contained in:
Jeroen van Rijn
2025-04-14 17:13:27 +02:00
parent 0e9cd0fb6a
commit 7088284ff4
3 changed files with 569 additions and 445 deletions
+29 -11
View File
@@ -10,6 +10,7 @@
// package mem_tlsf implements a Two Level Segregated Fit memory allocator.
package mem_tlsf
import "base:intrinsics"
import "base:runtime"
Error :: enum byte {
@@ -42,7 +43,7 @@ Allocator :: struct {
// If we're expected to grow when we run out of memory,
// how much should we ask the backing allocator for?
new_pool_size: int,
new_pool_size: uint,
}
#assert(size_of(Allocator) % ALIGN_SIZE == 0)
@@ -74,12 +75,11 @@ init_from_buffer :: proc(control: ^Allocator, buf: []byte) -> Error {
allocator = {},
}
clear(control)
return pool_add(control, buf[:])
return free_all(control)
}
@(require_results)
init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, initial_pool_size: int, new_pool_size := 0) -> Error {
init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, initial_pool_size: int) -> Error {
assert(control != nil)
pool_bytes := align_up(uint(initial_pool_size), ALIGN_SIZE) + INITIAL_POOL_OVERHEAD
if pool_bytes < BLOCK_SIZE_MIN {
@@ -98,8 +98,9 @@ init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, ini
allocator = backing,
}
clear(control)
return pool_add(control, buf[:])
// TODO(Jeroen): Add automatically growing the pools from the backing allocator
return free_all(control)
}
init :: proc{init_from_buffer, init_from_allocator}
@@ -112,8 +113,6 @@ destroy :: proc(control: ^Allocator) {
// No need to call `pool_remove` or anything, as they're they're embedded in the backing memory.
// We do however need to free the `Pool` tracking entities and the backing memory itself.
// As `Allocator` is embedded in the first backing slice, the `control` pointer will be
// invalid after this call.
for p := control.pool.next; p != nil; {
next := p.next
@@ -145,9 +144,8 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
return nil, nil
case .Free_All:
// NOTE: this doesn't work right at the moment, Jeroen has it on his to-do list :)
// clear(control)
return nil, .Mode_Not_Implemented
free_all(control)
return nil, nil
case .Resize:
return resize(control, old_memory, uint(old_size), uint(size), uint(alignment))
@@ -168,3 +166,23 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
return nil, nil
}
// Exported solely to facilitate testing
@(require_results)
ffs :: proc "contextless" (word: u32) -> (bit: i32) {
return -1 if word == 0 else i32(intrinsics.count_trailing_zeros(word))
}
// Exported solely to facilitate testing
@(require_results)
fls :: proc "contextless" (word: u32) -> (bit: i32) {
N :: (size_of(u32) * 8) - 1
return i32(N - intrinsics.count_leading_zeros(word))
}
// Exported solely to facilitate testing
@(require_results)
fls_uint :: proc "contextless" (size: uint) -> (bit: i32) {
N :: (size_of(uint) * 8) - 1
return i32(N - intrinsics.count_leading_zeros(size))
}