CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
rwlock.h
Go to the documentation of this file.
1
47
48#pragma once
49
50#include <cx/cx.h>
51#include <cx/meta/block.h>
52#include <cx/platform/base.h>
53#include "aspin.h"
54#include "futex.h"
55
56#ifdef CX_LOCK_DEBUG
57#include <cx/log/log.h>
58#endif
59
60CX_C_BEGIN
61
66
68#define RWLOCK_READER_MAX 4095
70#define RWLOCK_READWAIT_MAX 2047
72#define RWLOCK_WRITER_MAX 511
73
74#define RWLOCK_READER_MASK (0x00000fff)
75#define RWLOCK_READWAIT_MASK (0x007ff000)
76#define RWLOCK_WRITER_MASK (0xff800000)
77#define RWLOCK_READERS(state) ((state) & RWLOCK_READER_MASK)
78#define RWLOCK_READWAIT(state) (((state) & RWLOCK_READWAIT_MASK) >> 12)
79#define RWLOCK_WRITERS(state) (((state) & RWLOCK_WRITER_MASK) >> 23)
80#define RWLOCK_READ_ADD 0x00000001
81#define RWLOCK_READWAIT_ADD 0x00001000
82#define RWLOCK_WRITE_ADD 0x00800000
83
89typedef struct RWLock {
90 atomic(uint32) state;
91 Futex rftx;
92 Futex wftx;
93 AdaptiveSpin aspin;
95
96void _rwlockInit(_Out_ RWLock* l, uint32 flags);
97
105#define rwlockInit(l, ...) _rwlockInit(l, opt_flags(__VA_ARGS__))
106
115_When_(return == true, _Acquires_shared_lock_(*l))
116 _When_(timeout == timeForever, _Acquires_shared_lock_(*l)) _When_(
117 timeout != timeForever,
118 _Must_inspect_result_) bool rwlockTryAcquireReadTimeout(_Inout_ RWLock* l, int64 timeout);
119
127_When_(return == true, _Acquires_exclusive_lock_(*l))
128 _When_(timeout == timeForever, _Acquires_exclusive_lock_(*l)) _When_(
129 timeout != timeForever,
130 _Must_inspect_result_) bool rwlockTryAcquireWriteTimeout(_Inout_ RWLock* l, int64 timeout);
131
139_When_(
140 return == true,
141 _Acquires_shared_lock_(
142 *l)) _Must_inspect_result_ _meta_inline bool rwlockTryAcquireRead(_Inout_ RWLock* l)
143{
144 uint32 state = atomicLoad(uint32, &l->state, Relaxed);
145 // only valid when no writer locks are held or pending
146 if (RWLOCK_WRITERS(state) == 0) {
147 // cannot acquire if we are at the max
148 if (RWLOCK_READERS(state) == RWLOCK_READER_MAX)
149 return false;
150 if (atomicCompareExchange(uint32,
151 strong,
152 &l->state,
153 &state,
154 state + RWLOCK_READ_ADD,
155 Acquire,
156 Relaxed)) {
157 aspinRecordUncontended(&l->aspin);
158 return true; // got the lock
159 }
160 }
161
162 // no longer have valid conditions in which this lock can be acquired
163 return false;
164}
165
172_When_(
173 return == true,
174 _Acquires_exclusive_lock_(
175 *l)) _Must_inspect_result_ _meta_inline bool rwlockTryAcquireWrite(_Inout_ RWLock* l)
176{
177 uint32 state = atomicLoad(uint32, &l->state, Relaxed);
178 // only valid when no other writer locks are held, and there are no (active) readers
179 if (RWLOCK_WRITERS(state) == 0 && RWLOCK_READERS(state) == 0) {
180 // make sure we didn't hit the limit
181 if (RWLOCK_WRITERS(state) == RWLOCK_WRITER_MAX)
182 return false;
183 if (atomicCompareExchange(uint32,
184 strong,
185 &l->state,
186 &state,
187 state + RWLOCK_WRITE_ADD,
188 Acquire,
189 Relaxed)) {
190 aspinRecordUncontended(&l->aspin);
191 return true; // got the lock
192 }
193 }
194
195 // no longer have valid conditions in which this lock can be acquired
196 return false;
197}
198
204_Acquires_shared_lock_(*l) _meta_inline void rwlockAcquireRead(_Inout_ RWLock* l)
205{
207 relFatalError("Failed to acquire read lock (too many waiting readers?)");
208}
209
215#define withReadLock(l) blkWrap (rwlockAcquireRead(l), rwlockReleaseRead(l))
216
222#define withWriteLock(l) blkWrap (rwlockAcquireWrite(l), rwlockReleaseWrite(l))
223
224#ifdef CX_LOCK_DEBUG
225#define _logFmtRwlockArgComp2(level, fmt, nargs, args) \
226 _logFmt_##level(LOG_##level, LogDefault, fmt, nargs, args)
227#define _logFmtRwlockArgComp(level, fmt, ...) \
228 _logFmtRwlockArgComp2(level, fmt, count_macro_args(__VA_ARGS__), (stvar[]) { __VA_ARGS__ })
229_Acquires_shared_lock_(*l)
230 _meta_inline bool rwlockLogAndAcquireRead(_Inout_ RWLock* l, const char* name,
231 const char* filename, int line)
232{
233 _logFmtRwlockArgComp(CX_LOCK_DEBUG,
234 _S"Locking rwlock ${string} for READ at ${string}:${int}",
235 stvar(string, (string)name),
236 stvar(string, (string)filename),
237 stvar(int32, line));
238 return rwlockAcquireRead(l);
239}
240
241#define rwlockAcquireRead(l) rwlockLogAndAcquireRead(l, #l, __FILE__, __LINE__)
242#endif
243
249_Acquires_exclusive_lock_(*l) _meta_inline void rwlockAcquireWrite(_Inout_ RWLock* l)
250{
252 relFatalError("Failed to acquire write lock (too many waiting writers?)");
253}
254
255#ifdef CX_LOCK_DEBUG
256_Acquires_exclusive_lock_(*l)
257 _meta_inline bool rwlockLogAndAcquireWrite(_Inout_ RWLock* l, const char* name,
258 const char* filename, int line)
259{
260 _logFmtRwlockArgComp(CX_LOCK_DEBUG,
261 _S"Locking rwlock ${string} for WRITE at ${string}:${int}",
262 stvar(string, (string)name),
263 stvar(string, (string)filename),
264 stvar(int32, line));
265 return rwlockAcquireWrite(l);
266}
267
268#define rwlockAcquireWrite(l) rwlockLogAndAcquireWrite(l, #l, __FILE__, __LINE__)
269#endif
270
277_Releases_shared_lock_(*l) _meta_inline bool rwlockReleaseRead(_Inout_ RWLock* l)
278{
279 devAssert(RWLOCK_READERS(atomicLoad(uint32, &l->state, Relaxed)) > 0);
280 uint32 oldstate = atomicFetchSub(uint32, &l->state, RWLOCK_READ_ADD, Release);
281
282 // If we were the last reader and any writers are waiting, unblock one
283 if (RWLOCK_READERS(oldstate) == 1 && RWLOCK_WRITERS(oldstate) > 0) {
284 devVerify(atomicFetchAdd(int32, &l->wftx.val, 1, Relaxed) == 0);
285 futexWake(&l->wftx);
286 }
287
288 return true;
289}
290
291#ifdef CX_LOCK_DEBUG
292_Releases_shared_lock_(*l)
293 _meta_inline bool rwlockLogAndReleaseRead(_Inout_ RWLock* l, const char* name,
294 const char* filename, int line)
295{
296 _logFmtRwlockArgComp(CX_LOCK_DEBUG,
297 _S"Releasing rwlock ${string} for READ at ${string}:${int}",
298 stvar(string, (string)name),
299 stvar(string, (string)filename),
300 stvar(int32, line));
301 return rwlockReleaseRead(l);
302}
303
304#define rwlockReleaseRead(l) rwlockLogAndReleaseRead(l, #l, __FILE__, __LINE__)
305#endif
306
312_Releases_exclusive_lock_(*l) bool rwlockReleaseWrite(_Inout_ RWLock* l);
313
314#ifdef CX_LOCK_DEBUG
315_Releases_exclusive_lock_(*l)
316 _meta_inline bool rwlockLogAndReleaseWrite(_Inout_ RWLock* l, const char* name,
317 const char* filename, int line)
318{
319 _logFmtRwlockArgComp(CX_LOCK_DEBUG,
320 _S"Releasing rwlock ${string} for WRITE at ${string}:${int}",
321 stvar(string, (string)name),
322 stvar(string, (string)filename),
323 stvar(int32, line));
324 return rwlockReleaseWrite(l);
325}
326
327#define rwlockReleaseWrite(l) rwlockLogAndReleaseWrite(l, #l, __FILE__, __LINE__)
328#endif
329
337_Releases_exclusive_lock_(*l)
338 _Acquires_shared_lock_(*l) bool rwlockDowngradeWrite(_Inout_ RWLock* l);
339
345void rwlockDestroy(_Pre_valid_ _Post_invalid_ RWLock* l);
346
347CX_C_END
348
350// end of thread_rwlock group
Compiler and platform detection macros.
Block wrapping macros for automatic resource management.
#define relFatalError(msg)
Definition assert.h:195
#define devVerify(expr)
Definition assert.h:154
#define devAssert(expr)
Definition assert.h:138
#define _S
Creates a static ASCII string from a string literal.
Definition strbase.h:392
#define stvar(typen, val)
Definition stvar.h:153
bool rwlockReleaseWrite(RWLock *l)
void rwlockAcquireWrite(RWLock *l)
Definition rwlock.h:249
bool rwlockTryAcquireReadTimeout(RWLock *l, int64 timeout)
bool rwlockTryAcquireWriteTimeout(RWLock *l, int64 timeout)
bool rwlockTryAcquireWrite(RWLock *l)
Definition rwlock.h:175
bool rwlockReleaseRead(RWLock *l)
Definition rwlock.h:277
void rwlockDestroy(RWLock *l)
bool rwlockDowngradeWrite(RWLock *l)
bool rwlockTryAcquireRead(RWLock *l)
Definition rwlock.h:142
RWLOCK_Flags
Reader-writer lock initialization flags.
Definition rwlock.h:63
#define RWLOCK_WRITER_MAX
Maximum number of writers (active + pending)
Definition rwlock.h:72
void rwlockAcquireRead(RWLock *l)
Definition rwlock.h:204
#define RWLOCK_READER_MAX
Maximum number of concurrent readers.
Definition rwlock.h:68
@ RWLOCK_NoSpin
Disable adaptive spinning, use kernel futex immediately.
Definition rwlock.h:64
#define timeForever
Maximum representable time value (approximately year 294,276 CE)
Definition time.h:13
Core logging system API.
atomic(uint32) state
Packed lock state (readers, waiting readers, writers)
Futex rftx
Futex for reader wait queue.
Definition rwlock.h:91
AdaptiveSpin aspin
Adaptive spin state.
Definition rwlock.h:93
Futex wftx
Futex for writer wait queue.
Definition rwlock.h:92