Created
May 25, 2024 00:43
-
-
Save skeeto/dac3317691836ad0836dad0655831163 to your computer and use it in GitHub Desktop.
Growing arena persisting commit level through scratch copies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define assert(c) while (!(c)) __builtin_unreachable() | |
#define new(a, n, t) (t *)alloc(a, n, sizeof(t), _Alignof(t)) | |
enum { PAGESIZE = 1<<12 }; | |
typedef signed int b32; | |
typedef signed int i32; | |
typedef char byte; | |
typedef signed long long iz; | |
typedef unsigned long long uz; | |
static byte *os_reserve(iz); | |
static b32 os_commit(byte *, iz); | |
typedef struct { | |
byte *beg; | |
byte *end; | |
byte **commit; | |
} arena; | |
static arena newarena(iz cap) | |
{ | |
arena r = {0}; | |
r.end = r.beg = os_reserve(cap); | |
if (r.beg) { | |
if (!os_commit(r.beg, PAGESIZE)) { | |
return r; | |
} | |
r.end += cap; | |
r.commit = (byte **)r.beg; | |
*r.commit = r.beg + PAGESIZE; | |
r.beg += sizeof(void *); | |
} | |
return r; | |
} | |
static byte *alloc(arena *a, iz count, iz size, iz align) | |
{ | |
assert(align <= PAGESIZE); // simplifying assumption | |
iz pad = -(uz)a->beg & (align - 1); | |
assert(count < (a->end - a->beg - pad)/size); // OOM: too large | |
a->beg += pad; // align for remainder | |
iz total = count * size; | |
iz shortfall = total - (*a->commit - a->beg); | |
if (shortfall > 0) { | |
iz npages = (shortfall + PAGESIZE - 1) / PAGESIZE; | |
b32 ok = os_commit(*a->commit, npages*PAGESIZE); | |
assert(ok); // OOM: commit failed | |
*a->commit += npages * PAGESIZE; | |
} | |
byte *r = a->beg; | |
a->beg += total; | |
return __builtin_memset(r, 0, total); | |
} | |
static i32 run(void) | |
{ | |
arena perm = newarena((iz)1<<40); | |
for (iz n = 0; n < 1<<10; n++) { | |
// Commits should only happen on first iteration despite a | |
// per-iteration scratch arena. That is, the commit level | |
// persists between copies of the arena struct. I placed a | |
// couple of GDB dprintf probes to test this. | |
arena scratch = perm; | |
for (iz i = 0; i < (iz)1<<16; i++) { | |
new(&scratch, 1<<4, int); | |
} | |
} | |
return 0; | |
} | |
#define W32(r) __declspec(dllimport) r __stdcall | |
W32(void) ExitProcess(i32); | |
W32(byte *) VirtualAlloc(byte *, iz, i32, i32); | |
static byte *os_reserve(iz cap) | |
{ | |
enum { MEM_RESERVE = 0x2000, PAGE_NOACCESS = 1 }; | |
return VirtualAlloc(0, cap, MEM_RESERVE, PAGE_NOACCESS); | |
} | |
static b32 os_commit(byte *p, iz len) | |
{ | |
enum { MEM_COMMIT = 0x1000, PAGE_READWRITE = 4 }; | |
return !!VirtualAlloc(p, len, MEM_COMMIT, PAGE_READWRITE); | |
} | |
void mainCRTStartup(void) | |
{ | |
i32 r = run(); | |
ExitProcess(r); | |
assert(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment