Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7134d6ff13 | |||
| 786d7213ae | |||
| 59e2d3ff25 | |||
| f67a3cc335 | |||
| 6b46af52aa | |||
| 0af6582a64 | |||
| 6e0e6f7437 | |||
| d11557a4fe | |||
| 44a2982376 | |||
|
|
069f6b11dc | ||
|
|
e5fa743d85 | ||
|
|
45e6023d42 | ||
|
|
d3754e64e5 | ||
|
|
57979892f8 | ||
|
|
51feaf1d00 | ||
|
|
529955dbfb | ||
|
|
05e69d8776 | ||
|
|
9f6eaca42e | ||
|
|
d4a9708082 | ||
|
|
b9d743e868 | ||
|
|
b95c63c965 | ||
|
|
c4fe01a54a | ||
|
|
2b14b86ccb | ||
|
|
ba39a77c0c | ||
|
|
6576032654 | ||
|
|
09528df89b | ||
|
|
cd68347dd9 |
10
.clang-format
Normal file
10
.clang-format
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
BasedOnStyle: WebKit
|
||||||
|
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
AlignConsecutiveDeclarations: true
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: false
|
||||||
|
PointerAlignment: Right
|
||||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
obj/
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
build/
|
||||||
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
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()
|
||||||
|
|
||||||
27
Makefile
27
Makefile
@@ -1,27 +0,0 @@
|
|||||||
|
|
||||||
CC := gcc
|
|
||||||
CFLAGS := -ggdb -O0 -std=c99 -Wall -Wextra -Wpedantic
|
|
||||||
|
|
||||||
OBJS := obj/arena.o obj/alloc_backend.o
|
|
||||||
|
|
||||||
all : test/test_arena
|
|
||||||
|
|
||||||
test/test_arena : src/test_arena.c $(OBJS) | test
|
|
||||||
$(CC) -o $@ $(CFLAGS) $^
|
|
||||||
|
|
||||||
obj/%.o : src/%.c | obj
|
|
||||||
$(CC) -o $@ -c $(CFLAGS) $<
|
|
||||||
|
|
||||||
obj:
|
|
||||||
mkdir -p $@
|
|
||||||
|
|
||||||
bin:
|
|
||||||
mkdir -p $@
|
|
||||||
|
|
||||||
test:
|
|
||||||
mkdir -p $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf obj bin test
|
|
||||||
|
|
||||||
.PHONY: clean obj test bin all
|
|
||||||
65
README.md
Normal file
65
README.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
# 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()`
|
||||||
|
|
||||||
|
Allocate a new arena.
|
||||||
|
|
||||||
|
If `arena_new()` fails, the struct member `data` is set to NULL. This can be
|
||||||
|
checked with `arena_new_failed()`
|
||||||
|
|
||||||
|
|
||||||
|
## `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.
|
||||||
|
|
||||||
5
compile_flags.txt
Normal file
5
compile_flags.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-std=c2x
|
||||||
|
-Iinclude
|
||||||
81
include/arena.h
Normal file
81
include/arena.h
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef ARENA_H
|
||||||
|
#define ARENA_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// flags = 0 should be considered the default
|
||||||
|
enum arena_flags {
|
||||||
|
ARENA_GROW = 1 << 0,
|
||||||
|
ARENA_DONTALIGN = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct arena {
|
||||||
|
char* data;
|
||||||
|
size_t size;
|
||||||
|
size_t cap;
|
||||||
|
char flags;
|
||||||
|
} 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.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif
|
||||||
6
include/knob.h
Normal file
6
include/knob.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define KNOB_MMAP_SIZE (1UL << 36UL)
|
||||||
|
#define KNOB_ALIGNMENT (sizeof(char*))
|
||||||
|
|
||||||
4
src/CMakeLists.txt
Normal file
4
src/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
add_library(arena arena.c)
|
||||||
|
|
||||||
|
target_include_directories(arena PUBLIC ../include)
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
|
|
||||||
#include "alloc_backend.h"
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Separate function to make switching
|
|
||||||
* allocation backend easier
|
|
||||||
*
|
|
||||||
* must take size_t size as an argument
|
|
||||||
* must return NULL on failure
|
|
||||||
*/
|
|
||||||
void* call_alloc_backend(size_t size)
|
|
||||||
{
|
|
||||||
void *p = mmap(
|
|
||||||
NULL,
|
|
||||||
size,
|
|
||||||
PROT_READ | PROT_WRITE,
|
|
||||||
MAP_ANONYMOUS | MAP_PRIVATE,
|
|
||||||
-1, /* man mmap(2): "[...], some implementations require fd to be
|
|
||||||
-1 if MAP_ANONYMOUS is specified [...]" */
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
if (p == MAP_FAILED)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
#ifndef ALLOC_BACKEND_H
|
|
||||||
#define ALLOC_BACKEND_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
void* call_alloc_backend(size_t size);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
137
src/arena.c
137
src/arena.c
@@ -1,60 +1,125 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
#include "alloc_backend.h"
|
#include "knob.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h> // errno
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h> // fprintf
|
||||||
|
#include <string.h> // strerror
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define ARENA_SIZE ((size_t)(128*sysconf(_SC_PAGE_SIZE)))
|
|
||||||
|
|
||||||
/*
|
#ifndef NDEBUG
|
||||||
* Allocates and returns new arena
|
#define arena_err(msg) \
|
||||||
*/
|
fprintf(stderr, "%s (%s:%d): %s\n", msg, __func__, __LINE__, strerror(errno))
|
||||||
struct arena* arena_new()
|
#else
|
||||||
|
#define arena_err(msg)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool arena_grow(struct arena *a, size_t min_size)
|
||||||
{
|
{
|
||||||
size_t size = ARENA_SIZE;
|
if (!(a->flags & ARENA_GROW)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char *p = call_alloc_backend(size);
|
size_t new_cap = a->cap * 2;
|
||||||
|
while (new_cap < min_size) {
|
||||||
|
new_cap *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (p == NULL)
|
int ok = mprotect(
|
||||||
return NULL;
|
a->data + a->cap,
|
||||||
|
new_cap - a->cap,
|
||||||
|
PROT_READ | PROT_WRITE
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ok == -1) {
|
||||||
|
arena_err("mprotect");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = {
|
struct arena a = {
|
||||||
.begin = p + sizeof(struct arena),
|
.data = p,
|
||||||
.next = p + sizeof(struct arena),
|
.size = 0,
|
||||||
.cap = size
|
.cap = size,
|
||||||
|
.flags = ARENA_GROW,
|
||||||
};
|
};
|
||||||
|
|
||||||
memcpy(p, &a, sizeof a);
|
return a;
|
||||||
|
|
||||||
return (struct arena*)p;
|
mprotect_failed:
|
||||||
|
ok = munmap(p, KNOB_MMAP_SIZE);
|
||||||
|
if (ok == -1) {
|
||||||
|
arena_err("munmap");
|
||||||
|
}
|
||||||
|
mmap_failed:
|
||||||
|
sysconf_failed:
|
||||||
|
return (arena_t) { 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* arena_alloc(arena_t *a, size_t size)
|
||||||
/*
|
|
||||||
* Frees all memory in arena
|
|
||||||
*/
|
|
||||||
void arena_reset(struct arena *a)
|
|
||||||
{
|
{
|
||||||
a->next = a->begin;
|
// align
|
||||||
|
if (!(a->flags & ARENA_DONTALIGN)) {
|
||||||
|
size = (size + KNOB_ALIGNMENT - 1) & ~(KNOB_ALIGNMENT - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *p = a->data + a->size;
|
||||||
/*
|
if (a->size + size > a->cap) {
|
||||||
* Allocate new memory using arena
|
if (!arena_grow(a, a->size))
|
||||||
*/
|
|
||||||
void* arena_alloc(struct arena *a, size_t len)
|
|
||||||
{
|
|
||||||
void *p = a->next;
|
|
||||||
a->next += len;
|
|
||||||
|
|
||||||
if (a->next - a->begin >= a->cap) {
|
|
||||||
errno = ENOMEM;
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
a->size += size;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (!(a->flags & ARENA_GROW)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int ok = munmap(a->data, KNOB_MMAP_SIZE);
|
||||||
|
if (ok == -1) {
|
||||||
|
arena_err("munmap");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
a->cap = -1;
|
||||||
|
a->size = -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
19
src/arena.h
19
src/arena.h
@@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
#ifndef ARENA_H
|
|
||||||
#define ARENA_H
|
|
||||||
|
|
||||||
|
|
||||||
#include <stddef.h> // ptrdiff_t
|
|
||||||
|
|
||||||
struct arena {
|
|
||||||
unsigned char *begin,
|
|
||||||
*next;
|
|
||||||
ptrdiff_t cap;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct arena* arena_new();
|
|
||||||
void arena_reset(struct arena *a);
|
|
||||||
void* arena_alloc(struct arena *a, size_t len);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// _start test_arena_alloc.c
|
|
||||||
#include "arena.h"
|
|
||||||
#include <err.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
static struct arena *default_arena = NULL;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
default_arena = arena_new();
|
|
||||||
|
|
||||||
if (default_arena == NULL) {
|
|
||||||
err(errno, "failed to allocate arena");
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n%p\n", default_arena->next);
|
|
||||||
|
|
||||||
char *ten_A = arena_alloc(default_arena, 11 * sizeof *ten_A);
|
|
||||||
char *ten_B = arena_alloc(default_arena, 11 * sizeof *ten_B);
|
|
||||||
char *ten_C = arena_alloc(default_arena, 11 * sizeof *ten_C);
|
|
||||||
|
|
||||||
for (size_t i=0; i<10; ++i) {
|
|
||||||
ten_A[i] = 'A';
|
|
||||||
ten_B[i] = 'B';
|
|
||||||
ten_C[i] = 'C';
|
|
||||||
}
|
|
||||||
|
|
||||||
ten_A[10] = '\0';
|
|
||||||
ten_B[10] = '\0';
|
|
||||||
ten_C[10] = '\0';
|
|
||||||
|
|
||||||
printf("\n%s %s %s", ten_A, ten_B, ten_C);
|
|
||||||
printf("\n%p\n", default_arena->next);
|
|
||||||
|
|
||||||
arena_reset(default_arena);
|
|
||||||
|
|
||||||
printf("\n%p\n", default_arena->next);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
6
test/CMakeLists.txt
Normal file
6
test/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
add_executable(test_arena test_arena.c)
|
||||||
|
|
||||||
|
target_link_libraries(test_arena arena)
|
||||||
6
test/README
Normal file
6
test/README
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
run compiled test in conjunction with other tools, e.g.
|
||||||
|
|
||||||
|
/usr/bin/time strace -e trace=\!write ./test/test_arena
|
||||||
|
|
||||||
|
|
||||||
153
test/test_arena.c
Normal file
153
test/test_arena.c
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
|
||||||
|
#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