From a57ebb49becc62141ea46189e429117208de0b2b Mon Sep 17 00:00:00 2001 From: krzosa Date: Tue, 30 Dec 2025 10:09:46 +0100 Subject: [PATCH] Allocator memes --- src/basic/basic_alloc.cpp | 110 ++++++++++++++++++++++++-------- src/basic/basic_alloc.h | 40 +++++++++--- src/basic/basic_os.cpp | 2 +- src/basic/basic_string16.cpp | 4 +- src/render/font.cpp | 6 +- src/text_editor/buffer.cpp | 6 +- src/text_editor/text_editor.cpp | 2 +- src/text_editor/window.cpp | 2 - 8 files changed, 122 insertions(+), 50 deletions(-) diff --git a/src/basic/basic_alloc.cpp b/src/basic/basic_alloc.cpp index 3a178e6..3a9ca0b 100644 --- a/src/basic/basic_alloc.cpp +++ b/src/basic/basic_alloc.cpp @@ -148,13 +148,6 @@ API void *TrackingAllocatorProc(void *object, int kind, void *p, size_t size) { return result; } -API void TrackingAllocatorCheck() { - // For (MemoryTrackingRecord) { - // ReportConsolef("%s(%d): error: memory leak"); - // } - Assert(MemoryTrackingRecord.len == 0); -} - API Allocator GetTrackingAllocator() { Allocator result = {TrackingAllocatorProc}; return result; @@ -267,20 +260,28 @@ API void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size) { /////////////////////////////// // Block Arena +void AddBlock(BlockArena *arena, size_t size = MiB(1)) { + size_t block_size = MiB(1); + if (size > block_size) { + block_size = size; + } + if (arena->allocator.proc == NULL) { + arena->allocator = GetSystemAllocator(); + } + BlockArenaNode *new_block = (BlockArenaNode *)AllocSize(arena->allocator, block_size + sizeof(BlockArenaNode) + 16); + Assert(GetAlignOffset((size_t)new_block->start, DEFAULT_ALIGNMENT) == 0); + arena->start = new_block->start; + new_block->end = arena->end = new_block->start + block_size; + SLL_STACK_ADD(arena->blocks, new_block); +} + +API void Init(BlockArena *arena) { + AddBlock(arena); +} + API void *PushSize(BlockArena *arena, size_t size) { if (size > (size_t)(arena->end - arena->start)) { - size_t block_size = MiB(1); - if (size > block_size) { - block_size = size; - } - if (arena->allocator.proc == NULL) { - arena->allocator = GetSystemAllocator(); - } - BlockArenaNode *new_block = (BlockArenaNode *)AllocSize(arena->allocator, block_size + sizeof(BlockArenaNode)); - Assert(GetAlignOffset((size_t)new_block->start, DEFAULT_ALIGNMENT) == 0); - arena->start = new_block->start; - new_block->end = arena->end = new_block->start + block_size; - SLL_STACK_ADD(arena->blocks, new_block); + AddBlock(arena, size); } U8 *result = arena->start; Assert(GetAlignOffset((size_t)result, DEFAULT_ALIGNMENT) == 0); @@ -293,23 +294,31 @@ API void Release(BlockArena *arena) { next = it->next; Dealloc(arena->allocator, it); } + bool safe = arena->when_unwinding_make_sure_to_keep_last_block_alive; MemoryZero(arena, sizeof(BlockArena)); + arena->when_unwinding_make_sure_to_keep_last_block_alive = safe; } API void Unwind(BlockArena *arena, U8 *pos) { bool contains = false; for (BlockArenaNode *it = arena->blocks, *next = NULL; it; it = next) { next = it->next; - if ((pos >= it->start) && (pos < it->end)) { + if ((pos >= it->start) && (pos <= it->end)) { contains = true; break; } else { - arena->blocks = arena->blocks->next; - Dealloc(arena->allocator, it); + bool keep = arena->when_unwinding_make_sure_to_keep_last_block_alive && arena->blocks->next == NULL; + if (keep) { + pos = arena->blocks->start; + } else { + arena->blocks = arena->blocks->next; + Dealloc(arena->allocator, it); + } } } - Assert(contains || pos == NULL); + Assert(contains || pos == NULL || (arena->when_unwinding_make_sure_to_keep_last_block_alive && pos == arena->blocks->start)); arena->start = pos; + arena->end = arena->blocks ? arena->blocks->end : NULL; } API void *BlockArenaAllocatorProc(void *object, int kind, void *p, size_t size) { @@ -323,6 +332,37 @@ API void *BlockArenaAllocatorProc(void *object, int kind, void *p, size_t size) return NULL; } +thread_local VirtualArena ScratchArenas[4]; + +API void InitScratch() { + for (int i = 0; i < 4; i += 1) { + InitArena(&ScratchArenas[i]); + } +} + +API VirtualArena *GetScratchEx(VirtualArena **conflicts, int conflict_count) { + VirtualArena *unoccupied = 0; + for (int i = 0; i < Lengthof(ScratchArenas); i += 1) { + VirtualArena *from_pool = &ScratchArenas[i]; + unoccupied = from_pool; + for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) { + VirtualArena *from_conflict = conflicts[conflict_i]; + if (from_pool == from_conflict) { + unoccupied = 0; + break; + } + } + + if (unoccupied) { + break; + } + } + + // Failed to get free scratch memory, this is a fatal error, this shouldnt happen + Assert(unoccupied); + return unoccupied; +} + void RunArenaTest() { Allocator memory_tracking_allocator = GetTrackingAllocator(); { @@ -335,7 +375,7 @@ void RunArenaTest() { } } Release(&arena); - TrackingAllocatorCheck(); + Assert(MemoryTrackingRecord.len == 0); } { @@ -351,7 +391,7 @@ void RunArenaTest() { Assert(arena.blocks[0].start == (U8 *)vals); Unwind(&arena, NULL); Dealloc(arena.allocator, arena.blocks); - TrackingAllocatorCheck(); + Assert(MemoryTrackingRecord.len == 0); } { @@ -372,7 +412,7 @@ void RunArenaTest() { Assert(arena.start == p); Release(&arena); - TrackingAllocatorCheck(); + Assert(MemoryTrackingRecord.len == 0); } { @@ -381,6 +421,22 @@ void RunArenaTest() { U8 *a = (U8 *)PushSize(&arena, KiB(2000)); Assert((size_t)(arena.blocks[0].end - arena.blocks[0].start) == KiB(2000)); Release(&arena); - TrackingAllocatorCheck(); + Assert(MemoryTrackingRecord.len == 0); + } + + { + BlockArena arena = {}; + arena.allocator = memory_tracking_allocator; + arena.when_unwinding_make_sure_to_keep_last_block_alive = true; + PushSize(&arena, KiB(2000)); + Unwind(&arena, 0); + Assert(MemoryTrackingRecord.len == 1); + PushSize(&arena, MiB(1)); + PushSize(&arena, MiB(3)); + Assert(MemoryTrackingRecord.len == 2); + Unwind(&arena, 0); + Assert(MemoryTrackingRecord.len == 1); + Release(&arena); + Assert(MemoryTrackingRecord.len == 0); } } diff --git a/src/basic/basic_alloc.h b/src/basic/basic_alloc.h index a2f1302..5a2e398 100644 --- a/src/basic/basic_alloc.h +++ b/src/basic/basic_alloc.h @@ -51,6 +51,7 @@ struct BlockArena { U8 *end; BlockArenaNode *blocks; Allocator allocator; + bool when_unwinding_make_sure_to_keep_last_block_alive; operator Allocator() { return {BlockArenaAllocatorProc, this}; } }; @@ -95,17 +96,36 @@ API void Release(VirtualArena *arena); /////////////////////////////// // Scratch +extern thread_local VirtualArena ScratchArenas[4]; +API VirtualArena *GetScratchEx(VirtualArena **conflicts, int conflict_count); struct Scratch { - BlockArena arena = {}; - Scratch() {} - Scratch(BlockArena *conflict) {} - Scratch(BlockArena *c1, BlockArena *c2) {} - Scratch(Allocator conflict) {} - Scratch(Allocator c1, Allocator c2) {} - ~Scratch() { Release(&arena); } - operator BlockArena *() { return &arena; } - operator Allocator() { return arena; } + VirtualArena *arena; + U64 p; + Scratch() {arena = &ScratchArenas[0]; p = arena->len;} + Scratch(VirtualArena *conflict) { + VirtualArena *conf[] = {conflict}; + arena = GetScratchEx(conf, Lengthof(conf)); + p = arena->len; + } + Scratch(VirtualArena *c1, VirtualArena *c2) { + VirtualArena *conf[] = {c1, c2}; + arena = GetScratchEx(conf, Lengthof(conf)); + p = arena->len; + } + Scratch(Allocator conflict) { + VirtualArena *conf[] = {(VirtualArena *)conflict.object}; + arena = GetScratchEx(conf, Lengthof(conf)); + p = arena->len; + } + Scratch(Allocator c1, Allocator c2) { + VirtualArena *conf[] = {(VirtualArena *)c1.object, (VirtualArena *)c2.object}; + arena = GetScratchEx(conf, Lengthof(conf)); + p = arena->len; + } + ~Scratch() { SetLen(arena, p); } + operator VirtualArena *() { return arena; } + operator Allocator() { return *arena; } private: // @Note: Disable copy constructors, cause its error prone Scratch(Scratch &arena); @@ -113,4 +133,4 @@ struct Scratch { }; const int PAGE_SIZE = 4096; -const int DEFAULT_ALIGNMENT = sizeof(void *); \ No newline at end of file +const int DEFAULT_ALIGNMENT = sizeof(void *); diff --git a/src/basic/basic_os.cpp b/src/basic/basic_os.cpp index a9e5da9..7dba855 100644 --- a/src/basic/basic_os.cpp +++ b/src/basic/basic_os.cpp @@ -833,7 +833,7 @@ API Process SpawnProcess(String command_line, String working_dir, String write_s char *env = NULL; if (enviroment.len) { Int size = GetSize(enviroment) + enviroment.len + 1; - env = (char *)PushSize(scratch, size); + env = AllocArray(scratch, char, size); Int i = 0; For(enviroment) { MemoryCopy(env + i, it.data, it.len); diff --git a/src/basic/basic_string16.cpp b/src/basic/basic_string16.cpp index 1d26945..5ef3602 100644 --- a/src/basic/basic_string16.cpp +++ b/src/basic/basic_string16.cpp @@ -321,7 +321,7 @@ API String16 CutPostfix(String16 *string, int64_t len) { } API String16 Format16V(Allocator allocator, const char *data, va_list args1) { - Scratch scratch; + Scratch scratch(allocator); va_list args2; va_copy(args2, args1); int64_t len = stbsp_vsnprintf(0, 0, data, args2); @@ -335,7 +335,7 @@ API String16 Format16V(Allocator allocator, const char *data, va_list args1) { } API String16 Format16(Allocator allocator, const char *data, ...) { - Scratch scratch; + Scratch scratch(allocator); STRING_FORMAT(scratch, data, result); String16 result16 = ToString16(allocator, result); return result16; diff --git a/src/render/font.cpp b/src/render/font.cpp index 7faf020..1d40f2f 100644 --- a/src/render/font.cpp +++ b/src/render/font.cpp @@ -107,11 +107,11 @@ Glyph *GetGlyph(Font *font, uint32_t codepoint) { } Font CreateFont(Atlas *atlas, int32_t size, String path) { Allocator allocator = GetSystemAllocator(); - Scratch scratch; + Scratch scratch; - Font result = {}; + Font result = {}; result.glyphs.allocator = allocator; - String file = ReadFile(scratch, path); + String file = ReadFile(scratch, path); if (file.len == 0) { file = BakedInFont; Assert(file.len != 0); diff --git a/src/text_editor/buffer.cpp b/src/text_editor/buffer.cpp index 241eef0..ef46668 100644 --- a/src/text_editor/buffer.cpp +++ b/src/text_editor/buffer.cpp @@ -1313,13 +1313,11 @@ void RunBufferTest() { // Basic case make sure no leaks { - Scratch scratch; Buffer buffer = {}; InitBuffer(GetTrackingAllocator(), &buffer); RawReplaceText(&buffer, {}, u"Thing\nmeme"); RawReplaceText(&buffer, GetBufferEndAsRange(&buffer), u"\nnewThing"); DeinitBuffer(&buffer); - TrackingAllocatorCheck(); } // Testing Edit API and making sure no leaks @@ -1341,7 +1339,7 @@ void RunBufferTest() { String16 s = GetString(&buffer); Assert(s == u"t\nThings\nnewThing"); DeinitBuffer(&buffer); - TrackingAllocatorCheck(); + Assert(MemoryTrackingRecord.len == 0); } // Make sure no_history and no line_starts properly makes sure of these @@ -1371,7 +1369,7 @@ void RunBufferTest() { Assert(buffer.undo_stack.len == 0); Assert(buffer.undo_stack.data == 0); DeinitBuffer(&buffer); - TrackingAllocatorCheck(); + Assert(MemoryTrackingRecord.len == 0); } } RegisterFunction(&TestFunctions, RunBufferTest); diff --git a/src/text_editor/text_editor.cpp b/src/text_editor/text_editor.cpp index 07c0593..5abbb49 100644 --- a/src/text_editor/text_editor.cpp +++ b/src/text_editor/text_editor.cpp @@ -409,7 +409,6 @@ void OnCommand(Event event) { } if (event.kind == EVENT_TEXT_INPUT) { - Scratch scratch; String16 string16 = ToString16(scratch, event.text); Replace(active.view, string16); } @@ -725,6 +724,7 @@ extern char **environ; int main(int argc, char **argv) #endif { + InitScratch(); InitOS((OSErrorReport *)printf); #if _WIN32 int argc = __argc; diff --git a/src/text_editor/window.cpp b/src/text_editor/window.cpp index 757af79..24a8b19 100644 --- a/src/text_editor/window.cpp +++ b/src/text_editor/window.cpp @@ -117,8 +117,6 @@ View *WindowOpenBufferView(Window *new_parent_window, String name) { } void InitWindows() { - Scratch scratch; - CreateWind(); CommandWindowInit(); StatusWindowInit();