Fix bug and handle more errors

Fix bug where mprotect calls don't match the arena cap increase
(https://github.com/olemorud/arena-allocator/issues/1)

Handle syscall errors in arena_new()

Check for syscall errors in arena_delete() and switch from void to int
to return error status.
This commit is contained in:
2024-01-15 14:52:02 +01:00
parent 44a2982376
commit d11557a4fe
2 changed files with 58 additions and 18 deletions

View File

@@ -15,14 +15,16 @@ typedef struct arena {
/** /**
* Allocate a new arena. * Allocate a new arena.
* The underlying memory is allocated with mmap. * The underlying memory is allocated with mmap.
* Errors can be checked with `arena_new_failed()`
*/ */
arena_t arena_new(); arena_t arena_new();
/** /**
* Delete memory mapped for arena. * Delete memory mapped for arena.
* Should only be used with arenas from arena_new(). * 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. * Attach an arena to an existing memory region.
@@ -41,10 +43,21 @@ static inline void *arena_detatch(arena_t arena)
return arena.data; 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. * 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. * Allocate memory from an arena.

View File

@@ -10,6 +10,7 @@
#define ARENA_ALIGN (sizeof(void *)) #define ARENA_ALIGN (sizeof(void *))
#define ARENA_GROW_FACTOR 2UL #define ARENA_GROW_FACTOR 2UL
#define ARENA_OVERCOMMIT_SIZE (1UL << 40UL)
#ifndef NDEBUG #ifndef NDEBUG
#define arena_err(msg) \ #define arena_err(msg) \
@@ -18,53 +19,74 @@
#define arena_err(msg) #define arena_err(msg)
#endif #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( int ok = mprotect(
a->data + a->cap, a->data + a->cap,
a->cap * ARENA_GROW_FACTOR, new_cap - a->cap,
PROT_READ | PROT_WRITE); PROT_READ | PROT_WRITE
);
if (ok == -1) { if (ok == -1) {
arena_err("mprotect"); arena_err("mprotect");
return false; return false;
} }
a->cap *= ARENA_GROW_FACTOR; a->cap = new_cap;
return true; return true;
} }
inline void arena_reset(arena_t *a)
{
a->size = 0;
}
arena_t arena_new() arena_t arena_new()
{ {
size_t size = sysconf(_SC_PAGE_SIZE); size_t size = sysconf(_SC_PAGE_SIZE);
void *p = mmap(NULL, 1UL << 40UL, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (size == -1) {
mprotect(p, size, PROT_READ | PROT_WRITE); arena_err("sysconf");
if (p == MAP_FAILED) 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"); 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); 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 size = (size + ARENA_ALIGN - 1) & ~(ARENA_ALIGN - 1); // align
void *p = a->data + a->size; void *p = a->data + a->size;
a->size += size; a->size += size;
if (a->size > a->cap) { if (a->size > a->cap) {
if (!arena_grow(a)) if (!arena_grow(a, a->size))
return NULL; return NULL;
} }
return p; 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); void *p = arena_alloc(a, nmemb * size);
if (p == NULL) if (p == NULL)
@@ -73,9 +95,14 @@ void *arena_calloc(arena_t *a, size_t nmemb, size_t size)
return p; 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->cap = -1;
a->size = -1; a->size = -1;
return 0;
} }