From d4623a7ddf6bdb1bfe4285eebec7cfe69c930896 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Tue, 12 May 2026 13:48:57 -0700 Subject: [PATCH] slow & highly contentious implementation of barriers using more basic sync primitives; fall back if sync barriers not present in win32 version --- project.4coder | 4 +- src/base/base_core.h | 4 +- src/base/base_threads.c | 134 ++++++++++++++++++++++++++++++++++++ src/base/base_threads.h | 14 +++- src/radbin/radbin.c | 22 ++++++ src/raddbg/raddbg_main.c | 2 +- src/win32/base/win32_base.c | 63 ++++++++++++----- src/win32/base/win32_base.h | 12 +++- 8 files changed, 230 insertions(+), 25 deletions(-) diff --git a/project.4coder b/project.4coder index 1ff38098..0b48eaac 100644 --- a/project.4coder +++ b/project.4coder @@ -49,13 +49,13 @@ 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 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 raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + .f1 = { .win = "build radbin debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [raddbg wsl] // .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [scratch] - .f2 = { .win = "wsl ./build.sh radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + .f2 = { .win = "pushd build && for /l %i in (1, 1, 3) do (radbin --capture --rdi mule_main.pdb --async_thread_count=1 --thread_count=8)", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [textperf] // .f1 = { .win = "raddbg_stable --ipc kill_all && build no_meta telemetry textperf && raddbg_stable --ipc bring_to_front && raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, diff --git a/src/base/base_core.h b/src/base/base_core.h index 2536e38f..493519ab 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -253,13 +253,13 @@ # include # if ARCH_X64 # define ins_atomic_u128_eval_cond_assign(x,k,c) (B32)InterlockedCompareExchange128((__int64 *)(x), ((__int64 *)&(k))[1], ((__int64 *)&(k))[0], (__int64 *)c) -# define ins_atomic_u64_eval(x) *((volatile U64 *)(x)) +# define ins_atomic_u64_eval(x) InterlockedAdd64((__int64 *)(x), 0) # define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((__int64 *)(x)) # define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((__int64 *)(x)) # define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((__int64 *)(x),(c)) # define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((__int64 *)(x), c) # define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((__int64 *)(x),(k),(c)) -# define ins_atomic_u32_eval(x) *((volatile U32 *)(x)) +# define ins_atomic_u32_eval(x) InterlockedAdd((LONG *)(x), 0) # define ins_atomic_u32_inc_eval(x) InterlockedIncrement((LONG *)(x)) # define ins_atomic_u32_dec_eval(x) InterlockedDecrement((LONG *)(x)) # define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((LONG *)(x),(c)) diff --git a/src/base/base_threads.c b/src/base/base_threads.c index c8b4c1eb..a8c72585 100644 --- a/src/base/base_threads.c +++ b/src/base/base_threads.c @@ -58,3 +58,137 @@ set_thread_namef(char *fmt, ...) va_end(args); scratch_end(scratch); } + +//////////////////////////////// +//~ rjf: Platform-Abstracted Synchronization Primitive Functions + +//- rjf: slow barriers + +typedef struct BarrierNode BarrierNode; +struct BarrierNode +{ + BarrierNode *next; + U64 count; + U64 threads_left_to_enter; + U64 threads_left_to_leave; + RWMutex rw_mutex; + CondVar cv; +}; + +typedef struct BarrierTCTX BarrierTCTX; +struct BarrierTCTX +{ + Arena *arena; + BarrierNode *free_barrier_node; +}; + +thread_static BarrierTCTX *barrier_tctx = 0; + +internal Barrier +slow_barrier_alloc(U64 count) +{ + if(barrier_tctx == 0) + { + Arena *arena = arena_alloc(); + barrier_tctx = push_array(arena, BarrierTCTX, 1); + barrier_tctx->arena = arena; + } + BarrierNode *n = barrier_tctx->free_barrier_node; + if(n != 0) + { + SLLStackPop(barrier_tctx->free_barrier_node); + } + else + { + n = push_array_no_zero(barrier_tctx->arena, BarrierNode, 1); + } + MemoryZeroStruct(n); + n->count = count; + n->threads_left_to_enter = count; + n->rw_mutex = rw_mutex_alloc(); + n->cv = cond_var_alloc(); + Barrier result = {(U64)n}; + return result; +} + +internal void +slow_barrier_release(Barrier barrier) +{ + if(barrier_tctx == 0) + { + Arena *arena = arena_alloc(); + barrier_tctx = push_array(arena, BarrierTCTX, 1); + barrier_tctx->arena = arena; + } + BarrierNode *n = (BarrierNode *)barrier.u64[0]; + rw_mutex_release(n->rw_mutex); + cond_var_release(n->cv); + SLLStackPush(barrier_tctx->free_barrier_node, n); +} + +internal void +slow_barrier_wait(Barrier barrier) +{ + ProfBeginFunction(); + BarrierNode *n = (BarrierNode *)barrier.u64[0]; + U64 threads_left_to_enter = ins_atomic_u64_dec_eval(&n->threads_left_to_enter); + + //- rjf: threads left to enter > 0 => wait + if(threads_left_to_enter > 0) + { + // rjf: first try a spin loop + B32 done_waiting = 0; + ProfScope("spin loop wait") for(U64 spin_count = 0; spin_count < 10000; spin_count += 1) + { + if(ins_atomic_u64_eval(&n->threads_left_to_leave) != 0) + { + done_waiting = 1; + break; + } + } + + // rjf: not done waiting -> need to do slow wait on condition variable + if(!done_waiting) ProfScope("slow wait") + { + RWMutexScope(n->rw_mutex, 0) for(;;) + { + if(ins_atomic_u64_eval(&n->threads_left_to_leave) != 0) + { + break; + } + cond_var_wait_rw(n->cv, n->rw_mutex, 0, max_U64); + } + } + + // rjf: decrement leave counter + if(ins_atomic_u64_dec_eval(&n->threads_left_to_leave) > 0) + { + ProfScope("signal") cond_var_signal(n->cv); + } + } + + //- rjf: threads left to enter == 0 -> last thread, wakeup + else + { + ins_atomic_u64_eval_assign(&n->threads_left_to_enter, n->count); + ProfScope("wake up") RWMutexScope(n->rw_mutex, 1) + { + ins_atomic_u64_eval_assign(&n->threads_left_to_leave, n->count-1); + } + ProfScope("signal") cond_var_signal(n->cv); + } + + //- rjf: wait for threads left to leave == 0 + ProfScope("wait for threads to leave") + { + for(U64 spin_count = 0;; spin_count += 1) + { + if(ins_atomic_u64_eval(&n->threads_left_to_leave) == 0) + { + break; + } + } + } + + ProfEnd(); +} diff --git a/src/base/base_threads.h b/src/base/base_threads.h index f179a219..b34286cc 100644 --- a/src/base/base_threads.h +++ b/src/base/base_threads.h @@ -131,9 +131,9 @@ internal B32 semaphore_take(Semaphore semaphore, U64 endt_us); internal void semaphore_drop(Semaphore semaphore); //- rjf: barriers -internal Barrier barrier_alloc(U64 count); -internal void barrier_release(Barrier barrier); -internal void barrier_wait(Barrier barrier); +internal Barrier barrier_alloc(U64 count); +internal void barrier_release(Barrier barrier); +internal void barrier_wait(Barrier barrier); //- rjf: scope macros #define MutexScope(mutex) DeferLoop(mutex_take(mutex), mutex_drop(mutex)) @@ -142,6 +142,14 @@ internal void barrier_wait(Barrier barrier); #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))) +//////////////////////////////// +//~ rjf: Platform-Abstracted Synchronization Primitive Functions + +//- rjf: slow barriers +internal Barrier slow_barrier_alloc(U64 count); +internal void slow_barrier_release(Barrier barrier); +internal void slow_barrier_wait(Barrier barrier); + //////////////////////////////// //~ rjf: @per_os_impl Safe Calls diff --git a/src/radbin/radbin.c b/src/radbin/radbin.c index 734a6d85..b4a2c7f5 100644 --- a/src/radbin/radbin.c +++ b/src/radbin/radbin.c @@ -56,6 +56,28 @@ rb_thread_entry_point(void *p) log_select(log); log_scope_begin(); +#if 0 + ProfScope("work") + { + for(int i = 0; i < 5; i += 1) + { + int sum = 0; + ProfScope("do work") + { + for(int x = 0; x < 10000; x += 1) + { + for(int y = 0; y < 10000; y += 1) + { + sum += x*y + x-y; + } + } + } + lane_sync(); + } + } + abort_self(0); +#endif + ////////////////////////////// //- rjf: set up shared state // diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index 0823f23a..4c9d8c6e 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -10,11 +10,11 @@ // [ ] value coloring view in watch window, so you can quickly scroll & see values outside of a threshold // [x] step out of scopes / loops // -// [x] policy for closing debug info which is no longer relevant? // [ ] codebase-internal barrier impl (win7/linux support) // [ ] asan pass // [ ] external window focusing bugs // [ ] PDB -> RDI conversion memory usage +// [x] policy for closing debug info which is no longer relevant? // [x] killing/restarting thread performance (#780) // // [ ] project/user file improvements diff --git a/src/win32/base/win32_base.c b/src/win32/base/win32_base.c index e55504ad..5db1ef65 100644 --- a/src/win32/base/win32_base.c +++ b/src/win32/base/win32_base.c @@ -8,6 +8,12 @@ typedef HRESULT W32_SetThreadDescription_Type(HANDLE hThread, PCWSTR lpThreadDescription); global W32_SetThreadDescription_Type *w32_SetThreadDescription_func = 0; +typedef BOOL W32_InitializeSynchronizationBarrier_Type(W32_SYNCHRONIZATION_BARRIER *lpBarrier, LONG lTotalThreads, LONG lSpinCount); +global W32_InitializeSynchronizationBarrier_Type *w32_InitializeSynchronizationBarrier_func = 0; +typedef BOOL W32_DeleteSynchronizationBarrier_Type(W32_SYNCHRONIZATION_BARRIER *lpBarrier); +global W32_DeleteSynchronizationBarrier_Type *w32_DeleteSynchronizationBarrier_func = 0; +typedef BOOL W32_EnterSynchronizationBarrier_Type(W32_SYNCHRONIZATION_BARRIER *lpBarrier, DWORD dwFlags); +global W32_EnterSynchronizationBarrier_Type *w32_EnterSynchronizationBarrier_func = 0; global RIO_EXTENSION_FUNCTION_TABLE w32_rio_functions = {0}; //////////////////////////////// @@ -42,10 +48,10 @@ w32_date_time_from_system_time(DateTime *out, SYSTEMTIME *in) out->year = in->wYear; out->mon = in->wMonth - 1; out->wday = in->wDayOfWeek; - out->day = in->wDay; - out->hour = in->wHour; - out->min = in->wMinute; - out->sec = in->wSecond; + out->day = in->wDay; + out->hour = in->wHour; + out->min = in->wMinute; + out->sec = in->wSecond; out->msec = in->wMilliseconds; } @@ -681,34 +687,56 @@ semaphore_drop(Semaphore semaphore) internal Barrier barrier_alloc(U64 count) { - W32_Entity *entity = w32_entity_alloc(W32_EntityKind_Barrier); - if(entity != 0) + Barrier result = {0}; + if(w32_InitializeSynchronizationBarrier_func != 0) { - BOOL init_good = InitializeSynchronizationBarrier(&entity->sb, count, -1); - (void)init_good; + W32_Entity *entity = w32_entity_alloc(W32_EntityKind_Barrier); + if(entity != 0) + { + BOOL init_good = w32_InitializeSynchronizationBarrier_func(&entity->sb, count, -1); + (void)init_good; + } + result.u64[0] = IntFromPtr(entity); + } + else + { + result = slow_barrier_alloc(count); } - Barrier result = {IntFromPtr(entity)}; return result; } internal void barrier_release(Barrier barrier) { - W32_Entity *entity = (W32_Entity*)PtrFromInt(barrier.u64[0]); - if(entity != 0) + if(w32_InitializeSynchronizationBarrier_func != 0) { - DeleteSynchronizationBarrier(&entity->sb); - w32_entity_release(entity); + W32_Entity *entity = (W32_Entity*)PtrFromInt(barrier.u64[0]); + if(entity != 0) + { + w32_DeleteSynchronizationBarrier_func(&entity->sb); + w32_entity_release(entity); + } + } + else + { + slow_barrier_release(barrier); } } internal void barrier_wait(Barrier barrier) { - W32_Entity *entity = (W32_Entity*)PtrFromInt(barrier.u64[0]); - if(entity != 0) + if(w32_InitializeSynchronizationBarrier_func != 0) { - EnterSynchronizationBarrier(&entity->sb, 0); + W32_Entity *entity = (W32_Entity*)PtrFromInt(barrier.u64[0]); + if(entity != 0) + { + w32_EnterSynchronizationBarrier_func(&entity->sb, 0); + } + } + else + { + slow_barrier_wait(barrier); } } @@ -1741,6 +1769,9 @@ w32_entry_point_caller(int argc, WCHAR **wargv) { HMODULE module = LoadLibraryA("kernel32.dll"); w32_SetThreadDescription_func = (W32_SetThreadDescription_Type *)GetProcAddress(module, "SetThreadDescription"); + w32_InitializeSynchronizationBarrier_func = (W32_InitializeSynchronizationBarrier_Type *)GetProcAddress(module, "InitializeSynchronizationBarrier"); + w32_DeleteSynchronizationBarrier_func = (W32_DeleteSynchronizationBarrier_Type *)GetProcAddress(module, "DeleteSynchronizationBarrier"); + w32_EnterSynchronizationBarrier_func = (W32_EnterSynchronizationBarrier_Type *)GetProcAddress(module, "EnterSynchronizationBarrier"); FreeLibrary(module); } diff --git a/src/win32/base/win32_base.h b/src/win32/base/win32_base.h index 9940e51d..fe0e7957 100644 --- a/src/win32/base/win32_base.h +++ b/src/win32/base/win32_base.h @@ -53,6 +53,16 @@ typedef enum W32_EntityKind } W32_EntityKind; +typedef struct W32_SYNCHRONIZATION_BARRIER W32_SYNCHRONIZATION_BARRIER; +struct W32_SYNCHRONIZATION_BARRIER +{ + U32 reserved_0; + U32 reserved_1; + U64 reserved_2[2]; + U32 reserved_3; + U32 reserved_4; +}; + typedef struct W32_Entity W32_Entity; struct W32_Entity { @@ -70,7 +80,7 @@ struct W32_Entity CRITICAL_SECTION mutex; SRWLOCK rw_mutex; CONDITION_VARIABLE cv; - SYNCHRONIZATION_BARRIER sb; + W32_SYNCHRONIZATION_BARRIER sb; }; };