eliminate bifurcated rw lock path based on exclusive mode; promote thread operations to base layer, use os layer as impl; first pass on moving file streaming layer to base layer's async wavefront

This commit is contained in:
Ryan Fleury
2025-09-17 14:47:55 -07:00
parent 99c989a3c3
commit 0d15b8670b
40 changed files with 450 additions and 607 deletions
+57 -12
View File
@@ -2,18 +2,23 @@
// Licensed under the MIT license (https://opensource.org/license/mit/)
global U64 global_update_tick_idx = 0;
global CondVar async_tick_cond_var = {0};
global Mutex async_tick_mutex = {0};
global CondVar async_tick_start_cond_var = {0};
global CondVar async_tick_stop_cond_var = {0};
global Mutex async_tick_start_mutex = {0};
global Mutex async_tick_stop_mutex = {0};
global B32 global_async_exit = 0;
internal void
main_thread_base_entry_point(int arguments_count, char **arguments)
{
ThreadNameF("main_thread");
Temp scratch = scratch_begin(0, 0);
ThreadNameF("[main thread]");
//- rjf: set up async thread group info
async_tick_cond_var = cond_var_alloc();
async_tick_mutex = mutex_alloc();
async_tick_start_cond_var = cond_var_alloc();
async_tick_start_mutex = mutex_alloc();
async_tick_stop_cond_var = cond_var_alloc();
async_tick_stop_mutex = mutex_alloc();
//- rjf: set up telemetry
#if PROFILE_TELEMETRY
@@ -29,7 +34,13 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
#endif
//- rjf: parse command line
String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, arguments_count, arguments);
String8List command_line_argument_strings = {0};
{
for EachIndex(idx, arguments_count)
{
str8_list_push(scratch.arena, &command_line_argument_strings, str8_cstring(arguments[idx]));
}
}
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
//- rjf: begin captures
@@ -37,12 +48,9 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
if(capture)
{
ProfBeginCapture(arguments[0]);
ProfMsg(BUILD_TITLE);
}
#if PROFILE_TELEMETRY
tmMessage(0, TMMF_ICON_NOTE, BUILD_TITLE);
#endif
//- rjf: initialize all included layers
#if defined(ASYNC_H) && !defined(ASYNC_INIT_MANUAL)
async_init(&cmdline);
@@ -96,9 +104,42 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
rd_init(&cmdline);
#endif
//- rjf: launch async threads
Thread *async_threads = 0;
U64 async_threads_count = 0;
{
U64 num_main_threads = 1;
#if defined(CTRL_CORE_H)
num_main_threads += 1;
#endif
U64 num_async_threads = os_get_system_info()->logical_processor_count;
U64 num_main_threads_clamped = Min(num_async_threads, num_main_threads);
num_async_threads -= num_main_threads_clamped;
num_async_threads = Max(1, num_async_threads);
Barrier barrier = barrier_alloc(num_async_threads);
LaneCtx *lane_ctxs = push_array(scratch.arena, LaneCtx, num_async_threads);
async_threads_count = num_async_threads;
async_threads = push_array(scratch.arena, Thread, async_threads_count);
for EachIndex(idx, num_async_threads)
{
lane_ctxs[idx].lane_idx = idx;
lane_ctxs[idx].lane_count = num_async_threads;
lane_ctxs[idx].barrier = barrier;
async_threads[idx] = thread_launch(async_thread_entry_point, &lane_ctxs[idx]);
}
}
//- rjf: call into entry point
entry_point(&cmdline);
//- rjf: join async threads
ins_atomic_u32_inc_eval(&global_async_exit);
cond_var_broadcast(async_tick_start_cond_var);
for EachIndex(idx, async_threads_count)
{
thread_join(async_threads[idx], max_U64);
}
//- rjf: end captures
if(capture)
{
@@ -143,11 +184,15 @@ update(void)
internal void
async_thread_entry_point(void *params)
{
MutexScope(async_tick_mutex) for(;;)
LaneCtx lctx = *(LaneCtx *)params;
lane_ctx(lctx);
ThreadNameF("async_thread_%I64u", lane_idx());
for(;!ins_atomic_u32_eval(&global_async_exit);)
{
cond_var_wait(async_tick_cond_var, async_tick_mutex, max_U64);
MutexScope(async_tick_start_mutex) cond_var_wait(async_tick_start_cond_var, async_tick_start_mutex, os_now_microseconds()+100000);
#if defined(FILE_STREAM_H)
fs_async_tick();
#endif
cond_var_broadcast(async_tick_stop_cond_var);
}
}
+1 -1
View File
@@ -12,7 +12,7 @@
#include "base_arena.c"
#include "base_math.c"
#include "base_strings.c"
#include "base_sync.c"
#include "base_threads.c"
#include "base_thread_context.c"
#include "base_command_line.c"
#include "base_markup.c"
+1 -1
View File
@@ -14,7 +14,7 @@
#include "base_arena.h"
#include "base_math.h"
#include "base_strings.h"
#include "base_sync.h"
#include "base_threads.h"
#include "base_thread_context.h"
#include "base_command_line.h"
#include "base_markup.h"
@@ -1,6 +1,29 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Thread Functions
internal Thread
thread_launch(ThreadEntryPointFunctionType *f, void *p)
{
Thread thread = os_thread_launch(f, p);
return thread;
}
internal B32
thread_join(Thread thread, U64 endt_us)
{
B32 result = os_thread_join(thread, endt_us);
return result;
}
internal void
thread_detach(Thread thread)
{
os_thread_detach(thread);
}
////////////////////////////////
//~ rjf: Synchronization Primitive Functions
@@ -15,20 +38,17 @@ internal void mutex_drop(Mutex mutex) {os_mutex_drop(mutex);}
internal RWMutex rw_mutex_alloc(void) {return os_rw_mutex_alloc();}
internal void rw_mutex_release(RWMutex mutex) {os_rw_mutex_release(mutex);}
internal void rw_mutex_take_r(RWMutex mutex) {os_rw_mutex_take_r(mutex);}
internal void rw_mutex_drop_r(RWMutex mutex) {os_rw_mutex_drop_r(mutex);}
internal void rw_mutex_take_w(RWMutex mutex) {os_rw_mutex_take_w(mutex);}
internal void rw_mutex_drop_w(RWMutex mutex) {os_rw_mutex_drop_w(mutex);}
internal void rw_mutex_take(RWMutex mutex, B32 write_mode) {os_rw_mutex_take(mutex, write_mode);}
internal void rw_mutex_drop(RWMutex mutex, B32 write_mode) {os_rw_mutex_drop(mutex, write_mode);}
//- rjf: condition variables
internal CondVar cond_var_alloc(void) {return os_cond_var_alloc();}
internal void cond_var_release(CondVar cv) {os_cond_var_release(cv);}
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us) {return os_cond_var_wait(cv, mutex, endt_us);}
internal B32 cond_var_wait_rw_r(CondVar cv, RWMutex mutex_rw, U64 endt_us) {return os_cond_var_wait_rw_r(cv, mutex_rw, endt_us);}
internal B32 cond_var_wait_rw_w(CondVar cv, RWMutex mutex_rw, U64 endt_us) {return os_cond_var_wait_rw_w(cv, mutex_rw, endt_us);}
internal void cond_var_signal(CondVar cv) {os_cond_var_signal(cv);}
internal void cond_var_broadcast(CondVar cv) {os_cond_var_broadcast(cv);}
internal CondVar cond_var_alloc(void) {return os_cond_var_alloc();}
internal void cond_var_release(CondVar cv) {os_cond_var_release(cv);}
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us) {return os_cond_var_wait(cv, mutex, endt_us);}
internal B32 cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us) {return os_cond_var_wait_rw(cv, mutex_rw, write_mode, endt_us);}
internal void cond_var_signal(CondVar cv) {os_cond_var_signal(cv);}
internal void cond_var_broadcast(CondVar cv) {os_cond_var_broadcast(cv);}
//- rjf: cross-process semaphores
@@ -1,8 +1,18 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_SYNC_H
#define BASE_SYNC_H
#ifndef BASE_THREADS_H
#define BASE_THREADS_H
////////////////////////////////
//~ rjf: Thread Types
typedef struct Thread Thread;
struct Thread
{
U64 u64[1];
};
typedef void ThreadEntryPointFunctionType(void *p);
////////////////////////////////
//~ rjf: Synchronization Primitive Types
@@ -37,6 +47,13 @@ struct Barrier
U64 u64[1];
};
////////////////////////////////
//~ rjf: Thread Functions
internal Thread thread_launch(ThreadEntryPointFunctionType *f, void *p);
internal B32 thread_join(Thread thread, U64 endt_us);
internal void thread_detach(Thread thread);
////////////////////////////////
//~ rjf: Synchronization Primitive Functions
@@ -49,18 +66,21 @@ internal void mutex_drop(Mutex mutex);
//- rjf: reader/writer mutexes
internal RWMutex rw_mutex_alloc(void);
internal void rw_mutex_release(RWMutex mutex);
internal void rw_mutex_take_r(RWMutex mutex);
internal void rw_mutex_drop_r(RWMutex mutex);
internal void rw_mutex_take_w(RWMutex mutex);
internal void rw_mutex_drop_w(RWMutex mutex);
internal void rw_mutex_take(RWMutex mutex, B32 write_mode);
internal void rw_mutex_drop(RWMutex mutex, B32 write_mode);
#define rw_mutex_take_r(m) rw_mutex_take((m), (0))
#define rw_mutex_take_w(m) rw_mutex_take((m), (1))
#define rw_mutex_drop_r(m) rw_mutex_drop((m), (0))
#define rw_mutex_drop_w(m) rw_mutex_drop((m), (1))
//- rjf: condition variables
internal CondVar cond_var_alloc(void);
internal void cond_var_release(CondVar cv);
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us);
internal B32 cond_var_wait_rw_r(CondVar cv, RWMutex mutex_rw, U64 endt_us);
internal B32 cond_var_wait_rw_w(CondVar cv, RWMutex mutex_rw, U64 endt_us);
internal B32 cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us);
#define cond_var_wait_rw_r(cv, m, endt) cond_var_wait_rw((cv), (m), (0), (endt))
#define cond_var_wait_rw_w(cv, m, endt) cond_var_wait_rw((cv), (m), (1), (endt))
internal void cond_var_signal(CondVar cv);
internal void cond_var_broadcast(CondVar cv);
@@ -79,8 +99,9 @@ internal void barrier_wait(Barrier barrier);
//- rjf: scope macros
#define MutexScope(mutex) DeferLoop(mutex_take(mutex), mutex_drop(mutex))
#define RWMutexScope(mutex, write_mode) DeferLoop(rw_mutex_take((mutex), (write_mode)), rw_mutex_drop((mutex), (write_mode)))
#define MutexScopeR(mutex) DeferLoop(rw_mutex_take_r(mutex), rw_mutex_drop_r(mutex))
#define MutexScopeW(mutex) DeferLoop(rw_mutex_take_w(mutex), rw_mutex_drop_w(mutex))
#define MutexScopeRWPromote(mutex) DeferLoop((rw_mutex_drop_r(mutex), rw_mutex_take_w(mutex)), (rw_mutex_drop_w(mutex), rw_mutex_take_r(mutex)))
#endif // BASE_SYNC_H
#endif // BASE_THREADS_H