Compare commits

..

2 Commits

Author SHA1 Message Date
faa46b4921 dqn: Fix block metadata size free error 2023-06-06 21:10:51 +10:00
546f1a079c dqn: Reuse code for resetting blocks 2023-06-06 20:19:47 +10:00

86
dqn.h
View File

@ -1495,8 +1495,8 @@ struct Dqn_Arena
Dqn_String8 label; ///< Optional label to describe the arena Dqn_String8 label; ///< Optional label to describe the arena
Dqn_usize min_block_size; Dqn_usize min_block_size;
Dqn_ArenaBlock *curr; ///< The current memory block of the arena Dqn_ArenaBlock *curr; ///< Active block the arena is allocating from
Dqn_ArenaBlock *tail; ///< The tail memory block of the arena Dqn_ArenaBlock *tail; ///< Last block in the linked list of blocks
Dqn_ArenaStat stats; ///< Current arena stats, reset when reset usage is invoked. Dqn_ArenaStat stats; ///< Current arena stats, reset when reset usage is invoked.
}; };
@ -6316,6 +6316,36 @@ DQN_API Dqn_Allocator Dqn_Arena_Allocator(Dqn_Arena *arena)
return result; return result;
} }
struct Dqn_ArenaBlockResetInfo_
{
bool free_memory;
Dqn_usize used_value;
};
// Calculate the size in bytes required to allocate the memory for the block
// (*not* including the memory required for the user in the block!)
#define Dqn_Arena_BlockMetadataSize_(arena) ((arena)->use_after_free_guard ? (Dqn_PowerOfTwoAlign(sizeof(Dqn_ArenaBlock), DQN_VMEM_PAGE_GRANULARITY)) : sizeof(Dqn_ArenaBlock))
DQN_API void Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_FUNCTION Dqn_ArenaBlock *block, Dqn_ZeroMem zero_mem, Dqn_ArenaBlockResetInfo_ reset_info)
{
if (!block)
return;
if (zero_mem == Dqn_ZeroMem_Yes)
DQN_MEMSET(block->memory, DQN_MEMSET_BYTE, block->commit);
if (reset_info.free_memory) {
Dqn_usize block_metadata_size = Dqn_Arena_BlockMetadataSize_(block->arena);
Dqn_VMem_Release(block, block_metadata_size + block->size);
Dqn_Library_LeakTraceMarkFree(DQN_LEAK_TRACE_ARG block);
} else {
block->used = reset_info.used_value;
// NOTE: Guard all the committed pages again
if (block->arena->use_after_free_guard)
Dqn_VMem_Protect(block->memory, block->commit, Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard);
}
}
DQN_API void Dqn_Arena_Reset(Dqn_Arena *arena, Dqn_ZeroMem zero_mem) DQN_API void Dqn_Arena_Reset(Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
{ {
if (!arena) if (!arena)
@ -6325,14 +6355,8 @@ DQN_API void Dqn_Arena_Reset(Dqn_Arena *arena, Dqn_ZeroMem zero_mem)
for (Dqn_ArenaBlock *block = arena->tail; block; block = block->prev) { for (Dqn_ArenaBlock *block = arena->tail; block; block = block->prev) {
if (!block->prev) if (!block->prev)
arena->curr = block; arena->curr = block;
if (zero_mem == Dqn_ZeroMem_Yes) Dqn_ArenaBlockResetInfo_ reset_info = {};
DQN_MEMSET(block->memory, DQN_MEMSET_BYTE, block->commit); Dqn_Arena_BlockReset_(DQN_LEAK_TRACE block, zero_mem, reset_info);
block->used = 0;
// NOTE: Guard all the committed pages again
if (arena->use_after_free_guard)
Dqn_VMem_Protect(block->memory, block->commit, Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard);
} }
arena->stats.used = 0; arena->stats.used = 0;
@ -6366,30 +6390,26 @@ DQN_API void Dqn_Arena_EndTempMemory_(DQN_LEAK_TRACE_FUNCTION Dqn_ArenaTempMemor
arena->curr = scope.curr; arena->curr = scope.curr;
if (arena->curr) { if (arena->curr) {
Dqn_ArenaBlock *curr = arena->curr; Dqn_ArenaBlock *curr = arena->curr;
curr->used = scope.curr_used; Dqn_ArenaBlockResetInfo_ reset_info = {};
reset_info.used_value = scope.curr_used;
// NOTE: Reguard the pages Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG curr, Dqn_ZeroMem_No, reset_info);
if (arena->use_after_free_guard)
Dqn_VMem_Protect(curr->memory, curr->commit, Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard);
} }
// NOTE: Free the tail blocks until we reach the scope's tail block // NOTE: Free the tail blocks until we reach the scope's tail block
while (arena->tail != scope.tail) { while (arena->tail != scope.tail) {
Dqn_ArenaBlock *tail = arena->tail; Dqn_ArenaBlock *tail = arena->tail;
arena->tail = tail->prev; arena->tail = tail->prev;
Dqn_VMem_Release(tail, sizeof(*tail) + tail->size); Dqn_ArenaBlockResetInfo_ reset_info = {};
Dqn_Library_LeakTraceMarkFree(DQN_LEAK_TRACE_ARG tail); reset_info.free_memory = true;
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG tail, Dqn_ZeroMem_No, reset_info);
} }
// NOTE: Reset the usage of all the blocks between the tail and current block's // NOTE: Reset the usage of all the blocks between the tail and current block's
if (arena->tail) { if (arena->tail) {
arena->tail->next = nullptr; arena->tail->next = nullptr;
for (Dqn_ArenaBlock *block = arena->tail; block && block != arena->curr; block = block->prev) { for (Dqn_ArenaBlock *block = arena->tail; block && block != arena->curr; block = block->prev) {
block->used = 0; Dqn_ArenaBlockResetInfo_ reset_info = {};
Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG block, Dqn_ZeroMem_No, reset_info);
// NOTE: Reguard the pages
if (arena->use_after_free_guard)
Dqn_VMem_Protect(block->memory, block->commit, Dqn_VMemPage_ReadWrite | Dqn_VMemPage_Guard);
} }
} }
} }
@ -6463,10 +6483,7 @@ DQN_API Dqn_ArenaBlock *Dqn_Arena_Grow_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena
if (!arena || size == 0) if (!arena || size == 0)
return nullptr; return nullptr;
Dqn_usize block_metadata_size = sizeof(Dqn_ArenaBlock); Dqn_usize block_metadata_size = Dqn_Arena_BlockMetadataSize_(arena);
if (arena->use_after_free_guard)
block_metadata_size = Dqn_PowerOfTwoAlign(block_metadata_size, DQN_VMEM_PAGE_GRANULARITY);
commit = DQN_MIN(commit, size); commit = DQN_MIN(commit, size);
Dqn_usize reserve_aligned = Dqn_PowerOfTwoAlign(size + block_metadata_size, DQN_VMEM_RESERVE_GRANULARITY); Dqn_usize reserve_aligned = Dqn_PowerOfTwoAlign(size + block_metadata_size, DQN_VMEM_RESERVE_GRANULARITY);
Dqn_usize commit_aligned = Dqn_PowerOfTwoAlign(commit + block_metadata_size, DQN_VMEM_COMMIT_GRANULARITY); Dqn_usize commit_aligned = Dqn_PowerOfTwoAlign(commit + block_metadata_size, DQN_VMEM_COMMIT_GRANULARITY);
@ -6498,9 +6515,9 @@ DQN_API Dqn_ArenaBlock *Dqn_Arena_Grow_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena
result->flags = flags; result->flags = flags;
result->arena = arena; result->arena = arena;
// NOTE: Guard all the committed pages in the memory block // NOTE: Reset the block (this will guard the memory pages if required, otherwise no-op).
if (arena->use_after_free_guard) Dqn_ArenaBlockResetInfo_ reset_info = {};
Dqn_VMem_Protect(result->memory, result->commit, page_flags | Dqn_VMemPage_Guard); Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG result, Dqn_ZeroMem_No, reset_info);
// NOTE: Attach the block to the arena // NOTE: Attach the block to the arena
if (arena->tail) { if (arena->tail) {
@ -6579,12 +6596,11 @@ DQN_API void Dqn_Arena_Free_(DQN_LEAK_TRACE_FUNCTION Dqn_Arena *arena, Dqn_ZeroM
return; return;
while (arena->tail) { while (arena->tail) {
Dqn_ArenaBlock *tail = arena->tail; Dqn_ArenaBlock *block = arena->tail;
arena->tail = tail->prev; arena->tail = block->prev;
if (zero_mem == Dqn_ZeroMem_Yes) Dqn_ArenaBlockResetInfo_ reset_info = {};
DQN_MEMSET(tail->memory, DQN_MEMSET_BYTE, tail->commit); reset_info.free_memory = true;
Dqn_VMem_Release(tail, sizeof(*tail) + tail->size); Dqn_Arena_BlockReset_(DQN_LEAK_TRACE_ARG block, zero_mem, reset_info);
Dqn_Library_LeakTraceMarkFree(DQN_LEAK_TRACE_ARG tail);
} }
arena->curr = arena->tail = nullptr; arena->curr = arena->tail = nullptr;