diff --git a/include/arena.h b/include/arena.h index d24d51c..543cb52 100644 --- a/include/arena.h +++ b/include/arena.h @@ -15,14 +15,16 @@ typedef struct arena { /** * Allocate a new arena. * The underlying memory is allocated with mmap. + * Errors can be checked with `arena_new_failed()` */ arena_t arena_new(); /** * Delete memory mapped for arena. * Should only be used with arenas from arena_new(). + * Returns 0 on success, -1 on failure */ -void arena_delete(arena_t *a); +int arena_delete(arena_t *a); /** * Attach an arena to an existing memory region. @@ -41,10 +43,21 @@ static inline void *arena_detatch(arena_t arena) return arena.data; } +/** + * Returns true if creating new arena failed + */ +static inline bool arena_new_failed(arena_t *a) +{ + return a->data == NULL; +} + /** * Reset an arena. */ -void arena_reset(arena_t *a); +static inline void arena_reset(arena_t *a) +{ + a->size = 0; +} /** * Allocate memory from an arena. diff --git a/src/arena.c b/src/arena.c index 1e2952c..5a6dcf0 100644 --- a/src/arena.c +++ b/src/arena.c @@ -10,6 +10,7 @@ #define ARENA_ALIGN (sizeof(void *)) #define ARENA_GROW_FACTOR 2UL +#define ARENA_OVERCOMMIT_SIZE (1UL << 40UL) #ifndef NDEBUG #define arena_err(msg) \ @@ -18,53 +19,74 @@ #define arena_err(msg) #endif -static bool arena_grow(struct arena *a) +static bool arena_grow(struct arena *a, size_t min_size) { + size_t new_cap = a->cap * 2; + while (new_cap < min_size) { + new_cap *= 2; + } + int ok = mprotect( a->data + a->cap, - a->cap * ARENA_GROW_FACTOR, - PROT_READ | PROT_WRITE); + new_cap - a->cap, + PROT_READ | PROT_WRITE + ); if (ok == -1) { arena_err("mprotect"); return false; } - a->cap *= ARENA_GROW_FACTOR; + a->cap = new_cap; return true; } -inline void arena_reset(arena_t *a) -{ - a->size = 0; -} arena_t arena_new() { size_t size = sysconf(_SC_PAGE_SIZE); - void *p = mmap(NULL, 1UL << 40UL, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - mprotect(p, size, PROT_READ | PROT_WRITE); - if (p == MAP_FAILED) + if (size == -1) { + arena_err("sysconf"); + goto sysconf_failed; + } + + void *p = mmap(NULL, ARENA_OVERCOMMIT_SIZE, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) { arena_err("mmap"); + goto mmap_failed; + } + + int ok = mprotect(p, size, PROT_READ | PROT_WRITE); + if (ok == -1) { + arena_err("mprotect"); + goto mprotect_failed; + } return arena_attach(p, size); + +mprotect_failed: + munmap(p, ARENA_OVERCOMMIT_SIZE); +mmap_failed: +sysconf_failed: + return (arena_t) { 0 }; } -void *arena_alloc(arena_t *a, size_t size) +void* arena_alloc(arena_t *a, size_t size) { size = (size + ARENA_ALIGN - 1) & ~(ARENA_ALIGN - 1); // align void *p = a->data + a->size; a->size += size; if (a->size > a->cap) { - if (!arena_grow(a)) + if (!arena_grow(a, a->size)) return NULL; } return p; } -void *arena_calloc(arena_t *a, size_t nmemb, size_t size) +void* arena_calloc(arena_t *a, size_t nmemb, size_t size) { void *p = arena_alloc(a, nmemb * size); if (p == NULL) @@ -73,9 +95,14 @@ void *arena_calloc(arena_t *a, size_t nmemb, size_t size) return p; } -void arena_delete(struct arena *a) +int arena_delete(struct arena *a) { - munmap(a->data, a->cap); + int ok = munmap(a->data, a->cap); + if (ok == -1) { + arena_err("munmap"); + return -1; + } a->cap = -1; a->size = -1; + return 0; }