rntviewer/src/mem.cpp

242 lines
5.5 KiB
C++
Raw Normal View History

2024-07-10 18:11:42 +00:00
// Mostly taken from the raddebugger -- thanks, Ryan.
2024-07-10 15:48:23 +00:00
#define align_pow2(x, b) (((x) + (b) - 1) & ( ~((b) - 1)))
2024-07-11 12:00:43 +00:00
#ifdef ENABLE_ASAN
2024-07-10 15:48:23 +00:00
#define asan_poison_memory_region(mem, cmt) __asan_poison_memory_region(mem, cmt)
#define asan_unpoison_memory_region(mem, cmt) __asan_unpoison_memory_region(mem, cmt)
#else
#define asan_poison_memory_region(mem, cmt)
#define asan_unpoison_memory_region(mem, cmt)
#endif
thread_local Thread_Ctx *thread_local_ctx = nullptr;
internal
void tctx_init(Thread_Ctx &tctx) {
memset(&tctx, 0, sizeof(Thread_Ctx));
Arena **arena_ptr = tctx.arenas;
for (u64 i = 0; i < countof(tctx.arenas); ++i, ++arena_ptr) {
*arena_ptr = arena_alloc();
}
thread_local_ctx = &tctx;
}
internal
void tctx_release()
{
for (u64 i = 0; i < countof(thread_local_ctx->arenas); ++i)
arena_release(thread_local_ctx->arenas[i]);
}
internal
Arena *tctx_get_scratch(Arena **conflicts, u64 count)
{
Thread_Ctx *tctx = thread_local_ctx;
Arena *res = nullptr;
Arena **arena_ptr = tctx->arenas;
for (u64 i = 0; i < countof(tctx->arenas); ++i, ++arena_ptr) {
Arena **conflict_ptr = conflicts;
b8 has_conflict = false;
for (u64 j = 0; j < count; ++j, ++conflict_ptr) {
if (*arena_ptr == *conflict_ptr) {
has_conflict = true;
break;
}
}
if (!has_conflict) {
res = *arena_ptr;
break;
}
}
return res;
}
2024-07-10 15:48:23 +00:00
// init_res: initial reserved size
// init_cmt: initial committed size
2024-07-10 19:47:37 +00:00
internal
2024-07-10 15:48:23 +00:00
Arena *arena_alloc_sized(u64 init_res, u64 init_cmt)
{
assert(ARENA_HEADER_SIZE < init_cmt && init_cmt <= init_res);
2024-07-17 11:20:46 +00:00
u64 page_size = os_page_size();
2024-07-10 15:48:23 +00:00
u64 res = align_pow2(init_res, page_size);
u64 cmt = align_pow2(init_cmt, page_size);
// reserve memory
void *mem = os_reserve(res);
if (!os_commit(mem, cmt)) {
munmap(mem, init_res);
}
Arena *arena = (Arena *)mem;
if (arena) {
asan_poison_memory_region(mem, cmt);
asan_unpoison_memory_region(mem, ARENA_HEADER_SIZE);
arena->cur = arena;
arena->pos = ARENA_HEADER_SIZE;
arena->cmt = cmt;
arena->res = res;
arena->align = 8;
2024-07-10 18:11:42 +00:00
arena->grow = true;
2024-07-12 09:58:48 +00:00
arena->mem_used = arena->mem_peak_used = ARENA_HEADER_SIZE;
2024-07-10 15:48:23 +00:00
}
return arena;
}
2024-07-10 19:47:37 +00:00
internal
2024-07-10 15:48:23 +00:00
Arena *arena_alloc()
{
return arena_alloc_sized(ARENA_RESERVE_SIZE, ARENA_COMMIT_SIZE);
}
2024-07-10 19:47:37 +00:00
internal
2024-07-10 15:48:23 +00:00
void arena_release(Arena *arena)
{
for (Arena *node = arena->cur, *prev = 0; node; node = prev) {
prev = node->prev;
os_release(node, node->res);
}
}
2024-07-10 19:47:37 +00:00
internal
2024-07-10 15:48:23 +00:00
u64 arena_huge_push_threshold()
{
u64 res = ARENA_RESERVE_SIZE;
u64 threshold = (res - ARENA_HEADER_SIZE) / 2 + 1;
return threshold;
}
2024-07-10 19:47:37 +00:00
internal
2024-07-10 15:48:23 +00:00
void *arena_push_impl(Arena *arena, u64 size)
{
Arena *cur = arena->cur;
u64 pos_mem = align_pow2(cur->pos, arena->align);
u64 pos_new = pos_mem + size;
2024-07-10 18:11:42 +00:00
if (cur->res < pos_new && arena->grow) {
2024-07-10 15:48:23 +00:00
Arena *new_block;
if (size < arena_huge_push_threshold()) {
new_block = arena_alloc();
} else {
u64 new_block_size = size + ARENA_HEADER_SIZE;
new_block = arena_alloc_sized(new_block_size, new_block_size);
}
if (new_block) {
new_block->base_pos = cur->base_pos + cur->res;
2024-07-11 13:27:38 +00:00
new_block->prev = arena->cur;
arena->cur = new_block;
2024-07-10 15:48:23 +00:00
cur = new_block;
pos_mem = align_pow2(cur->pos, cur->align);
pos_new = pos_mem + size;
}
}
if (cur->cmt < pos_new) {
u64 cmt_new_aligned = align_pow2(pos_new, ARENA_COMMIT_SIZE);
2024-07-10 18:11:42 +00:00
u64 cmt_new_clamped = min(cmt_new_aligned, cur->res);
2024-07-10 15:48:23 +00:00
u64 cmt_new_size = cmt_new_clamped - cur->cmt;
b32x is_cmt_ok = os_commit((u8*)cur + cur->cmt, cmt_new_size);
if (is_cmt_ok)
cur->cmt = cmt_new_clamped;
}
void *mem = 0;
if (cur->cmt >= pos_new) {
mem = (u8*)cur + pos_mem;
2024-07-12 09:58:48 +00:00
u64 added_size = pos_new - cur->pos;
arena->mem_used += added_size;
arena->mem_peak_used = max(arena->mem_peak_used, arena->mem_used);
2024-07-10 15:48:23 +00:00
cur->pos = pos_new;
2024-07-12 09:58:48 +00:00
asan_unpoison_memory_region(mem, added_size);
2024-07-10 15:48:23 +00:00
}
return mem;
}
2024-07-10 18:11:42 +00:00
template <typename T>
2024-07-10 19:47:37 +00:00
internal
2024-07-12 16:29:35 +00:00
T *arena_push_array_nozero(Arena *arena, u64 count)
2024-07-10 18:11:42 +00:00
{
2024-07-12 16:29:35 +00:00
void *mem = arena_push_impl(arena, sizeof(T) * count);
2024-07-10 18:11:42 +00:00
return (T *)mem;
}
2024-07-11 12:00:43 +00:00
template <typename T>
internal
2024-07-12 16:29:35 +00:00
T *arena_push_array(Arena *arena, u64 count)
2024-07-11 12:00:43 +00:00
{
2024-07-12 16:29:35 +00:00
T *ary = arena_push_array_nozero<T>(arena, count);
memset(ary, 0, sizeof(T) * count);
return ary;
2024-07-11 12:00:43 +00:00
}
2024-07-10 18:11:42 +00:00
template <typename T>
2024-07-10 19:47:37 +00:00
internal
2024-07-12 16:29:35 +00:00
T *arena_push(Arena *arena)
2024-07-10 18:11:42 +00:00
{
2024-07-12 16:29:35 +00:00
return arena_push_array<T>(arena, 1);
2024-07-10 18:11:42 +00:00
}
template <typename T>
2024-07-10 19:47:37 +00:00
internal
2024-07-12 16:29:35 +00:00
T *arena_push_nozero(Arena *arena)
2024-07-10 18:11:42 +00:00
{
2024-07-12 16:29:35 +00:00
return arena_push_array_nozero<T>(arena, 1);
2024-07-10 18:11:42 +00:00
}
2024-07-10 19:47:37 +00:00
internal
2024-07-10 18:11:42 +00:00
u64 arena_pos(Arena *arena)
{
Arena *cur = arena->cur;
u64 pos = cur->base_pos + cur->pos;
return pos;
}
2024-07-10 19:47:37 +00:00
internal
2024-07-10 18:11:42 +00:00
void arena_pop_to(Arena *arena, u64 big_pos_unclamped)
{
2024-07-11 12:00:43 +00:00
u64 big_pos = max(ARENA_HEADER_SIZE, big_pos_unclamped);
2024-07-10 18:11:42 +00:00
// unroll the chain
Arena *current = arena->cur;
for (Arena *prev = 0; current->base_pos >= big_pos; current = prev) {
prev = current->prev;
os_release(current, current->res);
}
assert(current);
arena->cur = current;
// compute arena-relative position
u64 new_pos = big_pos - current->base_pos;
assert(new_pos <= current->pos);
// poison popped memory block
asan_poison_memory_region((u8*)current + new_pos, (current->pos - new_pos));
2024-07-12 09:58:48 +00:00
arena->mem_used -= (current->pos - new_pos);
2024-07-10 18:11:42 +00:00
// update position
current->pos = new_pos;
}
2024-07-10 19:47:37 +00:00
internal
2024-07-10 18:11:42 +00:00
Temp temp_begin(Arena *arena)
{
u64 pos = arena_pos(arena);
Temp temp = {arena, pos};
return temp;
}
2024-07-10 19:47:37 +00:00
internal
2024-07-10 18:11:42 +00:00
void temp_end(Temp temp)
{
arena_pop_to(temp.arena, temp.pos);
}