From 1ce128a5d5bb27339081bb65aa1a8b45bcd9b405 Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Mon, 19 Jan 2026 09:08:26 +0100 Subject: [PATCH] CoroutineLeakCheck --- src/basic/basic_alloc.h | 10 ++++- src/text_editor/coroutines.cpp | 10 +++++ src/text_editor/event.cpp | 19 ++++----- src/text_editor/plugin_project_management.cpp | 16 ++++++- src/text_editor/text_editor.cpp | 42 +++++++++++-------- 5 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/basic/basic_alloc.h b/src/basic/basic_alloc.h index 91f0d11..9e43444 100644 --- a/src/basic/basic_alloc.h +++ b/src/basic/basic_alloc.h @@ -47,6 +47,7 @@ struct BlockArena { U8 *end; BlockArenaNode *blocks; Allocator allocator; + int refs; // :CoroutineLeakCheck operator Allocator() { return {BlockArenaAllocatorProc, this}; } }; @@ -66,6 +67,7 @@ struct VirtualArena { size_t reserve; size_t commit; size_t align; + int refs; // :CoroutineLeakCheck operator Allocator() { void *ArenaAllocatorProc(void *object, int kind, void *p, size_t size); @@ -112,28 +114,32 @@ API VirtualArena *GetScratchEx(VirtualArena **conflicts, int conflict_count); struct Scratch { VirtualArena *arena; U64 p; - Scratch() {arena = &ScratchArenas[0]; p = arena->len;} + Scratch() {arena = &ScratchArenas[0]; p = arena->len; arena->refs += 1;} Scratch(VirtualArena *conflict) { VirtualArena *conf[] = {conflict}; arena = GetScratchEx(conf, Lengthof(conf)); p = arena->len; + arena->refs += 1; } Scratch(VirtualArena *c1, VirtualArena *c2) { VirtualArena *conf[] = {c1, c2}; arena = GetScratchEx(conf, Lengthof(conf)); p = arena->len; + arena->refs += 1; } Scratch(Allocator conflict) { VirtualArena *conf[] = {(VirtualArena *)conflict.object}; arena = GetScratchEx(conf, Lengthof(conf)); p = arena->len; + arena->refs += 1; } Scratch(Allocator c1, Allocator c2) { VirtualArena *conf[] = {(VirtualArena *)c1.object, (VirtualArena *)c2.object}; arena = GetScratchEx(conf, Lengthof(conf)); p = arena->len; + arena->refs += 1; } - ~Scratch() { SetLen(arena, p); } + ~Scratch() { SetLen(arena, p); arena->refs -= 1; } operator VirtualArena *() { return arena; } operator Allocator() { return *arena; } diff --git a/src/text_editor/coroutines.cpp b/src/text_editor/coroutines.cpp index b43831e..c0879b8 100644 --- a/src/text_editor/coroutines.cpp +++ b/src/text_editor/coroutines.cpp @@ -62,6 +62,16 @@ void CoUpdate(Event *event) { } } +#if DEBUG_BUILD + //:CoroutineLeakCheck + // Make sure we don't leak scratch arenas between coroutine boundaries. + // it leads to memory corruption! If you hit Assert here make sure you + // don't leak the scratch arena. + for (int i = 0; i < Lengthof(ScratchArenas); i += 1) { + Assert(ScratchArenas[i].refs == 0); + } +#endif + double took = GetTimeSeconds() - start; if (took > (0.016666 / 3.0)) { break; diff --git a/src/text_editor/event.cpp b/src/text_editor/event.cpp index 1c06438..a0160f6 100644 --- a/src/text_editor/event.cpp +++ b/src/text_editor/event.cpp @@ -472,35 +472,32 @@ inline void AddEvent(Array *events, Event ev) { Add(events, ev); } -Array GetEventsForFrame(Allocator allocator) { +void GetEventsForFrame(Array *events) { ProfileFunction(); - Array result = {allocator}; - if (EventPlayback.len) { - result = TightCopy(allocator, EventPlayback); - EventPlayback.len = 0; + For (EventPlayback) { + Add(events, it); } SDL_Event event; if (WaitForEventsState) { SDL_WaitEvent(&event); Event ev = TranslateSDLEvent(&event); - AddEvent(&result, ev); + AddEvent(events, ev); } while (SDL_PollEvent(&event)) { Event ev = TranslateSDLEvent(&event); - AddEvent(&result, ev); + AddEvent(events, ev); } - if (result.len == 0) { + if (events->len == 0) { Event event = {}; FillEventWithBasicData(&event); event.kind = EVENT_UPDATE; - Add(&result, event); + Add(events, event); } - Assert(result.len); - return result; + Assert(events->len); } diff --git a/src/text_editor/plugin_project_management.cpp b/src/text_editor/plugin_project_management.cpp index 40d8d27..5aa9efa 100644 --- a/src/text_editor/plugin_project_management.cpp +++ b/src/text_editor/plugin_project_management.cpp @@ -67,4 +67,18 @@ void CMD_SetProjectHere() { } } #endif -} RegisterCommand(CMD_SetProjectHere, "", "Sets the project directory, opens the project file etc."); \ No newline at end of file +} RegisterCommand(CMD_SetProjectHere, "", "Sets the project directory, opens the project file etc."); + +void CO_CreateProject(mco_coro *co) { + String16 project_name16 = QueryUserString(co, "Please tell me, how would you like to name your project?"); + String directory = GetPrimaryDirectory(); + { + Scratch scratch; + String project_name = ToString(scratch, project_name16); + String name = Format(scratch, "%S/%S", directory, project_name); + MakeDir(name); + Open(name); + } + + +} RegisterCoroutineCommand(CO_CreateProject, "", "Creates a project in current buffer directory with template files and all that, asks user for input etc."); \ No newline at end of file diff --git a/src/text_editor/text_editor.cpp b/src/text_editor/text_editor.cpp index 4674b78..9cf43d7 100644 --- a/src/text_editor/text_editor.cpp +++ b/src/text_editor/text_editor.cpp @@ -626,15 +626,17 @@ void Update(Event event) { ProfileFunction(); LayoutWindows(event.xwindow, event.ywindow); - Scratch scratch; - Array order = GetWindowZOrder(scratch); - For(order) { - if (!it->visible) { - continue; + { + Scratch scratch; + Array order = GetWindowZOrder(scratch); + For(order) { + if (!it->visible) { + continue; + } + View *view = GetView(it->active_view); + view->main_caret_on_begin_frame = view->carets[0]; + view->update_scroll = true; } - View *view = GetView(it->active_view); - view->main_caret_on_begin_frame = view->carets[0]; - view->update_scroll = true; } For (Windows) { @@ -670,7 +672,7 @@ void Update(Event event) { CoUpdate(&event); - For(IterateInReverse(&order)) { + For (Windows) { if (!it->visible) continue; View *view = GetView(it->active_view); UpdateScroll(it, !AreEqual(view->main_caret_on_begin_frame, view->carets[0]) && view->update_scroll); @@ -783,13 +785,13 @@ void Update(Event event) { GarbageCollect(); } +Array FrameEvents; void MainLoop() { ProfileFunction(); - Scratch scratch; FrameID += 1; - Array frame_events = GetEventsForFrame(scratch); - For(frame_events) { - + FrameEvents.len = 0; + GetEventsForFrame(&FrameEvents); + For (FrameEvents) { #if PLUGIN_RECORD_EVENTS if (it.kind != EVENT_UPDATE && !Testing) { Serialize(EventBuffer, &it); @@ -823,6 +825,7 @@ void MainLoop() { // This shouldn't matter to the state of the program, only appearance for // the user { + Scratch scratch; ProfileScope(SetWindowTitle); Window *window = GetActiveWind(); View *view = GetView(window->active_view); @@ -832,15 +835,18 @@ void MainLoop() { SDL_SetWindowTitle(SDLWindow, string.data); } - Event *event = GetLast(frame_events); + Event *event = GetLast(FrameEvents); SetMouseCursor(*event); LayoutWindows(event->xwindow, event->ywindow); // This is here to render changes in title bar size without a frame of delay BeginFrameRender(event->xwindow, event->ywindow); - Array order = GetWindowZOrder(scratch); - For(IterateInReverse(&order)) { - if (!it->visible) continue; - DrawWindow(it, *GetLast(frame_events)); + { + Scratch scratch; + Array order = GetWindowZOrder(scratch); + For(IterateInReverse(&order)) { + if (!it->visible) continue; + DrawWindow(it, *GetLast(FrameEvents)); + } } EndFrameRender(event->xwindow, event->ywindow, BackgroundColor); SDL_GL_SwapWindow(SDLWindow);