From e5fa743d854ec0b57ff65fa314b3e8f68a37454d Mon Sep 17 00:00:00 2001 From: Ole Morud Date: Sun, 4 Jun 2023 12:33:44 +0200 Subject: [PATCH] Add arena_free() --- Makefile | 30 +++++++++-- include/arena.h | 13 +++-- src/arena.c | 136 +++++++++++++++++++++++++++++++++++------------ src/test_arena.c | 36 ++++++++----- 4 files changed, 160 insertions(+), 55 deletions(-) diff --git a/Makefile b/Makefile index a264171..6b283bb 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,28 @@ -CC := gcc -CFLAGS := -g -O3 -std=c2x -Wall -Wextra -Wpedantic -Werror -Iinclude +all: + +BUILD ?= debug +COMPILER ?= gcc + +# ==== set compiler flags ==== +# credits Maxim Egorushkin: +# https://stackoverflow.com/questions/48791883/best-practice-for-building-a-make-file/48793058#48793058 + +CC := $(COMPILER) + +# -fsanitize={address,undefined} +CFLAGS.gcc.debug := -Og -ggdb -pg -fsanitize=address -fno-omit-frame-pointer +CFLAGS.gcc.release := -O3 -g -march=native -DNDEBUG +CFLAGS.gcc := -fanalyzer + +CFLAGS.clang.debug :=-O0 -g3 -DBACKTRACE -rdynamic +CFLAGS.clang.release :=-O3 -g -march=native -DNDEBUG +CFLAGS.clang := + +CFLAGS := ${CFLAGS.${COMPILER}} ${CFLAGS.${COMPILER}.${BUILD}} \ + -Iinclude \ + -std=c2x \ + -Wall -Wextra -Wpedantic -Werror BUILD_DIR := build BIN_DIR := lib @@ -9,6 +31,8 @@ TEST_DIR := test OBJS := $(BUILD_DIR)/arena.o FPIC_OBJS := $(BUILD_DIR)/fpic/arena.o +all : static + static : $(BIN_DIR)/libarena.a dynamic : $(BIN_DIR)/libarena.so @@ -22,7 +46,7 @@ $(BIN_DIR)/libarena.a : $(OBJS) | $(BIN_DIR) ar cr $@ $^ $(BIN_DIR)/libarena.so : $(FPIC_OBJS) | $(BIN_DIR) - $(CC) -shared -o $@ $^ + $(CC) -o $@ -shared $(CFLAGS) $^ $(BUILD_DIR)/%.o : src/%.c | $(BUILD_DIR) $(CC) -o $@ -c $(CFLAGS) $< diff --git a/include/arena.h b/include/arena.h index 6a46769..68b5e2f 100644 --- a/include/arena.h +++ b/include/arena.h @@ -3,19 +3,22 @@ #define ARENA_H #include // ptrdiff_t +#include // uintptr_t -typedef unsigned char byte_t; +#define _WORD_SIZE (sizeof(intptr_t)) +// linked list terminology: +// https://web.archive.org/web/20230604145332/https://i.stack.imgur.com/2FbXf.png typedef struct arena { - size_t next, prev, cap; - void* data; -} __attribute__((aligned(sizeof(void*)))) arena_t; + struct page *head, *last; +} __attribute__((aligned(_WORD_SIZE))) arena_t; -arena_t arena_new(); +arena_t arena_new(void); void arena_reset(arena_t* a); void* arena_alloc(arena_t* a, size_t len); void* arena_calloc(arena_t* a, size_t nmemb, size_t size); void* arena_realloc_tail(arena_t* a, size_t len); void arena_delete(arena_t* a); +void arena_free(arena_t* a, void* p); #endif diff --git a/src/arena.c b/src/arena.c index 3171a8b..5af820e 100644 --- a/src/arena.c +++ b/src/arena.c @@ -7,79 +7,147 @@ #include #include -#include - // (sizeof(intptr_t) isn't guaranteed to be the machine word size but on most // compilers it is) -#define WORD_SIZE (sizeof(intptr_t)) +#define SYS_PAGE_SIZE ((size_t)sysconf(_SC_PAGE_SIZE)) -#define ARENA_SIZE ((size_t)(1 * sysconf(_SC_PAGE_SIZE))) +#define BIG_PAGE (SYS_PAGE_SIZE + 1) -arena_t arena_new() +typedef unsigned char byte_t; + +struct page { + void* data; + size_t offset, prev_offset; + struct page* next; +}; + +// linked list terminology: +// https://web.archive.org/web/20230604145332/https://i.stack.imgur.com/2FbXf.png + +arena_t arena_new(void) { - arena_t a = { - .next = 0, - .prev = 0, - .cap = ARENA_SIZE, - .data = malloc(ARENA_SIZE) - }; + struct page* p = malloc(sizeof *p); - if (a.data == NULL) + if (p == NULL) exit(errno); + p->next = NULL; + p->offset = 0; + p->prev_offset = 0; + p->data = malloc(SYS_PAGE_SIZE); + + if (p->data == NULL) + exit(errno); + + arena_t a = { + .head = p, + .last = p, + }; + return a; } -void _arena_realloc_or_panic(arena_t* a, size_t len) +void _arena_new_page(arena_t* a, size_t size) { - // TODO: gracefully recover. - // although unlikely to be recoverable in most use cases - a->data = realloc(a->data, len); + /* potentially reuse page from previously + reset arena */ + if (a->head->next != NULL) { + a->head = a->head->next; + a->head->offset = 0; + a->head->prev_offset = 0; + return; + } - if (a->data == NULL) + void* tmp = calloc(1, sizeof *(a->head->next)); + + if (tmp == NULL) exit(errno); - a->cap = len; + a->head->next = tmp; + + a->head = a->head->next; + a->head->data = malloc(size); + + if (a->head->data == NULL) + exit(errno); } void arena_reset(arena_t* a) { - a->next = 0; - a->prev = 0; + a->head = a->last; + a->head->offset = 0; + a->head->prev_offset = 0; } -void* arena_alloc(arena_t* a, size_t len) +void* _arena_big_alloc(arena_t* a, size_t size) { - // align len to machine word size - len = (len + WORD_SIZE - 1) & ~(WORD_SIZE - 1); - fprintf(stderr, "allocating %zu bytes\n", len); + _arena_new_page(a, size); + a->head->offset = BIG_PAGE; + a->head->prev_offset = BIG_PAGE; - a->prev = a->next; - a->next += len; + return a->head->data; +} - if (a->next > a->cap) - _arena_realloc_or_panic(a, a->cap * 2); +void* arena_alloc(arena_t* a, size_t size) +{ + if (size > SYS_PAGE_SIZE) + return _arena_big_alloc(a, size); - return (byte_t*)(a->data) + a->prev; + // align size to machine word size + size = (size + _WORD_SIZE - 1) & ~(_WORD_SIZE - 1); + + if (a->head->offset > SYS_PAGE_SIZE - size) { + _arena_new_page(a, SYS_PAGE_SIZE); + return arena_alloc(a, size); + } + + a->head->prev_offset = a->head->offset; + a->head->offset += size; + + return (byte_t*)(a->head->data) + a->head->prev_offset; } void* arena_calloc(arena_t* a, size_t nmemb, size_t size) { void* p = arena_alloc(a, nmemb * size); - memset((byte_t*)(a->data) + a->prev, 0, size); + memset(p, 0, nmemb * size); return p; } void* arena_realloc_tail(arena_t* a, size_t len) { - a->next = a->prev; + if (a->head->offset == BIG_PAGE) { + void* tmp = realloc(a->head->data, len); + + if (tmp == NULL) + exit(errno); + + a->head->data = tmp; + + return tmp; + } + + a->head->offset = a->head->prev_offset; return arena_alloc(a, len); } void arena_delete(arena_t* a) { - free(a->data); - arena_reset(a); - a->data = NULL; + struct page* p = a->last; + + while (p != NULL) { + struct page* next = p->next; + free(p->data); + free(p); + p = next; + } +} + +// included for completeness +void arena_free(arena_t* a, void* p) +{ + (void)p; + (void)a; + return; } diff --git a/src/test_arena.c b/src/test_arena.c index 93c66f4..55d34fe 100644 --- a/src/test_arena.c +++ b/src/test_arena.c @@ -7,26 +7,36 @@ #include #include #include - -static struct arena default_arena = { 0 }; +#include int main() { - default_arena = arena_new(); + arena_t default_arena = arena_new(); - printf("\nAttempt to do allocations of 1 byte, cap is %zu, ", default_arena.cap); + printf("\nAttempting to allocate and write to 1 byte 1024 times"); + for (size_t i = 0; i < 1024; i++) { + char* c = arena_alloc(&default_arena, sizeof *c); - size_t i; - for (i = 0; default_arena.next < default_arena.cap; i++) { - char* c = arena_alloc(&default_arena, 1); - *c = i & 0xff; - if (c == NULL) { + if (c == NULL) err(EXIT_FAILURE, "failed to allocate memory"); - } - } - printf("did %zu allocations", i); - printf("\n OK"); + *c = i & 0xFF; + } + printf("\n OK!\n"); + + printf("\nAttempting to allocate and write to `_SC_PAGESIZE+1` bytes"); + size_t psz = sysconf(_SC_PAGESIZE) + 1; + char* c = arena_alloc(&default_arena, psz); + + if (c == NULL) + err(EXIT_FAILURE, "failed to allocate memory"); + + for (size_t i = 0; i < psz; i++) + *c = i & 0xFF; + + printf("\n OK!\n"); + + arena_delete(&default_arena); return 0; }