Created
December 27, 2023 21:34
-
-
Save skeeto/2e86f52e13b8411cb77a3fc4241ac298 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// Gradual-commit arena demonstration | |
// This is free and unencumbered software released into the public domain. | |
#include <stddef.h> | |
#include <string.h> | |
static void *os_reserve(ptrdiff_t); | |
static _Bool os_commit(void *, ptrdiff_t); | |
#define ARENA_PAGESIZE ((ptrdiff_t)1<<26) | |
typedef struct { | |
char *begin; | |
char *commit; | |
char *end; | |
} arena; | |
static arena newarena(ptrdiff_t cap) | |
{ | |
arena a = {0}; | |
cap += -cap & (ARENA_PAGESIZE - 1); | |
a.begin = a.commit = a.end = os_reserve(cap); | |
if (a.begin) { | |
a.end += cap; | |
} | |
return a; | |
} | |
static void *alloc(arena *a, ptrdiff_t size, ptrdiff_t align, ptrdiff_t count) | |
{ | |
ptrdiff_t padding = -(size_t)a->begin & (align - 1); | |
ptrdiff_t committed = a->commit - a->begin; | |
if (count > (committed-padding)/size) { | |
ptrdiff_t reserved = a->end - a->begin; | |
if (count > (reserved-padding)/size) { | |
return 0; | |
} | |
ptrdiff_t needed = size*count + padding - committed; | |
needed += -needed & (ARENA_PAGESIZE - 1); | |
if (!os_commit(a->commit, needed)) { | |
return 0; | |
} | |
a->commit += needed; | |
} | |
void *ptr = a->begin + padding; | |
a->begin += padding + size*count; | |
return memset(ptr, 0, size*count); | |
} | |
// Test | |
static ptrdiff_t test(arena *a, unsigned long long rng) | |
{ | |
ptrdiff_t total = 0; | |
void *save = a->begin; | |
for (;;) { | |
rng = rng*0x3243f6a8885a308d + 1; | |
ptrdiff_t size = 1 + ((rng>>50) & ((1<<10) - 1)); | |
ptrdiff_t count = 1 + ((rng>>40) & ((1<<10) - 1)); | |
ptrdiff_t align = 1 << ((rng>>30) & ((1<< 2) - 1)); | |
if (!alloc(a, size, align, count)) { | |
break; | |
} | |
total += size * count; | |
} | |
a->begin = save; | |
return total; // dprintf LINE,"%f\n",total/1024./1024. | |
} | |
static void run(arena a) | |
{ | |
for (int i = 0; i < 8; i++) { | |
test(&a, i+1); | |
} | |
} | |
#ifdef _WIN32 | |
// $ cc -g3 -nostartfiles -o arena.exe arena.c | |
// $ cl /Z7 arena.c /link /subsystem:console kernel32.lib libvcruntime.lib | |
#define W32(r) __declspec(dllimport) r __stdcall | |
W32(void) ExitProcess(int); | |
W32(void *) VirtualAlloc(void *, ptrdiff_t, int, int); | |
#define MEM_COMMIT 0x1000 | |
#define MEM_RESERVE 0x2000 | |
#define PAGE_NOACCESS 0x0001 | |
#define PAGE_READWRITE 0x0004 | |
static void *os_reserve(ptrdiff_t cap) | |
{ | |
return VirtualAlloc(0, cap, MEM_RESERVE, PAGE_NOACCESS); | |
} | |
static _Bool os_commit(void *ptr, ptrdiff_t len) | |
{ | |
return VirtualAlloc(ptr, len, MEM_COMMIT, PAGE_READWRITE); | |
} | |
void mainCRTStartup(void) | |
{ | |
arena a = newarena((ptrdiff_t)1 << 33); | |
run(a); | |
ExitProcess(0); | |
} | |
#else // POSIX | |
// $ cc -g3 -o arena arena.c | |
#include <sys/mman.h> | |
static void *os_reserve(ptrdiff_t cap) | |
{ | |
void *r = mmap(0, cap, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); | |
return r==MAP_FAILED ? 0 : r; | |
} | |
static _Bool os_commit(void *ptr, ptrdiff_t len) | |
{ | |
return !mprotect(ptr, len, PROT_READ|PROT_WRITE); | |
} | |
int main(void) | |
{ | |
arena a = newarena((ptrdiff_t)1 << 33); | |
run(a); | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment