remove limitations from PagingArena, small improvements

This commit is contained in:
Judah Caruso 2025-09-09 17:26:35 -06:00
parent a2ad1f9364
commit 8bc49b5b67
3 changed files with 135 additions and 76 deletions

View file

@ -26,11 +26,13 @@ ArenaEvent :: enum {
Save; Save;
Restore; Restore;
Report;
} }
/// ArenaPush pushes a value of type T to the current context.arena. /// ArenaPush pushes a value of type T to the current context.arena.
ArenaPush :: ($T: Type, loc := #caller_location) -> *T { 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. /// 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); 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 wraps the given arena, allowing it to be used with Jai's builtin Allocator system.
ArenaToAllocator :: (arena: *Arena) -> Allocator { ArenaToAllocator :: (arena: *Arena) -> Allocator {
return .{ proc = JaiAllocatorProc, data = arena }; return .{ proc = JaiAllocatorProc, data = arena };
@ -87,6 +94,7 @@ BumpData :: struct {
offset: int; offset: int;
} }
/// BumpArena is a simple linear Arena.
BumpArena :: (bump: *BumpData) -> Arena { BumpArena :: (bump: *BumpData) -> Arena {
return .{ proc = BumpArenaProc, data = bump }; return .{ proc = BumpArenaProc, data = bump };
} }
@ -171,6 +179,18 @@ BumpArenaProc :: (
if wm < 0 || wm >= bump.memory.count if wm < 0 || wm >= bump.memory.count
{ Panic("jc: BumpArena, restored invalid savepoint", loc = caller); } { Panic("jc: BumpArena, restored invalid savepoint", loc = caller); }
bump.offset = wm; 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; return null;
@ -185,60 +205,62 @@ PagingArenaProc :: (
caller: Source_Code_Location caller: Source_Code_Location
) -> *void { ) -> *void {
MaxPages :: 64; Page :: struct {
base: *u8;
PageArenaData :: struct { next: *Page;
pages: [MaxPages]Page; offset: int;
count: int;
in_use: int;
Page :: struct {
base: *u8;
offset: int;
}
} }
// @note(judah): page 0 has a special offset because it stores the header PageArenaData :: struct {
Page0Offset :: #run AlignForward(size_of(PageArenaData), align_of(PageArenaData)); 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; return p;
} }
data := arena_data.(*PageArenaData); data := arena_data.(*PageArenaData);
if event == { if event == {
case .Setup; case .Setup;
page := NewPage(null); // This is a special page that stores both the PageArenaData and Page 0
data = page.base.(*PageArenaData); mem := MemAcquirePage(.CommitImmediately);
header := mem.(*PageArenaData);
page := (mem + size_of(PageArenaData)).(*Page);
page.base = mem;
page.next = null;
page.offset = Page0Offset; page.offset = Page0Offset;
data.pages[0] = page;
data.in_use = 1; header.pages = page;
data.count = 1; header.current = page;
return data; return header;
case .Teardown; case .Teardown;
// @note(judah): release in reverse oreder so we don't release the memory page := data.pages;
// containing the header until we don't need it anymore. while page != null {
i := data.count - 1; next := page.next;
while i >= 0 { MemReleasePage(page.base, PageSize);
page := data.pages[i]; page = next;
if page.base != null
{ MemReleasePage(page.base, PageSize); }
i -= 1;
} }
case .Acquire; 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]; cur := data.current;
end := last.base + last.offset; end := cur.base + cur.offset;
if new_size == 0 if new_size == 0
{ return end; } { return end; }
@ -246,65 +268,87 @@ PagingArenaProc :: (
size := new_size + (ptr - end); size := new_size + (ptr - end);
// create or reuse page // create or reuse page
if last.offset + size > PageSize { if cur.offset + size > PageSize {
if data.in_use >= MaxPages page: *Page;
{ Panic("jc: PageArena, max page count"); } if cur.next != null {
page = cur.next;
page: *PageArenaData.Page;
if data.in_use < data.count {
page = *data.pages[data.in_use];
} }
else { else {
p := NewPage(data.pages[data.count - 1].base); cur.next = NewPage(cur.base);
data.pages[data.count] = p; page = cur.next;
page = *data.pages[data.count];
data.count += 1;
} }
data.in_use += 1;
ptr = page.base; ptr = page.base + page.offset;
size = new_size; size = new_size;
last = page; cur = page;
data.current = cur;
} }
last.offset += size; cur.offset += size;
return ptr; return ptr;
case .Reset; case .Reset;
for 0..data.count - 1 { page := data.pages;
page := *data.pages[it]; page.offset = Page0Offset;
if it == 0 { page = page.next;
page.offset = Page0Offset; while page != null {
} page.offset = PageNOffset;
else { page = page.next;
page.offset = 0;
}
} }
data.in_use = 1; data.current = data.pages;
case .Save; case .Save;
return data.in_use.(*void); return data.current.(*void);
case .Restore; 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 // @note(judah): when restoring, we need to reset every page
// because we could've created more pages inbetween save/restore // from the savepoint forward
for in_use_at_save..data.count - 1 { saved_page := old_ptr.(*Page);
p := *data.pages[it]; while saved_page != null {
if it == 0 { if saved_page == data.pages {
p.offset = Page0Offset; saved_page.offset = Page0Offset;
} }
else { 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; return null;
@ -326,10 +370,12 @@ TempArenaProc :: (
malloc :: (size: int) -> *void #c_call #foreign libc; malloc :: (size: int) -> *void #c_call #foreign libc;
free :: (ptr: *void) #c_call #foreign libc; free :: (ptr: *void) #c_call #foreign libc;
TempMemorySize :: (32 * Kilobyte) + size_of(BumpData);
if event == { if event == {
// @note(judah): allows the temp arena to initialize itself without relying on another arena to provide its memory. // @note(judah): allows the temp arena to initialize itself without relying on another arena to provide its memory.
case .Setup; case .Setup;
mcount := AlignForward(size_of(BumpData) + 32768).(int); mcount := AlignForward(TempMemorySize).(int);
memory := malloc(mcount).(*u8); memory := malloc(mcount).(*u8);
MemZero(memory, mcount); MemZero(memory, mcount);

View file

@ -201,18 +201,21 @@ DebugBuild :: #run -> bool {
PrintStackTrace :: () { PrintStackTrace :: () {
WriteStderrString("\n----- Stack Trace -----\n"); WriteStderrString("\n----- Stack Trace -----\n");
node := context.stack_trace.next; node := context.stack_trace;
while node != null { while node != null {
if node.info == null
{ break; }
loc := node.info.location; loc := node.info.location;
loc.line_number = node.line_number; loc.line_number = node.line_number;
WriteStderrLocation(loc); WriteStderrLocation(loc);
name := node.info.name; name := node.info.name;
if name.count <= 0 if name.count <= 0
{ name = "(unknown)"; } { name = "#run"; }
WriteStderrString(": in ", name, "\n"); WriteStderrString(": in ", name, "\n");
if node.next != null node = node.next;
{ node = node.next; }
} }
} }

View file

@ -9,6 +9,16 @@ This is what I'm doing day-to-day.
: notes, todos, etc. : 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 / 09.08.25
+ implemented PagingArena on mac + implemented PagingArena on mac