dqn: Add head ptr on arena blocks
This commit is contained in:
parent
faa46b4921
commit
d9116ae48e
62
dqn.h
62
dqn.h
@ -1447,10 +1447,6 @@ DQN_API int Dqn_VMem_Protect (void *ptr, Dqn_usize size, uint32_t page_flags);
|
||||
// @desc Dump the stats of the given arena to the memory log-stream.
|
||||
// @param[in] arena The arena to dump stats for
|
||||
|
||||
#if !defined(DQN_ARENA_MIN_BLOCK_SIZE)
|
||||
#define DQN_ARENA_MIN_BLOCK_SIZE DQN_VMEM_RESERVE_GRANULARITY
|
||||
#endif
|
||||
|
||||
enum Dqn_ArenaBlockFlags
|
||||
{
|
||||
Dqn_ArenaBlockFlags_Private = 1 << 0, ///< Private blocks can only allocate its memory when used in the 'FromBlock' API variants
|
||||
@ -1493,20 +1489,21 @@ struct Dqn_Arena
|
||||
{
|
||||
bool use_after_free_guard;
|
||||
|
||||
Dqn_String8 label; ///< Optional label to describe the arena
|
||||
Dqn_usize min_block_size;
|
||||
Dqn_ArenaBlock *curr; ///< Active block the arena is allocating from
|
||||
Dqn_ArenaBlock *tail; ///< Last block in the linked list of blocks
|
||||
Dqn_ArenaStat stats; ///< Current arena stats, reset when reset usage is invoked.
|
||||
Dqn_String8 label; ///< Optional label to describe the arena
|
||||
Dqn_ArenaBlock *head; ///< Active block the arena is allocating from
|
||||
Dqn_ArenaBlock *curr; ///< Active block the arena is allocating from
|
||||
Dqn_ArenaBlock *tail; ///< Last block in the linked list of blocks
|
||||
Dqn_ArenaStat stats; ///< Current arena stats, reset when reset usage is invoked.
|
||||
};
|
||||
|
||||
struct Dqn_ArenaTempMemory
|
||||
{
|
||||
Dqn_Arena *arena; ///< The arena the scope is for
|
||||
Dqn_ArenaBlock *curr; ///< The current block of the arena at the beginning of the scope
|
||||
Dqn_ArenaBlock *tail; ///< The tail block of the arena at the beginning of the scope
|
||||
Dqn_usize curr_used; ///< The current used amount of the current block
|
||||
Dqn_ArenaStat stats; ///< The stats of the arena at the beginning of the scope
|
||||
Dqn_Arena *arena; ///< Arena the scope is for
|
||||
Dqn_ArenaBlock *head; ///< Head block of the arena at the beginning of the scope
|
||||
Dqn_ArenaBlock *curr; ///< Current block of the arena at the beginning of the scope
|
||||
Dqn_ArenaBlock *tail; ///< Tail block of the arena at the beginning of the scope
|
||||
Dqn_usize curr_used; ///< Current used amount of the current block
|
||||
Dqn_ArenaStat stats; ///< Stats of the arena at the beginning of the scope
|
||||
};
|
||||
|
||||
// Automatically begin and end a temporary memory scope on object construction
|
||||
@ -6352,13 +6349,12 @@ DQN_API void Dqn_Arena_Reset(Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
|
||||
return;
|
||||
|
||||
// NOTE: Zero all the blocks until we reach the first block in the list
|
||||
for (Dqn_ArenaBlock *block = arena->tail; block; block = block->prev) {
|
||||
if (!block->prev)
|
||||
arena->curr = block;
|
||||
for (Dqn_ArenaBlock *block = arena->head; block; block = block->next) {
|
||||
Dqn_ArenaBlockResetInfo_ reset_info = {};
|
||||
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE block, zero_mem, reset_info);
|
||||
}
|
||||
|
||||
arena->curr = arena->head;
|
||||
arena->stats.used = 0;
|
||||
arena->stats.wasted = 0;
|
||||
}
|
||||
@ -6366,11 +6362,12 @@ DQN_API void Dqn_Arena_Reset(Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
|
||||
DQN_API Dqn_ArenaTempMemory Dqn_Arena_BeginTempMemory(Dqn_Arena *arena)
|
||||
{
|
||||
Dqn_ArenaTempMemory result = {};
|
||||
result.arena = arena;
|
||||
result.curr = arena->curr;
|
||||
result.tail = arena->tail;
|
||||
result.curr_used = (arena->curr) ? arena->curr->used : 0;
|
||||
result.stats = arena->stats;
|
||||
result.arena = arena;
|
||||
result.head = arena->head;
|
||||
result.curr = arena->curr;
|
||||
result.tail = arena->tail;
|
||||
result.curr_used = (arena->curr) ? arena->curr->used : 0;
|
||||
result.stats = arena->stats;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -6387,6 +6384,7 @@ DQN_API void Dqn_Arena_EndTempMemory_(DQN_LEAK_TRACE_FUNCTION Dqn_ArenaTempMemor
|
||||
arena->stats.blocks = scope.stats.blocks;
|
||||
|
||||
// NOTE: Revert the current block to the scope's current block
|
||||
arena->head = scope.head;
|
||||
arena->curr = scope.curr;
|
||||
if (arena->curr) {
|
||||
Dqn_ArenaBlock *curr = arena->curr;
|
||||
@ -6526,6 +6524,7 @@ DQN_API Dqn_ArenaBlock *Dqn_Arena_Grow_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena
|
||||
} else {
|
||||
DQN_ASSERT(!arena->curr);
|
||||
arena->curr = result;
|
||||
arena->head = result;
|
||||
}
|
||||
arena->tail = result;
|
||||
|
||||
@ -6558,15 +6557,18 @@ DQN_API void *Dqn_Arena_Allocate_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_
|
||||
while (arena->curr && (arena->curr->flags & Dqn_ArenaBlockFlags_Private))
|
||||
arena->curr = arena->curr->next;
|
||||
|
||||
if (!arena->curr) {
|
||||
Dqn_usize allocation_size = size + (align - 1);
|
||||
if (!Dqn_Arena_Grow(DQN_LEAK_TRACE_ARG arena, allocation_size, allocation_size /*commit*/, 0 /*flags*/)) {
|
||||
break;
|
||||
}
|
||||
arena->curr = arena->tail;
|
||||
}
|
||||
|
||||
result = Dqn_Arena_AllocateFromBlock(arena->curr, size, align, zero_mem);
|
||||
if (!result) {
|
||||
if (!arena->curr || arena->curr == arena->tail) {
|
||||
Dqn_usize allocation_size = size + (align - 1);
|
||||
if (!Dqn_Arena_Grow(DQN_LEAK_TRACE_ARG arena, allocation_size, allocation_size /*commit*/, 0 /*flags*/)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (arena->curr != arena->tail)
|
||||
arena->curr = arena->curr->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (result)
|
||||
|
@ -31,34 +31,82 @@
|
||||
#define DQN_TESTER_IMPLEMENTATION
|
||||
#include "dqn_tester.h"
|
||||
|
||||
enum Guard {
|
||||
Guard_None,
|
||||
Guard_UseAfterFree,
|
||||
Guard_Count,
|
||||
};
|
||||
|
||||
static Dqn_String8 ArenaGuardTestSuffix(uint32_t guard)
|
||||
{
|
||||
Dqn_String8 result = {};
|
||||
switch (guard) {
|
||||
case Guard_None: result = DQN_STRING8(" "); break;
|
||||
case Guard_UseAfterFree: result = DQN_STRING8(" [UAF]"); break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Dqn_Tester TestArena()
|
||||
{
|
||||
Dqn_Tester test = {};
|
||||
DQN_TESTER_GROUP(test, "Dqn_Arena") {
|
||||
DQN_TESTER_TEST("Reused memory is zeroed out") {
|
||||
Dqn_Arena arena = {};
|
||||
for (Dqn_usize guard = 0; guard < Guard_Count; guard++) {
|
||||
Dqn_String8 test_suffix = ArenaGuardTestSuffix(guard);
|
||||
DQN_TESTER_TEST("Reused memory is zeroed out%.*s", DQN_STRING_FMT(test_suffix)) {
|
||||
Dqn_Arena arena = {};
|
||||
arena.use_after_free_guard = guard == Guard_UseAfterFree;
|
||||
|
||||
// NOTE: Allocate 128 kilobytes, fill it with garbage, then reset the arena
|
||||
Dqn_usize size = DQN_KILOBYTES(128);
|
||||
uintptr_t first_ptr_address = 0;
|
||||
{
|
||||
Dqn_ArenaTempMemory temp_mem = Dqn_Arena_BeginTempMemory(&arena);
|
||||
void *ptr = Dqn_Arena_Allocate(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
first_ptr_address = DQN_CAST(uintptr_t)ptr;
|
||||
DQN_MEMSET(ptr, 'z', size);
|
||||
Dqn_Arena_EndTempMemory(temp_mem);
|
||||
// NOTE: Allocate 128 kilobytes, fill it with garbage, then reset the arena
|
||||
Dqn_usize size = DQN_KILOBYTES(128);
|
||||
uintptr_t first_ptr_address = 0;
|
||||
{
|
||||
Dqn_ArenaTempMemory temp_mem = Dqn_Arena_BeginTempMemory(&arena);
|
||||
void *ptr = Dqn_Arena_Allocate(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
first_ptr_address = DQN_CAST(uintptr_t)ptr;
|
||||
DQN_MEMSET(ptr, 'z', size);
|
||||
Dqn_Arena_EndTempMemory(temp_mem);
|
||||
}
|
||||
|
||||
// NOTE: Reallocate 128 kilobytes
|
||||
char *ptr = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
|
||||
// NOTE: Double check we got the same pointer
|
||||
DQN_TESTER_ASSERT(&test, first_ptr_address == DQN_CAST(uintptr_t)ptr);
|
||||
|
||||
// NOTE: Check that the bytes are set to 0
|
||||
for (Dqn_usize i = 0; i < size; i++)
|
||||
DQN_TESTER_ASSERT(&test, ptr[i] == 0);
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Reallocate 128 kilobytes
|
||||
char *ptr = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, size, 1, Dqn_ZeroMem_Yes);
|
||||
for (Dqn_usize guard = 0; guard < Guard_Count; guard++) {
|
||||
Dqn_String8 test_suffix = ArenaGuardTestSuffix(guard);
|
||||
DQN_TESTER_TEST("Test arena grows naturally, 1mb + 4mb%.*s", DQN_STRING_FMT(test_suffix)) {
|
||||
Dqn_Arena arena = {};
|
||||
arena.use_after_free_guard = guard == Guard_UseAfterFree;
|
||||
|
||||
// NOTE: Double check we got the same pointer
|
||||
DQN_TESTER_ASSERT(&test, first_ptr_address == DQN_CAST(uintptr_t)ptr);
|
||||
// NOTE: Allocate 1mb, then 4mb, this should force the arena to grow
|
||||
char *ptr_1mb = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(1), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
char *ptr_4mb = DQN_CAST(char *)Dqn_Arena_Allocate(&arena, DQN_MEGABYTES(4), 1 /*align*/, Dqn_ZeroMem_Yes);
|
||||
DQN_TESTER_ASSERT(&test, ptr_1mb);
|
||||
DQN_TESTER_ASSERT(&test, ptr_4mb);
|
||||
|
||||
// NOTE: Check that the bytes are set to 0
|
||||
for (Dqn_usize i = 0; i < size; i++)
|
||||
DQN_TESTER_ASSERT(&test, ptr[i] == 0);
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
Dqn_ArenaBlock const *block_1mb = arena.head;
|
||||
char const *block_1mb_begin = DQN_CAST(char *)block_1mb->memory;
|
||||
char const *block_1mb_end = DQN_CAST(char *)block_1mb->memory + block_1mb->size;
|
||||
|
||||
Dqn_ArenaBlock const *block_4mb = arena.curr;
|
||||
char const *block_4mb_begin = DQN_CAST(char *)block_4mb->memory;
|
||||
char const *block_4mb_end = DQN_CAST(char *)block_4mb->memory + block_4mb->size;
|
||||
|
||||
DQN_TESTER_ASSERTF(&test, block_1mb != block_4mb, "New block should have been allocated and linked");
|
||||
DQN_TESTER_ASSERTF(&test, ptr_1mb >= block_1mb_begin && ptr_1mb <= block_1mb_end, "Pointer was not allocated from correct memory block");
|
||||
DQN_TESTER_ASSERTF(&test, ptr_4mb >= block_4mb_begin && ptr_4mb <= block_4mb_end, "Pointer was not allocated from correct memory block");
|
||||
|
||||
Dqn_Arena_Free(&arena, Dqn_ZeroMem_No);
|
||||
}
|
||||
}
|
||||
|
||||
Dqn_usize sizes[] = {DQN_KILOBYTES(1), DQN_KILOBYTES(4), DQN_KILOBYTES(5)};
|
||||
|
Loading…
Reference in New Issue
Block a user