From 8bc49b5b67043e6acd19964fcfecf29272524059 Mon Sep 17 00:00:00 2001 From: Judah Caruso Date: Tue, 9 Sep 2025 17:26:35 -0600 Subject: [PATCH] remove limitations from PagingArena, small improvements --- +internal/arenas.jai | 190 ++++++++++++++++++++++++++---------------- +internal/builtin.jai | 11 ++- PLAN.Judah | 10 +++ 3 files changed, 135 insertions(+), 76 deletions(-) diff --git a/+internal/arenas.jai b/+internal/arenas.jai index 27557a8..9d195bb 100644 --- a/+internal/arenas.jai +++ b/+internal/arenas.jai @@ -26,11 +26,13 @@ ArenaEvent :: enum { Save; Restore; + + Report; } /// ArenaPush pushes a value of type T to the current context.arena. ArenaPush :: ($T: Type, loc := #caller_location) -> *T { - return ArenaPushSize(size_of(T), loc = loc).(*T); + return ArenaPushSize(size_of(T), align_of(T), loc = loc).(*T); } /// ArenaPushSize pushes size bytes to the current context.arena. @@ -72,6 +74,11 @@ ArenaRelease :: (loc := #caller_location) { context.arena.proc(.Teardown, context.arena.data, 0, 0, 0, null, null, loc); } +ArenaReport :: (loc := #caller_location) { + TrySetupArena(*context.arena, loc); + context.arena.proc(.Report, context.arena.data, 0, 0, 0, null, null, loc); +} + /// ArenaToAllocator wraps the given arena, allowing it to be used with Jai's builtin Allocator system. ArenaToAllocator :: (arena: *Arena) -> Allocator { return .{ proc = JaiAllocatorProc, data = arena }; @@ -87,6 +94,7 @@ BumpData :: struct { offset: int; } +/// BumpArena is a simple linear Arena. BumpArena :: (bump: *BumpData) -> Arena { return .{ proc = BumpArenaProc, data = bump }; } @@ -171,6 +179,18 @@ BumpArenaProc :: ( if wm < 0 || wm >= bump.memory.count { Panic("jc: BumpArena, restored invalid savepoint", loc = caller); } bump.offset = wm; + + case .Report; + WriteString("\n--- BumpArena Report ---"); + + WriteString("\nMem Max: "); + WriteNumber(bump.memory.count); + + WriteString("\nMem Used: "); + WriteNumber(bump.offset); + + WriteString("\n--- BumpArena Report ---\n\n"); + } return null; @@ -185,60 +205,62 @@ PagingArenaProc :: ( caller: Source_Code_Location ) -> *void { - MaxPages :: 64; - - PageArenaData :: struct { - pages: [MaxPages]Page; - count: int; - in_use: int; - - Page :: struct { - base: *u8; - offset: int; - } + Page :: struct { + base: *u8; + next: *Page; + offset: int; } - // @note(judah): page 0 has a special offset because it stores the header - Page0Offset :: #run AlignForward(size_of(PageArenaData), align_of(PageArenaData)); + PageArenaData :: struct { + pages: *Page; + current: *Page; + } + + PageNOffset :: size_of(Page); + Page0Offset :: size_of(PageArenaData) + PageNOffset; + + NewPage :: inline (previous_base: *void) -> *Page { + mem := MemAcquirePage(.CommitImmediately); + + p := mem.(*Page); + p.base = mem; + p.next = null; + p.offset = PageNOffset; - NewPage :: inline (previous_base: *void) -> PageArenaData.Page { - p: PageArenaData.Page = ---; - p.base = MemAcquirePage(.CommitImmediately); - p.offset = 0; return p; } data := arena_data.(*PageArenaData); if event == { case .Setup; - page := NewPage(null); - data = page.base.(*PageArenaData); + // This is a special page that stores both the PageArenaData and Page 0 + mem := MemAcquirePage(.CommitImmediately); + header := mem.(*PageArenaData); + page := (mem + size_of(PageArenaData)).(*Page); + page.base = mem; + page.next = null; page.offset = Page0Offset; - data.pages[0] = page; - data.in_use = 1; - data.count = 1; + header.pages = page; + header.current = page; - return data; + return header; case .Teardown; - // @note(judah): release in reverse oreder so we don't release the memory - // containing the header until we don't need it anymore. - i := data.count - 1; - while i >= 0 { - page := data.pages[i]; - if page.base != null - { MemReleasePage(page.base, PageSize); } - - i -= 1; + page := data.pages; + while page != null { + next := page.next; + MemReleasePage(page.base, PageSize); + page = next; } case .Acquire; - Assert(data.count != 0, "jc: PageArena, no pages exist"); + Assert(data.pages != null, "jc: PageArena, no pages exist"); + Assert(data.current != null, "jc: PageArena, no current page"); - last := *data.pages[data.in_use - 1]; - end := last.base + last.offset; + cur := data.current; + end := cur.base + cur.offset; if new_size == 0 { return end; } @@ -246,65 +268,87 @@ PagingArenaProc :: ( size := new_size + (ptr - end); // create or reuse page - if last.offset + size > PageSize { - if data.in_use >= MaxPages - { Panic("jc: PageArena, max page count"); } - - page: *PageArenaData.Page; - if data.in_use < data.count { - page = *data.pages[data.in_use]; + if cur.offset + size > PageSize { + page: *Page; + if cur.next != null { + page = cur.next; } else { - p := NewPage(data.pages[data.count - 1].base); - data.pages[data.count] = p; - page = *data.pages[data.count]; - - data.count += 1; + cur.next = NewPage(cur.base); + page = cur.next; } - data.in_use += 1; - ptr = page.base; + ptr = page.base + page.offset; size = new_size; - last = page; + cur = page; + + data.current = cur; } - last.offset += size; + cur.offset += size; return ptr; case .Reset; - for 0..data.count - 1 { - page := *data.pages[it]; + page := data.pages; + page.offset = Page0Offset; - if it == 0 { - page.offset = Page0Offset; - } - else { - page.offset = 0; - } + page = page.next; + while page != null { + page.offset = PageNOffset; + page = page.next; } - data.in_use = 1; + data.current = data.pages; case .Save; - return data.in_use.(*void); + return data.current.(*void); case .Restore; - in_use_at_save := old_ptr.(int); + if old_ptr == null + { Panic("jc: PagingArena, restore point was invalid"); } - // @note(judah): We need to restore from the savepoint to count - // because we could've created more pages inbetween save/restore - for in_use_at_save..data.count - 1 { - p := *data.pages[it]; - if it == 0 { - p.offset = Page0Offset; + // @note(judah): when restoring, we need to reset every page + // from the savepoint forward + saved_page := old_ptr.(*Page); + while saved_page != null { + if saved_page == data.pages { + saved_page.offset = Page0Offset; } else { - p.offset = 0; + saved_page.offset = PageNOffset; } + + saved_page = saved_page.next; } - data.in_use = in_use_at_save; + case .Report; + pages := 0; + allocated := 0; + in_use := 0; + + page := data.pages; + while page != null { + pages += 1; + allocated += PageSize; + in_use += page.offset; + + page = page.next; + } + + WriteString("\n--- PagingArena Report ---"); + WriteString("\nPages: "); + WriteNumber(pages); + + WriteString("\nMem Used: "); + WriteNumber(in_use); + + WriteString("\nMem Allocated: "); + WriteNumber(allocated); + WriteString(" (PageSize * N Pages)"); + + + WriteString("\n--- PagingArena Report ---\n\n"); } return null; @@ -326,10 +370,12 @@ TempArenaProc :: ( malloc :: (size: int) -> *void #c_call #foreign libc; free :: (ptr: *void) #c_call #foreign libc; + TempMemorySize :: (32 * Kilobyte) + size_of(BumpData); + if event == { // @note(judah): allows the temp arena to initialize itself without relying on another arena to provide its memory. case .Setup; - mcount := AlignForward(size_of(BumpData) + 32768).(int); + mcount := AlignForward(TempMemorySize).(int); memory := malloc(mcount).(*u8); MemZero(memory, mcount); diff --git a/+internal/builtin.jai b/+internal/builtin.jai index 0568b99..c4c9879 100644 --- a/+internal/builtin.jai +++ b/+internal/builtin.jai @@ -201,18 +201,21 @@ DebugBuild :: #run -> bool { PrintStackTrace :: () { WriteStderrString("\n----- Stack Trace -----\n"); - node := context.stack_trace.next; + node := context.stack_trace; while node != null { + if node.info == null + { break; } + loc := node.info.location; loc.line_number = node.line_number; WriteStderrLocation(loc); name := node.info.name; if name.count <= 0 - { name = "(unknown)"; } + { name = "#run"; } + WriteStderrString(": in ", name, "\n"); - if node.next != null - { node = node.next; } + node = node.next; } } diff --git a/PLAN.Judah b/PLAN.Judah index bdc69c9..66b22af 100644 --- a/PLAN.Judah +++ b/PLAN.Judah @@ -8,6 +8,16 @@ This is what I'm doing day-to-day. < decided against on a later day : notes, todos, etc. + +/ 09.09.25 + ++ PagingArena needs higher max memory ++ added ArenaReport ++ fixed PrintStackTrace ++ add align_of(T) to ArenaPush + +: should TempArena get more than 32k? + / 09.08.25