ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
common/sync.h
Go to the documentation of this file.
1#pragma once
2
16
17#include <libplatform/compiler.h>
18#include <libplatform/system.h>
19#include <libplatform/mutex.h>
20#include <libplatform/rwlock.h>
21#include <libplatform/cond.h>
22#include "../debug/named.h"
23
24// ============================================================================
25// Static Initialization Helpers
26// ============================================================================
27
28// Since Windows doesn't support static initialization of synchronization
29// primitives the same way POSIX does, we need a different approach.
30// We'll use a combination of static initialization and lazy initialization.
31
45typedef struct {
47 mutex_t mutex;
48#if PLATFORM_WINDOWS
50 volatile LONG initialized;
51#else
53 volatile int initialized;
54#endif
56
72typedef struct {
75#if PLATFORM_WINDOWS
77 volatile LONG initialized;
78#else
80 volatile int initialized;
81#endif
83
96typedef struct {
98 cond_t cond;
99#if PLATFORM_WINDOWS
101 volatile LONG initialized;
102#else
104 volatile int initialized;
105#endif
107
108// Initialization macros using designated initializers to avoid warnings
109// clang-format off
110#if PLATFORM_WINDOWS
111#define STATIC_MUTEX_INIT {.mutex = {0}, .initialized = 0}
112#define STATIC_RWLOCK_INIT {.lock = {0}, .initialized = 0}
113#define STATIC_COND_INIT {.cond = {0}, .initialized = 0}
114#else
115#define STATIC_MUTEX_INIT {.mutex.impl = PTHREAD_MUTEX_INITIALIZER, .mutex.name = NULL, .initialized = 1}
116#define STATIC_RWLOCK_INIT {.lock.impl = PTHREAD_RWLOCK_INITIALIZER, .lock.name = NULL, .initialized = 1}
117#define STATIC_COND_INIT {.cond.impl = PTHREAD_COND_INITIALIZER, .cond.name = NULL, .initialized = 1}
118#endif
119// clang-format on
120
121// Lazy initialization functions
122static inline void static_mutex_lock_named(static_mutex_t *m, const char *name) {
123#if PLATFORM_WINDOWS
124 // Use InterlockedCompareExchange for thread-safe lazy init with proper synchronization
125 // Pattern: First thread sets flag to 1, initializes, then sets to 2
126 // Other threads spin until flag is 2 (fully initialized)
127 LONG prev = InterlockedCompareExchange(&m->initialized, 1, 0);
128 if (prev == 0) {
129 // We won the race - initialize the mutex
130 mutex_init(&m->mutex, name);
131 // Memory barrier to ensure init is visible before flag update
132 MemoryBarrier();
133 InterlockedExchange(&m->initialized, 2);
134 } else if (prev == 1) {
135 // Another thread is initializing - spin until done
136 while (InterlockedCompareExchange(&m->initialized, 2, 2) != 2) {
137 YieldProcessor();
138 }
139 }
140 // prev == 2 means already initialized, proceed directly
141#else
142 // Register a statically initialized native mutex once, without reinitializing it.
143 int state = __atomic_load_n(&m->initialized, __ATOMIC_ACQUIRE);
144 if (state != 2) {
145 int expected = state;
146 if (state >= 0 &&
147 __atomic_compare_exchange_n(&m->initialized, &expected, -1, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
148 if (state == 0)
149 mutex_init(&m->mutex, name);
150 else
151 m->mutex.name = NAMED_REGISTER_MUTEX(&m->mutex, name, NULL);
152 __atomic_store_n(&m->initialized, 2, __ATOMIC_RELEASE);
153 } else {
154 while (__atomic_load_n(&m->initialized, __ATOMIC_ACQUIRE) != 2) {
155 }
156 }
157 }
158#endif
159 mutex_lock(&m->mutex);
160}
161
162static inline void static_mutex_lock(static_mutex_t *m) {
163 static_mutex_lock_named(m, "static_mutex");
164}
165
166static inline void static_mutex_unlock(static_mutex_t *m) {
167 mutex_unlock(&m->mutex);
168}
169
170static inline void static_rwlock_rdlock(static_rwlock_t *l) {
171#if PLATFORM_WINDOWS
172 LONG prev = InterlockedCompareExchange(&l->initialized, 1, 0);
173 if (prev == 0) {
174 rwlock_init(&l->lock, "static_rwlock");
175 MemoryBarrier();
176 InterlockedExchange(&l->initialized, 2);
177 } else if (prev == 1) {
178 while (InterlockedCompareExchange(&l->initialized, 2, 2) != 2) {
179 YieldProcessor();
180 }
181 }
182#else
183 if (__atomic_load_n(&l->initialized, __ATOMIC_ACQUIRE) == 0) {
184 rwlock_init(&l->lock, "static_rwlock");
185 __atomic_store_n(&l->initialized, 1, __ATOMIC_RELEASE);
186 }
187#endif
188 rwlock_rdlock(&l->lock);
189}
190
191static inline void static_rwlock_wrlock(static_rwlock_t *l) {
192#if PLATFORM_WINDOWS
193 LONG prev = InterlockedCompareExchange(&l->initialized, 1, 0);
194 if (prev == 0) {
195 rwlock_init(&l->lock, "static_rwlock");
196 MemoryBarrier();
197 InterlockedExchange(&l->initialized, 2);
198 } else if (prev == 1) {
199 while (InterlockedCompareExchange(&l->initialized, 2, 2) != 2) {
200 YieldProcessor();
201 }
202 }
203#else
204 if (__atomic_load_n(&l->initialized, __ATOMIC_ACQUIRE) == 0) {
205 rwlock_init(&l->lock, "static_rwlock");
206 __atomic_store_n(&l->initialized, 1, __ATOMIC_RELEASE);
207 }
208#endif
209 rwlock_wrlock(&l->lock);
210}
211
212static inline void static_cond_wait(static_cond_t *c, static_mutex_t *m) {
213#if PLATFORM_WINDOWS
214 LONG prev = InterlockedCompareExchange(&c->initialized, 1, 0);
215 if (prev == 0) {
216 cond_init(&c->cond, "static_cond");
217 MemoryBarrier();
218 InterlockedExchange(&c->initialized, 2);
219 } else if (prev == 1) {
220 while (InterlockedCompareExchange(&c->initialized, 2, 2) != 2) {
221 YieldProcessor();
222 }
223 }
224 if (InterlockedCompareExchange(&m->initialized, 1, 0) == 0) {
225 mutex_init(&m->mutex, "static_mutex");
226 }
227#else
228 if (__atomic_load_n(&c->initialized, __ATOMIC_ACQUIRE) == 0) {
229 cond_init(&c->cond, "static_cond");
230 __atomic_store_n(&c->initialized, 1, __ATOMIC_RELEASE);
231 }
232 if (__atomic_load_n(&m->initialized, __ATOMIC_ACQUIRE) == 0) {
233 mutex_init(&m->mutex, "static_mutex");
234 __atomic_store_n(&m->initialized, 1, __ATOMIC_RELEASE);
235 }
236#endif
237 cond_wait(&c->cond, &m->mutex);
238}
239
240static inline void static_cond_timedwait(static_cond_t *c, static_mutex_t *m, int timeout_ms) {
241#if PLATFORM_WINDOWS
242 LONG prev = InterlockedCompareExchange(&c->initialized, 1, 0);
243 if (prev == 0) {
244 cond_init(&c->cond, "static_cond");
245 MemoryBarrier();
246 InterlockedExchange(&c->initialized, 2);
247 } else if (prev == 1) {
248 while (InterlockedCompareExchange(&c->initialized, 2, 2) != 2) {
249 YieldProcessor();
250 }
251 }
252 if (InterlockedCompareExchange(&m->initialized, 1, 0) == 0) {
253 mutex_init(&m->mutex, "static_mutex");
254 }
255#else
256 if (__atomic_load_n(&c->initialized, __ATOMIC_ACQUIRE) == 0) {
257 cond_init(&c->cond, "static_cond");
258 __atomic_store_n(&c->initialized, 1, __ATOMIC_RELEASE);
259 }
260 if (__atomic_load_n(&m->initialized, __ATOMIC_ACQUIRE) == 0) {
261 mutex_init(&m->mutex, "static_mutex");
262 __atomic_store_n(&m->initialized, 1, __ATOMIC_RELEASE);
263 }
264#endif
265 cond_timedwait(&c->cond, &m->mutex, timeout_ms);
266}
267
268static inline void static_cond_signal(static_cond_t *c) {
269#if PLATFORM_WINDOWS
270 LONG prev = InterlockedCompareExchange(&c->initialized, 1, 0);
271 if (prev == 0) {
272 cond_init(&c->cond, "static_cond");
273 MemoryBarrier();
274 InterlockedExchange(&c->initialized, 2);
275 } else if (prev == 1) {
276 while (InterlockedCompareExchange(&c->initialized, 2, 2) != 2) {
277 YieldProcessor();
278 }
279 }
280#else
281 if (__atomic_load_n(&c->initialized, __ATOMIC_ACQUIRE) == 0) {
282 cond_init(&c->cond, "static_cond");
283 __atomic_store_n(&c->initialized, 1, __ATOMIC_RELEASE);
284 }
285#endif
286 cond_signal(&c->cond);
287}
288
289static inline void static_cond_broadcast(static_cond_t *c) {
290#if PLATFORM_WINDOWS
291 LONG prev = InterlockedCompareExchange(&c->initialized, 1, 0);
292 if (prev == 0) {
293 cond_init(&c->cond, "static_cond");
294 MemoryBarrier();
295 InterlockedExchange(&c->initialized, 2);
296 } else if (prev == 1) {
297 while (InterlockedCompareExchange(&c->initialized, 2, 2) != 2) {
298 YieldProcessor();
299 }
300 }
301#else
302 if (__atomic_load_n(&c->initialized, __ATOMIC_ACQUIRE) == 0) {
303 cond_init(&c->cond, "static_cond");
304 __atomic_store_n(&c->initialized, 1, __ATOMIC_RELEASE);
305 }
306#endif
307 cond_broadcast(&c->cond);
308}
309
310// ============================================================================
311// Platform Initialization
312// ============================================================================
313
Named object registry for debugging — log identifiable resource names.
#define NAMED_REGISTER_MUTEX(mutex, name, parent_ptr)
Register a mutex with automatic format specifier.
int rwlock_init(rwlock_t *lock, const char *name)
Initialize a read-write lock with a name.
#define rwlock_rdlock(lock)
Acquire a read lock (with debug tracking in debug builds).
Definition rwlock.h:299
#define rwlock_wrlock(lock)
Acquire a write lock (with debug tracking in debug builds).
Definition rwlock.h:319
Read-write lock type (POSIX: pthread_rwlock_t with debug tracking).
Definition rwlock.h:63
Static condition variable structure for global condition variables requiring static initialization.
Definition common/sync.h:96
volatile int initialized
Thread-safe initialization flag (POSIX: int for atomic operations).
cond_t cond
The actual condition variable.
Definition common/sync.h:98
Static mutex structure for global mutexes requiring static initialization.
Definition common/sync.h:45
volatile int initialized
Thread-safe initialization flag (POSIX: int for atomic operations).
Definition common/sync.h:53
mutex_t mutex
The actual mutex.
Definition common/sync.h:47
Statically initialized mutex wrapper for globals touched during startup. The embedded mutex carries t...
Definition common/sync.h:72
rwlock_t lock
The actual reader-writer lock.
Definition common/sync.h:74
volatile int initialized
Thread-safe initialization flag (POSIX: int for atomic operations).
Definition common/sync.h:80