rtengine/src/runtime/mem_arena.c
2024-02-05 01:23:31 +01:00

109 lines
3.0 KiB
C

#include "mem_arena.h"
#include "config.h"
#include <stdlib.h>
RT_CVAR_I(rt_TemporaryArenaSize, "Size of temporary arenas in bytes. Default: 32 MB", RT_MB(32));
#define ALIGNMENT 0xf
#define ALIGN(n) (((n) + ALIGNMENT) & ~ALIGNMENT)
extern void *memset(void *, int, size_t);
RT_DLLEXPORT rt_create_arena_result rtCreateArena(void *memory, size_t size) {
rt_arena arena = {.needs_free = 0};
if (!memory) {
size = ALIGN(size);
memory = malloc(size);
if (!memory) {
return (rt_create_arena_result){
.ok = 0,
};
}
arena.needs_free = 1;
}
arena.base = memory;
arena.size = size;
arena.at = 0;
return (rt_create_arena_result){
.arena = arena,
.ok = 1,
};
}
RT_DLLEXPORT void *rtArenaPush(rt_arena *arena, size_t n) {
if (n == 0)
return NULL;
n = ALIGN(n);
if (arena->at + n > arena->size)
return NULL;
void *p = (char *)arena->base + arena->at;
arena->at += n;
return p;
}
RT_DLLEXPORT void *rtArenaPushZero(rt_arena *arena, size_t n) {
void *p = rtArenaPush(arena, n);
if (p)
memset(p, 0, ALIGN(n));
return p;
}
RT_DLLEXPORT void rtArenaPop(rt_arena *arena, size_t n) {
n = ALIGN(n);
if (arena->at < n)
return;
arena->at -= n;
}
RT_DLLEXPORT void rtReleaseArena(rt_arena *arena) {
if (arena->needs_free)
free(arena->base);
arena->base = NULL;
arena->at = 0;
arena->size = 0;
arena->needs_free = 0;
}
/* Temporary arena pool */
typedef uint32_t rt_thread_id;
extern RT_DLLEXPORT rt_thread_id rtGetCurrentThreadId(void);
#define NUM_TEMP_ARENAS_PER_THREAD 2
typedef struct {
rt_arena arenas[NUM_TEMP_ARENAS_PER_THREAD];
} rt_thread_temp_arenas;
static RT_THREAD_LOCAL rt_thread_temp_arenas t_arenas;
RT_DLLEXPORT rt_temp_arena rtGetTemporaryArena(const rt_arena **permanent_arenas, int count) {
if (!t_arenas.arenas[0].base) {
/* Initialize */
for (int i = 0; i < NUM_TEMP_ARENAS_PER_THREAD; ++i) {
rt_create_arena_result res = rtCreateArena(NULL, (size_t)rt_TemporaryArenaSize.i);
if (!res.ok) {
rtLog("CORE",
"Failed to initialize thread-local temporary arenas for thread: %u",
rtGetCurrentThreadId());
return (rt_temp_arena){.arena = NULL, .at = 0};
}
t_arenas.arenas[i] = res.arena;
}
}
for (int i = 0; i < NUM_TEMP_ARENAS_PER_THREAD; ++i) {
int conflict_found = 0;
for (int j = 0; j < count; ++j) {
if (permanent_arenas[j] == &t_arenas.arenas[i])
conflict_found = 1;
}
if (!conflict_found) {
return rtBeginTempArena(&t_arenas.arenas[i]);
}
}
rtLog("CORE",
"Failed to find a usable thread-local temporary arena for thread: %u",
rtGetCurrentThreadId());
return (rt_temp_arena){.arena = NULL, .at = 0};
}