artifact_cache: fix leak of old-generation artifacts, when those artifacts are replaced by newer generation computations. artifacts can opaquely hold things like arena pointers, and so when we replace them in the cache, we need to respect the lookup api's provision of create/destroy hooks for the cache, such that the artifacts can be released correctly (using the destroy hook) before being replaced. this is separate from regular eviction.

This commit is contained in:
Ryan Fleury
2026-05-11 11:49:15 -07:00
parent 6a95da35e0
commit 52da228efe
8 changed files with 157 additions and 24 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ commands =
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
// .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
.f1 = { .win = "build raddbg opengl", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
//- rjf: [raddbg wsl] //- rjf: [raddbg wsl]
// .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
+24
View File
@@ -422,6 +422,18 @@ ac_async_tick(void)
{ {
if(str8_match(n->key, r->key, 0)) if(str8_match(n->key, r->key, 0))
{ {
// rjf: eliminate existing values, if nay
if(cache->destroy && ins_atomic_u64_eval(&n->completion_count) > 0) for(;;)
{
if(access_pt_is_expired(&n->access_pt, .time = 0, .update_idxs = 0))
{
cache->destroy(n->val);
break;
}
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, max_U64);
}
// rjf: write new value
n->last_completed_gen = gen; n->last_completed_gen = gen;
n->val = val; n->val = val;
ins_atomic_u64_dec_eval(&n->working_count); ins_atomic_u64_dec_eval(&n->working_count);
@@ -531,6 +543,18 @@ ac_async_tick(void)
{ {
if(str8_match(n->key, r->key, 0)) if(str8_match(n->key, r->key, 0))
{ {
// rjf: eliminate existing values, if nay
if(cache->destroy && ins_atomic_u64_eval(&n->completion_count) > 0) for(;;)
{
if(access_pt_is_expired(&n->access_pt, .time = 0, .update_idxs = 0))
{
cache->destroy(n->val);
break;
}
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, max_U64);
}
// rjf: store
n->last_completed_gen = gen; n->last_completed_gen = gen;
n->val = val; n->val = val;
ins_atomic_u64_dec_eval(&n->working_count); ins_atomic_u64_dec_eval(&n->working_count);
+78
View File
@@ -1,6 +1,25 @@
// Copyright (c) Epic Games Tools // Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Global Arena Table
#if ARENA_TABLE_DEBUG
typedef struct ArenaTableNode ArenaTableNode;
struct ArenaTableNode
{
ArenaTableNode *next;
Arena *arena;
};
global U64 arena_table_take_init = 0;
global U64 arena_table_inited = 0;
global U64 arena_table_lock = 0;
global ArenaTableNode *arena_table = 0;
global U64 arena_table_count = 0;
global U64 arena_table_cap = 0;
global ArenaTableNode *free_arena_table_node = 0;
#endif
//////////////////////////////// ////////////////////////////////
//~ rjf: Arena Functions //~ rjf: Arena Functions
@@ -73,6 +92,47 @@ arena_alloc_(ArenaParams *params)
#if ARENA_FREE_LIST #if ARENA_FREE_LIST
arena->free_last = 0; arena->free_last = 0;
#endif #endif
// rjf: store in global arena table
#if ARENA_TABLE_DEBUG
if(ins_atomic_u64_eval_cond_assign(&arena_table_take_init, 1, 0) == 0)
{
arena_table = reserve_memory(GB(256));
arena_table_cap = 4096;
commit_memory(arena_table, arena_table_cap * sizeof(arena_table[0]));
ins_atomic_u64_inc_eval(&arena_table_inited);
}
for(;!ins_atomic_u64_eval(&arena_table_inited);) {}
for(;;)
{
B32 got_lock = (ins_atomic_u64_eval_cond_assign(&arena_table_lock, 1, 0) == 0);
if(got_lock)
{
ArenaTableNode *node = free_arena_table_node;
if(node != 0)
{
SLLStackPop(free_arena_table_node);
}
else
{
node = &arena_table[arena_table_count];
if(arena_table_count >= arena_table_cap)
{
U64 arena_table_cap__pre_grow = arena_table_cap;
arena_table_cap *= 2;
U64 arena_table_cap__post_grow = arena_table_cap;
commit_memory(arena_table + arena_table_cap__pre_grow, sizeof(arena_table[0]) * (arena_table_cap__post_grow - arena_table_cap__pre_grow));
}
arena_table_count += 1;
}
node->arena = arena;
arena->table_node = node;
ins_atomic_u64_eval_assign(&arena_table_lock, 0);
break;
}
}
#endif
return arena; return arena;
} }
@@ -87,6 +147,24 @@ arena_release(Arena *arena)
} }
#endif #endif
#if ARENA_TABLE_DEBUG
for(Arena *n = arena->current; n != 0; n = n->prev)
{
for(;;)
{
B32 got_lock = (ins_atomic_u64_eval_cond_assign(&arena_table_lock, 1, 0) == 0);
if(got_lock)
{
ArenaTableNode *table_node = n->table_node;
MemoryZeroStruct(table_node);
SLLStackPush(free_arena_table_node, table_node);
ins_atomic_u64_eval_assign(&arena_table_lock, 0);
break;
}
}
}
#endif
for(Arena *n = arena->current, *prev = 0; n != 0; n = prev) for(Arena *n = arena->current, *prev = 0; n != 0; n = prev)
{ {
prev = n->prev; prev = n->prev;
+3
View File
@@ -46,6 +46,9 @@ struct Arena
#if ARENA_FREE_LIST #if ARENA_FREE_LIST
Arena *free_last; Arena *free_last;
#endif #endif
#if ARENA_TABLE_DEBUG
struct ArenaTableNode *table_node;
#endif
}; };
StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check); StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check);
+2 -2
View File
@@ -19,8 +19,8 @@ tctx_alloc(void)
{ {
#if PROFILE_TELEMETRY #if PROFILE_TELEMETRY
thread_static static char name[2][1024]; thread_static static char name[2][1024];
raddbg_snprintf(name[0], sizeof(name[0]), "Scratch/0[TID:%u]", os_tid()); raddbg_snprintf(name[0], sizeof(name[0]), "Scratch/0[TID:%u]", tid());
raddbg_snprintf(name[1], sizeof(name[1]), "Scratch/1[TID:%u]", os_tid()); raddbg_snprintf(name[1], sizeof(name[1]), "Scratch/1[TID:%u]", tid());
Arena *arena_0 = arena_alloc(.name = name[0]); Arena *arena_0 = arena_alloc(.name = name[0]);
Arena *arena_1 = arena_alloc(.name = name[1]); Arena *arena_1 = arena_alloc(.name = name[1]);
#else #else
+1 -1
View File
@@ -6820,7 +6820,7 @@ d_call_stack_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U6
good = 1; good = 1;
call_stack[0] = d_call_stack_from_unwind(arena, process, &unwind); call_stack[0] = d_call_stack_from_unwind(arena, process, &unwind);
} }
if(unwind.flags & D_UnwindFlag_Stale) else
{ {
retry = 1; retry = 1;
} }
+43 -15
View File
@@ -4,19 +4,45 @@
//////////////////////////////// ////////////////////////////////
//~ rjf: post-0.9.25 TODO notes //~ rjf: post-0.9.25 TODO notes
// //
// [ ] "autos" collection, which can be evaluated
// [ ] show "autos" inline in source code near thread?
// [ ] step out of scopes / loops
// [ ] memory_size(...) view for quickly evaluating memory sizes
// [ ] value coloring view in watch window, so you can quickly scroll & see values outside of a threshold
//
// [ ] project/user file improvements
// [ ] "default" -> "untitled"
// [ ] should clear default project data every time the program starts
// [ ] new project / user should not require picking a path; should just
// by default go to "untitled" in default user path
// [ ] more things should move to user data, but project-tagged - like
// recent files, watches?, etc.
//
// [ ] killing/restarting thread performance (#780)
// [ ] codebase-internal barrier impl (win7/linux support)
// [ ] PDB -> RDI conversion memory usage
// [ ] policy for closing debug info which is no longer relevant?
// [ ] external window focusing bugs
//
// [ ] linux/dwarf fixes // [ ] linux/dwarf fixes
// [ ] excessive CPU usage on async threads - barrier impl?
// [ ] step-over/step-into doesn't step successfully in many cases, just causes a continue // [ ] step-over/step-into doesn't step successfully in many cases, just causes a continue
// [ ] rd_frame has no type info, should be (void -> void) - type should be generated but it // [x] rd_frame has no type info, should be (void -> void) - type should be generated but it
// is not being hooked up correctly // is not being hooked up correctly
// [ ] type views for `MyByte *` example do not match correctly // [ ] type views for `MyByte *` example do not match correctly
// //
// [ ] many threads hitting conditional breakpoints -> causes 0x8000003 exception! // [ ] many threads hitting conditional breakpoints -> causes 0x8000003 exception!
// [ ] string conditional breakpoints -> size != 0 check seems to fail, can test w/ "rd_init" subprogram type gen in d2r2 // [ ] string conditional breakpoints -> size != 0 check seems to fail, can test w/ "rd_init" subprogram type gen in d2r2
// [ ] no selected thread -> causing evaluation failures, e.g. with go-to-definition
// //
//- evaluation space coverage pass //- evaluation space coverage pass
// [ ] eval space reads/writes -> needs staleness/badness info - replace ctrl layer, to apply to all spaces // [ ] eval space reads/writes -> needs staleness/badness info - replace ctrl layer, to apply to all spaces
// [ ] need concrete ways of referring into a space at any offset - e.g. `process.memory + 0x1234`, `file:"foo".data + 0x1234`, `thread.regs + 0x80`, etc. // [ ] need concrete ways of referring into a space at any offset - e.g. `process.memory + 0x1234`, `file:"foo".data + 0x1234`, `thread.regs + 0x80`, etc.
// [ ] memory view needs to take advantage of above when peeking; ensure peeking works on files etc. // [ ] memory view needs to take advantage of above when peeking; ensure peeking works on files etc.
// [ ] need to eliminate accelerators from evaluation context, and build them on the fly instead -
// this is necessary because, for instance, the correct locals_map varies by expression, if we
// want to (we do) support features like "look up call stack to find local"
// [ ] unit / module name qualification
// //
//- memory view pass //- memory view pass
// [ ] toggleable ascii column // [ ] toggleable ascii column
@@ -31,10 +57,10 @@
// [ ] signify empty watch window "expression" slot more as a text field? // [ ] signify empty watch window "expression" slot more as a text field?
// //
//- jeff notes //- jeff notes
// [ ] option to prefer addresses first with string ptrs
// [ ] focus changing on f10/f11? may be related to auto_run/auto_step - look at a bin/jeffr // [ ] focus changing on f10/f11? may be related to auto_run/auto_step - look at a bin/jeffr
// [ ] option to turn off transient tabs altogether
// [ ] single-line viz for pointers w/ bad (unmapped) addresses // [ ] single-line viz for pointers w/ bad (unmapped) addresses
// [ ] option to prefer addresses first with string ptrs
// [x] option to turn off transient tabs altogether
// //
//- namespace/locations/variables RDI pass //- namespace/locations/variables RDI pass
// [x] RDI_Local, RDI_GlobalVariable, RDI_ThreadVariable -> RDI_Variable // [x] RDI_Local, RDI_GlobalVariable, RDI_ThreadVariable -> RDI_Variable
@@ -58,12 +84,12 @@
//- urgent fixes //- urgent fixes
// [ ] (use msvc assert as an example) show fastfail exception info (code, name, etc.) - comes from ExceptionInformation @fastfail // [ ] (use msvc assert as an example) show fastfail exception info (code, name, etc.) - comes from ExceptionInformation @fastfail
// [ ] stepping w/ spoofs & shadow stack enabled - writing spoof will send a stack buffer overrun event @shadow_stack_step // [ ] stepping w/ spoofs & shadow stack enabled - writing spoof will send a stack buffer overrun event @shadow_stack_step
// [ ] hardware breakpoints regression (global eval in ctrl)
// [ ] native filesystem dialog, resizing raddbg window -> crash! // [ ] native filesystem dialog, resizing raddbg window -> crash!
// [ ] stdout/stderr path target setting is now busted >:( // [ ] stdout/stderr path target setting is now busted >:( (i think this is because of path confusion? check working dir)
// [ ] target ui entry point should override built-in entry point // [ ] target ui entry point should override built-in entry point
// [ ] list of all tabs in palette // [ ] list of all tabs in palette
// [ ] u64 + (ptr - ptr) seems to produce unexpected results - double check with C rules? // [ ] u64 + (ptr - ptr) seems to produce unexpected results - double check with C rules?
// [x] hardware breakpoints regression (global eval in ctrl)
// //
//- flow notes //- flow notes
// [ ] "skip breakpoint, run to source", when stopped at a non-source location // [ ] "skip breakpoint, run to source", when stopped at a non-source location
@@ -74,9 +100,9 @@
//- memory view //- memory view
// [ ] have smaller visible range than entire memory space, within some bounds (e.g. 64KB) // [ ] have smaller visible range than entire memory space, within some bounds (e.g. 64KB)
// [ ] dynamically expand memory space, based on scrolling // [ ] dynamically expand memory space, based on scrolling
// [ ] fix clicking through occluded panels etc.
// [ ] disambiguate . character in ASCII columns // [ ] disambiguate . character in ASCII columns
// [ ] fix type intepretations of cursor in bottom pane // [x] fix type intepretations of cursor in bottom pane
// [x] fix clicking through occluded panels etc.
// //
//- watch improvements //- watch improvements
// [ ] *ALL* expressions in watch windows need to be editable. // [ ] *ALL* expressions in watch windows need to be editable.
@@ -203,21 +229,21 @@
// [ ] search-in-all-files // [ ] search-in-all-files
// [ ] automatically snap to search matches when searching source files // [ ] automatically snap to search matches when searching source files
// [ ] memory view // [ ] memory view
// [ ] memory view mutation controls
// [ ] memory view user-made annotations // [ ] memory view user-made annotations
// [ ] memory view searching // [ ] memory view searching
// [x] memory view mutation controls
// [ ] disasm view // [ ] disasm view
// [ ] visualize jump destinations in disasm // [ ] visualize jump destinations in disasm
// //
//- longer-term future features //- longer-term future features
// [ ] long-term future notes from martins // [ ] long-term future notes from martins
// [ ] core dump saving/loading // [ ] core dump saving/loading
// [ ] parallel call stacks view // [ ] parallel call stacks view
// [ ] parallel watch view // [ ] parallel watch view
// [ ] mixed native/interpreted/jit debugging // [ ] mixed native/interpreted/jit debugging
// - it seems python has a top-level linked list of interpreter states, // - it seems python has a top-level linked list of interpreter states,
// which should allow the debugger to map native callstacks to python // which should allow the debugger to map native callstacks to python
// code // code
// //
//- code cleanup //- code cleanup
// [ ] eliminate explicit font parameters in the various ui paths (e.g. // [ ] eliminate explicit font parameters in the various ui paths (e.g.
@@ -257,6 +283,8 @@
#define FNT_INIT_MANUAL 1 #define FNT_INIT_MANUAL 1
#define RD_INIT_MANUAL 1 #define RD_INIT_MANUAL 1
#define ARENA_TABLE_DEBUG BUILD_DEBUG
//////////////////////////////// ////////////////////////////////
//~ rjf: Includes //~ rjf: Includes
+4 -4
View File
@@ -278,11 +278,11 @@ internal R_OGL_FormatInfo r_ogl_format_info_from_tex2dformat(R_Tex2DFormat fmt);
internal GLuint r_ogl_instance_buffer_from_size(U64 size); internal GLuint r_ogl_instance_buffer_from_size(U64 size);
internal void r_ogl_debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam); internal void r_ogl_debug_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam);
#define glUseProgramScope(...) DeferLoop(glUseProgram(__VA_ARGS__), glUseProgram(0)) #define glUseProgramScope(...) DeferLoop(glUseProgram(__VA_ARGS__), glUseProgram(0))
#define glBindVertexArrayScope(...) DeferLoop(glBindVertexArray(__VA_ARGS__), glBindVertexArray(0)) #define glBindVertexArrayScope(...) DeferLoop(glBindVertexArray(__VA_ARGS__), glBindVertexArray(0))
#define glBindFramebufferScope(target, ...) DeferLoop(glBindFramebuffer((target), __VA_ARGS__), glBindFramebuffer((target), 0)) #define glBindFramebufferScope(target, ...) DeferLoop(glBindFramebuffer((target), __VA_ARGS__), glBindFramebuffer((target), 0))
#define glBindTextureScope(target, ...) DeferLoop(glBindTexture((target), __VA_ARGS__), glBindTexture((target), 0)) #define glBindTextureScope(target, ...) DeferLoop(glBindTexture((target), __VA_ARGS__), glBindTexture((target), 0))
#define glEnableScope(...) DeferLoop(glEnable(__VA_ARGS__), glDisable(__VA_ARGS__)) #define glEnableScope(...) DeferLoop(glEnable(__VA_ARGS__), glDisable(__VA_ARGS__))
//////////////////////////////// ////////////////////////////////
//~ rjf: OS-Specific Hooks //~ rjf: OS-Specific Hooks