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:
@@ -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.
|
||||||
|
|||||||
59
src/arena.c
59
src/arena.c
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user