ensure rd frame depth is always computed correctly; eliminate incorrect frame scope (ctrl/di) usage - fix weird deadlocks!

This commit is contained in:
Ryan Fleury
2025-05-19 16:21:38 -07:00
parent b9e3df4cae
commit 47d658daed
3 changed files with 115 additions and 73 deletions
+97 -45
View File
@@ -139,10 +139,15 @@ hs_root_alloc(void)
internal void
hs_root_release(HS_Root root)
{
//- rjf: unpack root
U64 slot_idx = root.u64[1]%hs_shared->root_slots_count;
U64 stripe_idx = slot_idx%hs_shared->root_stripes_count;
HS_RootSlot *slot = &hs_shared->root_slots[slot_idx];
HS_Stripe *stripe = &hs_shared->root_stripes[stripe_idx];
//- rjf: release root node, grab its arena / ID list
Arena *root_arena = 0;
HS_RootIDChunkList root_ids = {0};
OS_MutexScopeW(stripe->rw_mutex)
{
for(HS_RootNode *n = slot->first; n != 0; n = n->next)
@@ -150,12 +155,62 @@ hs_root_release(HS_Root root)
if(MemoryMatchStruct(&root, &n->root))
{
DLLRemove(slot->first, slot->last, n);
arena_release(n->arena);
root_arena = n->arena;
root_ids = n->ids;
SLLStackPush(hs_shared->root_stripes_free_nodes[stripe_idx], n);
break;
}
}
}
//- rjf: release all IDs
for(HS_RootIDChunkNode *id_chunk_n = root_ids.first; id_chunk_n != 0; id_chunk_n = id_chunk_n->next)
{
for EachIndex(chunk_idx, id_chunk_n->count)
{
HS_ID id = id_chunk_n->v[chunk_idx];
HS_Key key = hs_key_make(root, id);
U64 key_hash = hs_little_hash_from_data(str8_struct(&key));
U64 key_slot_idx = key_hash%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
OS_MutexScopeW(key_stripe->rw_mutex)
{
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(hs_key_match(n->key, key))
{
// rjf: release reference to all hashes
for(U64 history_idx = 0; history_idx < HS_KEY_HASH_HISTORY_STRONG_REF_COUNT && history_idx < n->hash_history_gen; history_idx += 1)
{
U128 hash = n->hash_history[(n->hash_history_gen+history_idx)%ArrayCount(n->hash_history)];
U64 hash_slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 hash_stripe_idx = hash_slot_idx%hs_shared->stripes_count;
HS_Slot *hash_slot = &hs_shared->slots[hash_slot_idx];
HS_Stripe *hash_stripe = &hs_shared->stripes[hash_stripe_idx];
OS_MutexScopeR(hash_stripe->rw_mutex)
{
for(HS_Node *n = hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
// rjf: release key node
DLLRemove(key_slot->first, key_slot->last, n);
SLLStackPush(hs_shared->key_stripes_free_nodes[key_stripe_idx], n);
break;
}
}
}
}
}
}
////////////////////////////////
@@ -226,6 +281,8 @@ hs_submit_data(HS_Key key, Arena **data_arena, String8 data)
U128 key_expired_hash = {0};
ProfScope("commit this hash to key cache") OS_MutexScopeW(key_stripe->rw_mutex)
{
// rjf: find existing key
B32 key_is_new = 0;
HS_KeyNode *key_node = 0;
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
@@ -235,8 +292,11 @@ hs_submit_data(HS_Key key, Arena **data_arena, String8 data)
break;
}
}
// rjf: create key node if it doesn't exist
if(!key_node)
{
key_is_new = 1;
key_node = hs_shared->key_stripes_free_nodes[key_stripe_idx];
if(key_node)
{
@@ -249,6 +309,8 @@ hs_submit_data(HS_Key key, Arena **data_arena, String8 data)
key_node->key = key;
DLLPushBack(key_slot->first, key_slot->last, key_node);
}
// rjf: push hash into key's history
if(key_node)
{
if(key_node->hash_history_gen >= HS_KEY_HASH_HISTORY_STRONG_REF_COUNT)
@@ -258,6 +320,40 @@ hs_submit_data(HS_Key key, Arena **data_arena, String8 data)
key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)] = hash;
key_node->hash_history_gen += 1;
}
// rjf: key is new -> add this key to the associated root
if(key_is_new)
{
U64 root_hash = hs_little_hash_from_data(str8_struct(&key.root));
U64 root_slot_idx = root_hash%hs_shared->root_slots_count;
U64 root_stripe_idx = root_slot_idx%hs_shared->root_stripes_count;
HS_RootSlot *root_slot = &hs_shared->root_slots[root_slot_idx];
HS_Stripe *root_stripe = &hs_shared->root_stripes[root_stripe_idx];
OS_MutexScopeW(root_stripe->rw_mutex)
{
for(HS_RootNode *n = root_slot->first; n != 0; n = n->next)
{
if(MemoryMatchStruct(&n->root, &key.root))
{
HS_RootIDChunkNode *chunk = n->ids.last;
if(chunk == 0 || chunk->count >= chunk->cap)
{
chunk = push_array(n->arena, HS_RootIDChunkNode, 1);
SLLQueuePush(n->ids.first, n->ids.last, chunk);
n->ids.chunk_count += 1;
chunk->cap = 1024;
chunk->v = push_array_no_zero(n->arena, HS_ID, chunk->cap);
}
chunk->v[chunk->count] = key.id;
key_node->root_id_chunk_node = chunk;
key_node->root_id_chunk_idx = chunk->count;
chunk->count += 1;
n->ids.total_count += 1;
break;
}
}
}
}
}
//- rjf: decrement key ref count of expired hash
@@ -354,50 +450,6 @@ hs_scope_touch_node__stripe_r_guarded(HS_Scope *scope, HS_Node *node)
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Key Closing
internal void
hs_key_close(HS_Key key)
{
U64 key_hash = hs_little_hash_from_data(str8_struct(&key));
U64 key_slot_idx = key_hash%hs_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%hs_shared->key_stripes_count;
HS_KeySlot *key_slot = &hs_shared->key_slots[key_slot_idx];
HS_Stripe *key_stripe = &hs_shared->key_stripes[key_stripe_idx];
OS_MutexScopeW(key_stripe->rw_mutex)
{
for(HS_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(hs_key_match(n->key, key))
{
for(U64 history_idx = 0; history_idx < HS_KEY_HASH_HISTORY_STRONG_REF_COUNT && history_idx < n->hash_history_gen; history_idx += 1)
{
U128 hash = n->hash_history[(n->hash_history_gen+history_idx)%ArrayCount(n->hash_history)];
U64 hash_slot_idx = hash.u64[1]%hs_shared->slots_count;
U64 hash_stripe_idx = hash_slot_idx%hs_shared->stripes_count;
HS_Slot *hash_slot = &hs_shared->slots[hash_slot_idx];
HS_Stripe *hash_stripe = &hs_shared->stripes[hash_stripe_idx];
OS_MutexScopeR(hash_stripe->rw_mutex)
{
for(HS_Node *n = hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
DLLRemove(key_slot->first, key_slot->last, n);
SLLStackPush(hs_shared->key_stripes_free_nodes[key_stripe_idx], n);
break;
}
}
}
}
////////////////////////////////
//~ rjf: Downstream Accesses
+3 -6
View File
@@ -70,7 +70,7 @@ typedef struct HS_RootIDChunkNode HS_RootIDChunkNode;
struct HS_RootIDChunkNode
{
HS_RootIDChunkNode *next;
U128 *v;
HS_ID *v;
U64 count;
U64 cap;
};
@@ -109,6 +109,8 @@ struct HS_KeyNode
{
HS_KeyNode *next;
HS_KeyNode *prev;
HS_RootIDChunkNode *root_id_chunk_node;
U64 root_id_chunk_idx;
HS_Key key;
U128 hash_history[HS_KEY_HASH_HISTORY_COUNT];
U64 hash_history_gen;
@@ -250,11 +252,6 @@ internal HS_Scope *hs_scope_open(void);
internal void hs_scope_close(HS_Scope *scope);
internal void hs_scope_touch_node__stripe_r_guarded(HS_Scope *scope, HS_Node *node);
////////////////////////////////
//~ rjf: Key Closing
internal void hs_key_close(HS_Key key);
////////////////////////////////
//~ rjf: Downstream Accesses
+15 -22
View File
@@ -5778,7 +5778,6 @@ rd_window_state_from_cfg(RD_Cfg *cfg)
if(window_cfg != &rd_nil_cfg && ws == &rd_nil_window_state)
{
Temp scratch = scratch_begin(0, 0);
rd_state->frame_depth += 1;
// rjf: unpack configuration options
B32 has_pos = 0;
@@ -5853,7 +5852,6 @@ rd_window_state_from_cfg(RD_Cfg *cfg)
DLLPushBack_NPZ(&rd_nil_window_state, rd_state->first_window_state, rd_state->last_window_state, ws, order_next, order_prev);
DLLPushBack_NP(slot->first, slot->last, ws, hash_next, hash_prev);
rd_state->frame_depth -= 1;
scratch_end(scratch);
}
@@ -11026,6 +11024,7 @@ rd_frame(void)
ProfBeginFunction();
Temp scratch = scratch_begin(0, 0);
log_scope_begin();
rd_state->frame_depth += 1;
//////////////////////////////
//- rjf: (DEBUG) take top-level cfg roots, stringize them, and store them to hash store
@@ -11076,7 +11075,7 @@ rd_frame(void)
//////////////////////////////
//- rjf: iterate all tabs, touch their view-states
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
Temp scratch = scratch_begin(0, 0);
RD_CfgList windows = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("window"));
@@ -11103,7 +11102,7 @@ rd_frame(void)
//////////////////////////////
//- rjf: garbage collect untouched immediate cfg trees
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
RD_Cfg *transient = rd_cfg_child_from_string(rd_state->root_cfg, str8_lit("transient"));
for(RD_Cfg *tln = transient->first, *next = &rd_nil_cfg; tln != &rd_nil_cfg; tln = next)
@@ -11136,7 +11135,7 @@ rd_frame(void)
//////////////////////////////
//- rjf: garbage collect untouched view states
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
for EachIndex(slot_idx, rd_state->view_state_slots_count)
{
@@ -11195,7 +11194,7 @@ rd_frame(void)
//////////////////////////////
//- rjf: animate all views
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
F32 slow_rate = 1 - pow_f32(2, (-10.f * rd_state->frame_dt));
F32 fast_rate = 1 - pow_f32(2, (-40.f * rd_state->frame_dt));
@@ -11242,7 +11241,7 @@ rd_frame(void)
//- rjf: get events from the OS
//
OS_EventList events = {0};
if(rd_state->frame_depth == 0) DeferLoop(rd_state->frame_depth += 1, rd_state->frame_depth -= 1)
if(rd_state->frame_depth == 1)
{
events = os_get_events(scratch.arena, rd_state->num_frames_requested == 0);
}
@@ -11250,8 +11249,10 @@ rd_frame(void)
//////////////////////////////
//- rjf: open frame scopes
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
if(rd_state->frame_di_scope) { di_scope_close(rd_state->frame_di_scope); }
if(rd_state->frame_ctrl_scope) { ctrl_scope_close(rd_state->frame_ctrl_scope); }
rd_state->frame_di_scope = di_scope_open();
rd_state->frame_ctrl_scope = ctrl_scope_open();
}
@@ -12456,7 +12457,7 @@ rd_frame(void)
////////////////////////////
//- rjf: process top-level graphical commands
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
for(;rd_next_cmd(&cmd);) RD_RegsScope()
{
@@ -16512,7 +16513,7 @@ rd_frame(void)
// the commands pushed by the view will be in the queue, and the core can
// treat that queue as r/w again.
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
// rjf: rotate
{
@@ -16714,15 +16715,6 @@ rd_frame(void)
rd_state->num_frames_requested -= 1;
}
//////////////////////////////
//- rjf: close frame scopes
//
if(rd_state->frame_depth == 0)
{
di_scope_close(rd_state->frame_di_scope);
ctrl_scope_close(rd_state->frame_ctrl_scope);
}
//////////////////////////////
//- rjf: submit rendering to all windows
//
@@ -16741,7 +16733,7 @@ rd_frame(void)
//////////////////////////////
//- rjf: show windows after first frame
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
RD_CfgIDList windows_to_show = {0};
for(RD_WindowState *w = rd_state->first_window_state; w != &rd_nil_window_state; w = w->order_next)
@@ -16755,7 +16747,7 @@ rd_frame(void)
{
RD_Cfg *window = rd_cfg_from_id(n->v);
RD_WindowState *ws = rd_window_state_from_cfg(window);
DeferLoop(rd_state->frame_depth += 1, rd_state->frame_depth -= 1) os_window_first_paint(ws->os);
os_window_first_paint(ws->os);
}
}
@@ -16776,7 +16768,7 @@ rd_frame(void)
//////////////////////////////
//- rjf: bump command batch ring buffer generation
//
if(rd_state->frame_depth == 0)
if(rd_state->frame_depth == 1)
{
rd_state->cmds_gen += 1;
}
@@ -16809,6 +16801,7 @@ rd_frame(void)
}
#endif
rd_state->frame_depth -= 1;
scratch_end(scratch);
ProfEnd();
}