Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf964c4dcc | ||
|
|
b3638c3c00 | ||
|
|
1fd96ff1ce | ||
|
|
0cde95de64 | ||
|
|
ac8193eb8b | ||
|
|
279618f52d | ||
|
|
90f72e9593 | ||
|
|
c71788ba72 | ||
|
|
f8c0db9937 | ||
|
|
bf73807e40 | ||
|
|
680936296f | ||
|
|
7ccb5bed74 | ||
|
|
42cb5d0c4e | ||
|
|
98b67e1a53 | ||
|
|
846ef41cd4 | ||
|
|
e0a98b0f08 | ||
|
|
22fb643c0f | ||
|
|
d5fea00170 | ||
|
|
1fad6fed5d | ||
|
|
e0e363a189 | ||
|
|
b35f63ccc5 | ||
|
|
6d9059aefc |
@@ -1,10 +1,2 @@
|
||||
|
||||
BasedOnStyle: WebKit
|
||||
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveDeclarations: true
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: false
|
||||
PointerAlignment: Right
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
obj/
|
||||
test/
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
build/
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project("ArenaAllocator"
|
||||
VERSION 1.0
|
||||
LANGUAGES C
|
||||
DESCRIPTION "Arena Allocator in C"
|
||||
)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
message(STATUS "Building tests")
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
72
Makefile
Normal file
72
Makefile
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
tests : test/test_arena
|
||||
|
||||
$(TEST_DIR)/test_arena : src/test_arena.c $(BIN_DIR)/libarena.a | $(TEST_DIR)
|
||||
$(CC) -o $@ $(CFLAGS) $^
|
||||
|
||||
$(BIN_DIR)/libarena.a : $(OBJS) | $(BIN_DIR)
|
||||
ar cr $@ $^
|
||||
|
||||
$(BIN_DIR)/libarena.so : $(FPIC_OBJS) | $(BIN_DIR)
|
||||
$(CC) -o $@ -shared $(CFLAGS) $^
|
||||
|
||||
$(BUILD_DIR)/%.o : src/%.c | $(BUILD_DIR)
|
||||
$(CC) -o $@ -c $(CFLAGS) $<
|
||||
|
||||
$(BUILD_DIR)/fpic/%.o : src/%.c | $(BUILD_DIR)/fpic
|
||||
$(CC) -o $@ -c -fPIC $(CFLAGS) $<
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(BUILD_DIR)/fpic:
|
||||
mkdir -p $@
|
||||
|
||||
$(BIN_DIR) :
|
||||
mkdir -p $@
|
||||
|
||||
$(TEST_DIR) :
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(BIN_DIR) $(TEST_DIR)
|
||||
|
||||
.PHONY: clean $(BUILD_DIR) test bin all
|
||||
64
README.md
64
README.md
@@ -1,65 +1,13 @@
|
||||
|
||||
# Arena Allocator
|
||||
|
||||
Arena allocators are region based allocators that tie many allocations to a
|
||||
single region of memory. Benefits are massivly simplified allocation and
|
||||
deallocation for complex structures, increased performance due to improved
|
||||
cache locality, and reduced memory fragmentation as long as individual items
|
||||
don't need to be deallocated. For programs that need to micro-manage individual
|
||||
allocations this is not an ideal solution.
|
||||
|
||||
Deallocating a region of memory with arenas is extremely fast, because the
|
||||
arena length is just set to 0. Allocating memory is also extremely fast. This
|
||||
implementation also grows the arena on demand using mmap and mprotect.
|
||||
Extending the library with a new type of allocation strategy should be easy.
|
||||
|
||||
# Reference
|
||||
|
||||
|
||||
## `arena_t arena_new()`
|
||||
`arena_t* arena_new()`
|
||||
|
||||
Allocate a new arena.
|
||||
Initializes and returns new arena
|
||||
|
||||
If `arena_new()` fails, the struct member `data` is set to NULL. This can be
|
||||
checked with `arena_new_failed()`
|
||||
`void arena_reset(arena_t *a)`
|
||||
|
||||
Free memory allocated in arena
|
||||
|
||||
## `bool arena_new_failed(arena_t *a)`
|
||||
|
||||
Returns true if creating a new arena failed
|
||||
|
||||
|
||||
## `int arena_delete(arena_t *a)`
|
||||
|
||||
Delete underlying buffer of arena. Should only be used with arenas from
|
||||
`arena_new()`.
|
||||
|
||||
Returns 0 on success, -1 on failure
|
||||
|
||||
|
||||
## `arena_t arena_attach(void *ptr, size_t size)`
|
||||
|
||||
Attach an arena to an existing memory region. The arena will not expand if
|
||||
capacity is exceeded.
|
||||
|
||||
|
||||
## `void *arena_detatch(arena_t arena)`
|
||||
|
||||
Detach an arena from an existing memory region. Returns the underlying data.
|
||||
|
||||
|
||||
## `void arena_reset(arena_t *a)`
|
||||
|
||||
Resets an arena.
|
||||
|
||||
|
||||
## `void *arena_alloc(arena_t *a, size_t len)`
|
||||
|
||||
Allocate memory with an arena. Returns NULL and sets errno on failure.
|
||||
|
||||
|
||||
## `void *arena_calloc(arena_t *a, size_t nmemb, size_t size)`
|
||||
|
||||
Allocate and zero memory with an arena. Returns NULL and sets errno on
|
||||
failure.
|
||||
`void* arena_alloc(arena_t *a, size_t len)`
|
||||
|
||||
Allocate new memory using arena
|
||||
|
||||
@@ -1,81 +1,24 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef ARENA_H
|
||||
#define ARENA_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stddef.h> // ptrdiff_t
|
||||
#include <stdint.h> // uintptr_t
|
||||
|
||||
// flags = 0 should be considered the default
|
||||
enum arena_flags {
|
||||
ARENA_GROW = 1 << 0,
|
||||
ARENA_DONTALIGN = 1 << 1,
|
||||
};
|
||||
#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 {
|
||||
char* data;
|
||||
size_t size;
|
||||
size_t cap;
|
||||
char flags;
|
||||
} arena_t;
|
||||
struct page *head, *last;
|
||||
} __attribute__((aligned(_WORD_SIZE))) arena_t;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
int arena_delete(arena_t *a);
|
||||
|
||||
/**
|
||||
* Attach an arena to an existing memory region.
|
||||
* The arena will not expand the region if space is exceeded.
|
||||
*/
|
||||
static inline arena_t arena_attach(void *ptr, size_t size)
|
||||
{
|
||||
return (arena_t) { .data = ptr, .size = 0, .cap = size, .flags = 0 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach an arena from an existing memory region.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
static inline void arena_reset(arena_t *a)
|
||||
{
|
||||
a->size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate memory from an arena.
|
||||
* Returns NULL and sets errno on failure.
|
||||
*/
|
||||
arena_t arena_new(void);
|
||||
void arena_reset(arena_t* a);
|
||||
void* arena_alloc(arena_t* a, size_t len);
|
||||
|
||||
/**
|
||||
* Allocate and zero memory from an arena.
|
||||
* Returns NULL and sets errno on failure.
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define KNOB_MMAP_SIZE (1UL << 36UL)
|
||||
#define KNOB_ALIGNMENT (sizeof(char*))
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
add_library(arena arena.c)
|
||||
|
||||
target_include_directories(arena PUBLIC ../include)
|
||||
213
src/arena.c
213
src/arena.c
@@ -1,125 +1,158 @@
|
||||
|
||||
#include "arena.h"
|
||||
#include "knob.h"
|
||||
|
||||
#include <errno.h> // errno
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h> // fprintf
|
||||
#include <string.h> // strerror
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// (sizeof(intptr_t) isn't guaranteed to be the machine word size but on most
|
||||
// compilers it is)
|
||||
#define SYS_PAGE_SIZE ((size_t)sysconf(_SC_PAGE_SIZE))
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define arena_err(msg) \
|
||||
fprintf(stderr, "%s (%s:%d): %s\n", msg, __func__, __LINE__, strerror(errno))
|
||||
#else
|
||||
#define arena_err(msg)
|
||||
#endif
|
||||
#define BIG_PAGE (SYS_PAGE_SIZE + 1)
|
||||
|
||||
static bool arena_grow(struct arena *a, size_t min_size)
|
||||
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)
|
||||
{
|
||||
if (!(a->flags & ARENA_GROW)) {
|
||||
return false;
|
||||
}
|
||||
struct page* p = malloc(sizeof *p);
|
||||
|
||||
size_t new_cap = a->cap * 2;
|
||||
while (new_cap < min_size) {
|
||||
new_cap *= 2;
|
||||
}
|
||||
if (p == NULL)
|
||||
exit(errno);
|
||||
|
||||
int ok = mprotect(
|
||||
a->data + a->cap,
|
||||
new_cap - a->cap,
|
||||
PROT_READ | PROT_WRITE
|
||||
);
|
||||
p->next = NULL;
|
||||
p->offset = 0;
|
||||
p->prev_offset = 0;
|
||||
p->data = malloc(SYS_PAGE_SIZE);
|
||||
|
||||
if (ok == -1) {
|
||||
arena_err("mprotect");
|
||||
return false;
|
||||
}
|
||||
if (p->data == NULL)
|
||||
exit(errno);
|
||||
|
||||
a->cap = new_cap;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
arena_t arena_new()
|
||||
{
|
||||
size_t size = sysconf(_SC_PAGE_SIZE);
|
||||
if (size == -1) {
|
||||
arena_err("sysconf");
|
||||
goto sysconf_failed;
|
||||
}
|
||||
|
||||
void *p = mmap(NULL, KNOB_MMAP_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;
|
||||
}
|
||||
|
||||
struct arena a = {
|
||||
.data = p,
|
||||
.size = 0,
|
||||
.cap = size,
|
||||
.flags = ARENA_GROW,
|
||||
arena_t a = {
|
||||
.head = p,
|
||||
.last = p,
|
||||
};
|
||||
|
||||
return a;
|
||||
|
||||
mprotect_failed:
|
||||
ok = munmap(p, KNOB_MMAP_SIZE);
|
||||
if (ok == -1) {
|
||||
arena_err("munmap");
|
||||
}
|
||||
mmap_failed:
|
||||
sysconf_failed:
|
||||
return (arena_t) { 0 };
|
||||
|
||||
void _arena_new_page(arena_t* a, size_t size)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
void* tmp = calloc(1, sizeof *(a->head->next));
|
||||
|
||||
if (tmp == NULL)
|
||||
exit(errno);
|
||||
|
||||
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->head = a->last;
|
||||
a->head->offset = 0;
|
||||
a->head->prev_offset = 0;
|
||||
}
|
||||
|
||||
void* _arena_big_alloc(arena_t* a, size_t size)
|
||||
{
|
||||
_arena_new_page(a, size);
|
||||
a->head->offset = BIG_PAGE;
|
||||
a->head->prev_offset = BIG_PAGE;
|
||||
|
||||
return a->head->data;
|
||||
}
|
||||
|
||||
void* arena_alloc(arena_t* a, size_t size)
|
||||
{
|
||||
// align
|
||||
if (!(a->flags & ARENA_DONTALIGN)) {
|
||||
size = (size + KNOB_ALIGNMENT - 1) & ~(KNOB_ALIGNMENT - 1);
|
||||
if (size > SYS_PAGE_SIZE)
|
||||
return _arena_big_alloc(a, size);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void *p = a->data + a->size;
|
||||
if (a->size + size > a->cap) {
|
||||
if (!arena_grow(a, a->size))
|
||||
return NULL;
|
||||
}
|
||||
a->size += size;
|
||||
return p;
|
||||
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);
|
||||
if (p == NULL)
|
||||
return p;
|
||||
memset(p, 0, nmemb * size);
|
||||
return p;
|
||||
}
|
||||
|
||||
int arena_delete(struct arena *a)
|
||||
void* arena_realloc_tail(arena_t* a, size_t len)
|
||||
{
|
||||
if (!(a->flags & ARENA_GROW)) {
|
||||
return -1;
|
||||
if (len <= (a->head->offset - a->head->prev_offset)) {
|
||||
a->head->offset = a->head->prev_offset + len;
|
||||
return (byte_t*)(a->head->data) + a->head->prev_offset;
|
||||
}
|
||||
int ok = munmap(a->data, KNOB_MMAP_SIZE);
|
||||
if (ok == -1) {
|
||||
arena_err("munmap");
|
||||
return -1;
|
||||
|
||||
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->cap = -1;
|
||||
a->size = -1;
|
||||
return 0;
|
||||
|
||||
a->head->offset = a->head->prev_offset;
|
||||
|
||||
return arena_alloc(a, len);
|
||||
}
|
||||
|
||||
void arena_delete(arena_t* a)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
42
src/test_arena.c
Normal file
42
src/test_arena.c
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
|
||||
// _start test_arena_alloc.c
|
||||
#include "arena.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
arena_t default_arena = arena_new();
|
||||
|
||||
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);
|
||||
|
||||
if (c == NULL)
|
||||
err(EXIT_FAILURE, "failed to allocate memory");
|
||||
|
||||
*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;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||
|
||||
add_executable(test_arena test_arena.c)
|
||||
|
||||
target_link_libraries(test_arena arena)
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
run compiled test in conjunction with other tools, e.g.
|
||||
|
||||
/usr/bin/time strace -e trace=\!write ./test/test_arena
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
|
||||
#include "arena.h"
|
||||
#include "knob.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h> // fprintf
|
||||
#include <stdlib.h> // exit, EXIT_FAILURE
|
||||
#include <string.h> // memset
|
||||
#include <sys/wait.h> // waitpid
|
||||
#include <unistd.h> // sysconf
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t page_size = sysconf(_SC_PAGE_SIZE);
|
||||
if (page_size == -1) {
|
||||
perror("sysconf");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* test arena from arena_new();
|
||||
* ===============================
|
||||
*/
|
||||
{
|
||||
fprintf(stderr, "creating new arena with arena_new()\n ");
|
||||
arena_t a = arena_new();
|
||||
if (arena_new_failed(&a)) {
|
||||
fprintf(stderr, "arena_new failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "OK\n");
|
||||
|
||||
fprintf(stderr, "attempting many small allocations\n ");
|
||||
for (int i = 0; i < page_size * 8; i++) {
|
||||
char* s = arena_alloc(&a, 4 * sizeof *s);
|
||||
if (!s) {
|
||||
fprintf(stderr, "arena_alloc failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(s, 'a', 4);
|
||||
}
|
||||
fprintf(stderr, "OK\n");
|
||||
|
||||
fprintf(stderr, "testing allocations of cap * 3 + 123\n ");
|
||||
for (int i = 0; i < 2; i++) {
|
||||
size_t n = a.cap * 3 + 123;
|
||||
volatile char* s = arena_alloc(&a, n);
|
||||
if (!s) {
|
||||
fprintf(stderr, "arena_alloc failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "OK\n");
|
||||
|
||||
fprintf(stderr, "calling arena_delete() on arena from arena_new()\n ");
|
||||
int ok = arena_delete(&a);
|
||||
if (ok == -1) {
|
||||
fprintf(stderr, "arena_delete failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "OK\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* test arena made by arena_attach();
|
||||
* ===============================
|
||||
*/
|
||||
{
|
||||
fprintf(stderr, "creating new arena with arena_attach() and malloc()\n ");
|
||||
char* p = malloc(page_size);
|
||||
if (!p) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
arena_t a = arena_attach(p, page_size);
|
||||
fprintf(stderr, "OK\n");
|
||||
|
||||
bool failed = false;
|
||||
|
||||
fprintf(stderr, "try to grow more than the buffer size (should fail)\n ");
|
||||
int i;
|
||||
for (i=0; i < page_size + 1; i++) {
|
||||
char* s = arena_alloc(&a, 8);
|
||||
if (!s) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!failed) {
|
||||
fprintf(stderr, "allocation was supposed to fail, but didn't\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (i != page_size / KNOB_ALIGNMENT ) {
|
||||
fprintf(stderr, "allocation failed after %d allocations, but should fail after %d!\n", i, page_size/sizeof(void*));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "OK\n");
|
||||
|
||||
fprintf(stderr, "deleting an arena not made with arena_new (should fail)\n ");
|
||||
int ok = arena_delete(&a);
|
||||
if (ok != -1) {
|
||||
fprintf(stderr, "arena_delete was supposed to fail, but didn't\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "OK\n");
|
||||
|
||||
free(arena_detatch(a));
|
||||
}
|
||||
|
||||
/*
|
||||
* test the memory protection
|
||||
* ===============================
|
||||
*/
|
||||
{
|
||||
fprintf(stderr, "attempting to access memory beyond arena cap (should sigsegv)\n ");
|
||||
pid_t p;
|
||||
switch (p = fork()) {
|
||||
case -1: /* error */
|
||||
perror("fork()");
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
case 0: /* child */
|
||||
arena_t a = arena_new();
|
||||
if (arena_new_failed(&a)) {
|
||||
fprintf(stderr, "arena_new failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char* s = arena_alloc(&a, 12345);
|
||||
if (!s) {
|
||||
fprintf(stderr, "arena_alloc failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// should sigsegv
|
||||
a.data[a.cap] = 1;
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
int status;
|
||||
int ok = waitpid(p, &status, 0);
|
||||
if (!ok) {
|
||||
perror("waitpid");
|
||||
}
|
||||
|
||||
if (!( WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV)) {
|
||||
fprintf(stderr, "allocating beyond arena cap succeeded when it shouldn't!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "OK\n");
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user