Allocator memes

This commit is contained in:
krzosa
2025-12-30 10:09:46 +01:00
parent 187bbcc5fc
commit a57ebb49be
8 changed files with 122 additions and 50 deletions

View File

@@ -148,13 +148,6 @@ API void *TrackingAllocatorProc(void *object, int kind, void *p, size_t size) {
return result; return result;
} }
API void TrackingAllocatorCheck() {
// For (MemoryTrackingRecord) {
// ReportConsolef("%s(%d): error: memory leak");
// }
Assert(MemoryTrackingRecord.len == 0);
}
API Allocator GetTrackingAllocator() { API Allocator GetTrackingAllocator() {
Allocator result = {TrackingAllocatorProc}; Allocator result = {TrackingAllocatorProc};
return result; return result;
@@ -267,8 +260,7 @@ API void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size) {
/////////////////////////////// ///////////////////////////////
// Block Arena // Block Arena
API void *PushSize(BlockArena *arena, size_t size) { void AddBlock(BlockArena *arena, size_t size = MiB(1)) {
if (size > (size_t)(arena->end - arena->start)) {
size_t block_size = MiB(1); size_t block_size = MiB(1);
if (size > block_size) { if (size > block_size) {
block_size = size; block_size = size;
@@ -276,11 +268,20 @@ API void *PushSize(BlockArena *arena, size_t size) {
if (arena->allocator.proc == NULL) { if (arena->allocator.proc == NULL) {
arena->allocator = GetSystemAllocator(); arena->allocator = GetSystemAllocator();
} }
BlockArenaNode *new_block = (BlockArenaNode *)AllocSize(arena->allocator, block_size + sizeof(BlockArenaNode)); BlockArenaNode *new_block = (BlockArenaNode *)AllocSize(arena->allocator, block_size + sizeof(BlockArenaNode) + 16);
Assert(GetAlignOffset((size_t)new_block->start, DEFAULT_ALIGNMENT) == 0); Assert(GetAlignOffset((size_t)new_block->start, DEFAULT_ALIGNMENT) == 0);
arena->start = new_block->start; arena->start = new_block->start;
new_block->end = arena->end = new_block->start + block_size; new_block->end = arena->end = new_block->start + block_size;
SLL_STACK_ADD(arena->blocks, new_block); 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)) {
AddBlock(arena, size);
} }
U8 *result = arena->start; U8 *result = arena->start;
Assert(GetAlignOffset((size_t)result, DEFAULT_ALIGNMENT) == 0); Assert(GetAlignOffset((size_t)result, DEFAULT_ALIGNMENT) == 0);
@@ -293,23 +294,31 @@ API void Release(BlockArena *arena) {
next = it->next; next = it->next;
Dealloc(arena->allocator, it); Dealloc(arena->allocator, it);
} }
bool safe = arena->when_unwinding_make_sure_to_keep_last_block_alive;
MemoryZero(arena, sizeof(BlockArena)); MemoryZero(arena, sizeof(BlockArena));
arena->when_unwinding_make_sure_to_keep_last_block_alive = safe;
} }
API void Unwind(BlockArena *arena, U8 *pos) { API void Unwind(BlockArena *arena, U8 *pos) {
bool contains = false; bool contains = false;
for (BlockArenaNode *it = arena->blocks, *next = NULL; it; it = next) { for (BlockArenaNode *it = arena->blocks, *next = NULL; it; it = next) {
next = it->next; next = it->next;
if ((pos >= it->start) && (pos < it->end)) { if ((pos >= it->start) && (pos <= it->end)) {
contains = true; contains = true;
break; break;
} else {
bool keep = arena->when_unwinding_make_sure_to_keep_last_block_alive && arena->blocks->next == NULL;
if (keep) {
pos = arena->blocks->start;
} else { } else {
arena->blocks = arena->blocks->next; arena->blocks = arena->blocks->next;
Dealloc(arena->allocator, it); 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->start = pos;
arena->end = arena->blocks ? arena->blocks->end : NULL;
} }
API void *BlockArenaAllocatorProc(void *object, int kind, void *p, size_t size) { 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; 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() { void RunArenaTest() {
Allocator memory_tracking_allocator = GetTrackingAllocator(); Allocator memory_tracking_allocator = GetTrackingAllocator();
{ {
@@ -335,7 +375,7 @@ void RunArenaTest() {
} }
} }
Release(&arena); Release(&arena);
TrackingAllocatorCheck(); Assert(MemoryTrackingRecord.len == 0);
} }
{ {
@@ -351,7 +391,7 @@ void RunArenaTest() {
Assert(arena.blocks[0].start == (U8 *)vals); Assert(arena.blocks[0].start == (U8 *)vals);
Unwind(&arena, NULL); Unwind(&arena, NULL);
Dealloc(arena.allocator, arena.blocks); Dealloc(arena.allocator, arena.blocks);
TrackingAllocatorCheck(); Assert(MemoryTrackingRecord.len == 0);
} }
{ {
@@ -372,7 +412,7 @@ void RunArenaTest() {
Assert(arena.start == p); Assert(arena.start == p);
Release(&arena); Release(&arena);
TrackingAllocatorCheck(); Assert(MemoryTrackingRecord.len == 0);
} }
{ {
@@ -381,6 +421,22 @@ void RunArenaTest() {
U8 *a = (U8 *)PushSize(&arena, KiB(2000)); U8 *a = (U8 *)PushSize(&arena, KiB(2000));
Assert((size_t)(arena.blocks[0].end - arena.blocks[0].start) == KiB(2000)); Assert((size_t)(arena.blocks[0].end - arena.blocks[0].start) == KiB(2000));
Release(&arena); 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);
} }
} }

View File

@@ -51,6 +51,7 @@ struct BlockArena {
U8 *end; U8 *end;
BlockArenaNode *blocks; BlockArenaNode *blocks;
Allocator allocator; Allocator allocator;
bool when_unwinding_make_sure_to_keep_last_block_alive;
operator Allocator() { return {BlockArenaAllocatorProc, this}; } operator Allocator() { return {BlockArenaAllocatorProc, this}; }
}; };
@@ -95,17 +96,36 @@ API void Release(VirtualArena *arena);
/////////////////////////////// ///////////////////////////////
// Scratch // Scratch
extern thread_local VirtualArena ScratchArenas[4];
API VirtualArena *GetScratchEx(VirtualArena **conflicts, int conflict_count);
struct Scratch { struct Scratch {
BlockArena arena = {}; VirtualArena *arena;
Scratch() {} U64 p;
Scratch(BlockArena *conflict) {} Scratch() {arena = &ScratchArenas[0]; p = arena->len;}
Scratch(BlockArena *c1, BlockArena *c2) {} Scratch(VirtualArena *conflict) {
Scratch(Allocator conflict) {} VirtualArena *conf[] = {conflict};
Scratch(Allocator c1, Allocator c2) {} arena = GetScratchEx(conf, Lengthof(conf));
~Scratch() { Release(&arena); } p = arena->len;
operator BlockArena *() { return &arena; } }
operator Allocator() { return arena; } 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 private: // @Note: Disable copy constructors, cause its error prone
Scratch(Scratch &arena); Scratch(Scratch &arena);

View File

@@ -833,7 +833,7 @@ API Process SpawnProcess(String command_line, String working_dir, String write_s
char *env = NULL; char *env = NULL;
if (enviroment.len) { if (enviroment.len) {
Int size = GetSize(enviroment) + enviroment.len + 1; Int size = GetSize(enviroment) + enviroment.len + 1;
env = (char *)PushSize(scratch, size); env = AllocArray(scratch, char, size);
Int i = 0; Int i = 0;
For(enviroment) { For(enviroment) {
MemoryCopy(env + i, it.data, it.len); MemoryCopy(env + i, it.data, it.len);

View File

@@ -321,7 +321,7 @@ API String16 CutPostfix(String16 *string, int64_t len) {
} }
API String16 Format16V(Allocator allocator, const char *data, va_list args1) { API String16 Format16V(Allocator allocator, const char *data, va_list args1) {
Scratch scratch; Scratch scratch(allocator);
va_list args2; va_list args2;
va_copy(args2, args1); va_copy(args2, args1);
int64_t len = stbsp_vsnprintf(0, 0, data, args2); 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, ...) { API String16 Format16(Allocator allocator, const char *data, ...) {
Scratch scratch; Scratch scratch(allocator);
STRING_FORMAT(scratch, data, result); STRING_FORMAT(scratch, data, result);
String16 result16 = ToString16(allocator, result); String16 result16 = ToString16(allocator, result);
return result16; return result16;

View File

@@ -1313,13 +1313,11 @@ void RunBufferTest() {
// Basic case make sure no leaks // Basic case make sure no leaks
{ {
Scratch scratch;
Buffer buffer = {}; Buffer buffer = {};
InitBuffer(GetTrackingAllocator(), &buffer); InitBuffer(GetTrackingAllocator(), &buffer);
RawReplaceText(&buffer, {}, u"Thing\nmeme"); RawReplaceText(&buffer, {}, u"Thing\nmeme");
RawReplaceText(&buffer, GetBufferEndAsRange(&buffer), u"\nnewThing"); RawReplaceText(&buffer, GetBufferEndAsRange(&buffer), u"\nnewThing");
DeinitBuffer(&buffer); DeinitBuffer(&buffer);
TrackingAllocatorCheck();
} }
// Testing Edit API and making sure no leaks // Testing Edit API and making sure no leaks
@@ -1341,7 +1339,7 @@ void RunBufferTest() {
String16 s = GetString(&buffer); String16 s = GetString(&buffer);
Assert(s == u"t\nThings\nnewThing"); Assert(s == u"t\nThings\nnewThing");
DeinitBuffer(&buffer); DeinitBuffer(&buffer);
TrackingAllocatorCheck(); Assert(MemoryTrackingRecord.len == 0);
} }
// Make sure no_history and no line_starts properly makes sure of these // 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.len == 0);
Assert(buffer.undo_stack.data == 0); Assert(buffer.undo_stack.data == 0);
DeinitBuffer(&buffer); DeinitBuffer(&buffer);
TrackingAllocatorCheck(); Assert(MemoryTrackingRecord.len == 0);
} }
} RegisterFunction(&TestFunctions, RunBufferTest); } RegisterFunction(&TestFunctions, RunBufferTest);

View File

@@ -409,7 +409,6 @@ void OnCommand(Event event) {
} }
if (event.kind == EVENT_TEXT_INPUT) { if (event.kind == EVENT_TEXT_INPUT) {
Scratch scratch;
String16 string16 = ToString16(scratch, event.text); String16 string16 = ToString16(scratch, event.text);
Replace(active.view, string16); Replace(active.view, string16);
} }
@@ -725,6 +724,7 @@ extern char **environ;
int main(int argc, char **argv) int main(int argc, char **argv)
#endif #endif
{ {
InitScratch();
InitOS((OSErrorReport *)printf); InitOS((OSErrorReport *)printf);
#if _WIN32 #if _WIN32
int argc = __argc; int argc = __argc;

View File

@@ -117,8 +117,6 @@ View *WindowOpenBufferView(Window *new_parent_window, String name) {
} }
void InitWindows() { void InitWindows() {
Scratch scratch;
CreateWind(); CreateWind();
CommandWindowInit(); CommandWindowInit();
StatusWindowInit(); StatusWindowInit();