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.
* 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.

View File

@@ -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;
}