slow & highly contentious implementation of barriers using more basic sync primitives; fall back if sync barriers not present in win32 version

This commit is contained in:
Ryan Fleury
2026-05-12 13:48:57 -07:00
parent 641a7c8b30
commit d4623a7ddf
8 changed files with 230 additions and 25 deletions
+2 -2
View File
@@ -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, },
+2 -2
View File
@@ -253,13 +253,13 @@
# include <intrin.h>
# 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))
+134
View File
@@ -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();
}
+11 -3
View File
@@ -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
+22
View File
@@ -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
//
+1 -1
View File
@@ -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
+47 -16
View File
@@ -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);
}
+11 -1
View File
@@ -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;
};
};