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)) _When_(timeout == timeForever, _Acquires_shared_lock_(*l)) _When_(timeout != timeForever, _Must_inspect_result_) bool rwlockTryAcquireReadTimeout(_Inout_ RWLock* l, int64 timeout);
116
124_When_(return == true, _Acquires_exclusive_lock_(*l)) _When_(timeout == timeForever, _Acquires_exclusive_lock_(*l)) _When_(timeout != timeForever, _Must_inspect_result_) bool rwlockTryAcquireWriteTimeout(_Inout_ RWLock* l, int64 timeout);
125
133_When_(return == true, _Acquires_shared_lock_(*l)) _Must_inspect_result_ _meta_inline bool rwlockTryAcquireRead(_Inout_ RWLock* l)
134{
135 uint32 state = atomicLoad(uint32, &l->state, Relaxed);
136 // only valid when no writer locks are held or pending
137 if (RWLOCK_WRITERS(state) == 0) {
138 // cannot acquire if we are at the max
139 if (RWLOCK_READERS(state) == RWLOCK_READER_MAX)
140 return false;
141 if (atomicCompareExchange(uint32,
142 strong,
143 &l->state,
144 &state,
145 state + RWLOCK_READ_ADD,
146 Acquire,
147 Relaxed)) {
148 aspinRecordUncontended(&l->aspin);
149 return true; // got the lock
150 }
151 }
152
153 // no longer have valid conditions in which this lock can be acquired
154 return false;
155}
156
163_When_(return == true, _Acquires_exclusive_lock_(*l)) _Must_inspect_result_ _meta_inline bool rwlockTryAcquireWrite(_Inout_ RWLock* l)
164{
165 uint32 state = atomicLoad(uint32, &l->state, Relaxed);
166 // only valid when no other writer locks are held, and there are no (active) readers
167 if (RWLOCK_WRITERS(state) == 0 && RWLOCK_READERS(state) == 0) {
168 // make sure we didn't hit the limit
169 if (RWLOCK_WRITERS(state) == RWLOCK_WRITER_MAX)
170 return false;
171 if (atomicCompareExchange(uint32,
172 strong,
173 &l->state,
174 &state,
175 state + RWLOCK_WRITE_ADD,
176 Acquire,
177 Relaxed)) {
178 aspinRecordUncontended(&l->aspin);
179 return true; // got the lock
180 }
181 }
182
183 // no longer have valid conditions in which this lock can be acquired
184 return false;
185}
186
192_Acquires_shared_lock_(*l) _meta_inline void rwlockAcquireRead(_Inout_ RWLock* l)
193{
195 relFatalError("Failed to acquire read lock (too many waiting readers?)");
196}
197
203#define withReadLock(l) blkWrap (rwlockAcquireRead(l), rwlockReleaseRead(l))
204
210#define withWriteLock(l) blkWrap (rwlockAcquireWrite(l), rwlockReleaseWrite(l))
211
212#ifdef CX_LOCK_DEBUG
213// see the note on _logFmtMutexArgComp in mutex.h
214#define _logFmtRwlockArgComp(level, fmt, ...) \
215 _logFmtArgs(level, LogDefault, LOG_SiteAlways, 0, fmt, __VA_ARGS__)
216_Acquires_shared_lock_(*l)
217 static bool rwlockLogAndAcquireRead(_Inout_ RWLock* l, const char* name,
218 const char* filename, int line)
219{
220 _logFmtRwlockArgComp(CX_LOCK_DEBUG,
221 _S"Locking rwlock ${string} for READ at ${string}:${int}",
222 stvar(string, (string)name),
223 stvar(string, (string)filename),
224 stvar(int32, line));
226 return true;
227}
228
229#define rwlockAcquireRead(l) rwlockLogAndAcquireRead(l, #l, __FILE__, __LINE__)
230#endif
231
237_Acquires_exclusive_lock_(*l) _meta_inline void rwlockAcquireWrite(_Inout_ RWLock* l)
238{
240 relFatalError("Failed to acquire write lock (too many waiting writers?)");
241}
242
243#ifdef CX_LOCK_DEBUG
244_Acquires_exclusive_lock_(*l)
245 static bool rwlockLogAndAcquireWrite(_Inout_ RWLock* l, const char* name,
246 const char* filename, int line)
247{
248 _logFmtRwlockArgComp(CX_LOCK_DEBUG,
249 _S"Locking rwlock ${string} for WRITE at ${string}:${int}",
250 stvar(string, (string)name),
251 stvar(string, (string)filename),
252 stvar(int32, line));
254 return true;
255}
256
257#define rwlockAcquireWrite(l) rwlockLogAndAcquireWrite(l, #l, __FILE__, __LINE__)
258#endif
259
266_Releases_shared_lock_(*l) _meta_inline bool rwlockReleaseRead(_Inout_ RWLock* l)
267{
268 devAssert(RWLOCK_READERS(atomicLoad(uint32, &l->state, Relaxed)) > 0);
269 uint32 oldstate = atomicFetchSub(uint32, &l->state, RWLOCK_READ_ADD, Release);
270
271 // If we were the last reader and any writers are waiting, unblock one
272 if (RWLOCK_READERS(oldstate) == 1 && RWLOCK_WRITERS(oldstate) > 0) {
273 devVerify(atomicFetchAdd(int32, &l->wftx.val, 1, Relaxed) == 0);
274 futexWake(&l->wftx);
275 }
276
277 return true;
278}
279
280#ifdef CX_LOCK_DEBUG
281_Releases_shared_lock_(*l)
282 static bool rwlockLogAndReleaseRead(_Inout_ RWLock* l, const char* name,
283 const char* filename, int line)
284{
285 _logFmtRwlockArgComp(CX_LOCK_DEBUG,
286 _S"Releasing rwlock ${string} for READ at ${string}:${int}",
287 stvar(string, (string)name),
288 stvar(string, (string)filename),
289 stvar(int32, line));
290 return rwlockReleaseRead(l);
291}
292
293#define rwlockReleaseRead(l) rwlockLogAndReleaseRead(l, #l, __FILE__, __LINE__)
294#endif
295
301_Releases_exclusive_lock_(*l) bool rwlockReleaseWrite(_Inout_ RWLock* l);
302
303#ifdef CX_LOCK_DEBUG
304_Releases_exclusive_lock_(*l)
305 static bool rwlockLogAndReleaseWrite(_Inout_ RWLock* l, const char* name,
306 const char* filename, int line)
307{
308 _logFmtRwlockArgComp(CX_LOCK_DEBUG,
309 _S"Releasing rwlock ${string} for WRITE at ${string}:${int}",
310 stvar(string, (string)name),
311 stvar(string, (string)filename),
312 stvar(int32, line));
313 return rwlockReleaseWrite(l);
314}
315
316#define rwlockReleaseWrite(l) rwlockLogAndReleaseWrite(l, #l, __FILE__, __LINE__)
317#endif
318
326_Releases_exclusive_lock_(*l)
327 _Acquires_shared_lock_(*l) bool rwlockDowngradeWrite(_Inout_ RWLock* l);
328
334void rwlockDestroy(_Pre_valid_ _Post_invalid_ RWLock* l);
335
336CX_C_END
337
339// end of thread_rwlock group
Compiler and platform detection macros.
Block wrapping macros for automatic resource management.
#define relFatalError(msg)
Definition assert.h:197
#define devVerify(expr)
Definition assert.h:156
#define devAssert(expr)
Definition assert.h:140
#define _S
Creates a static ASCII string literal (STR_LEN0, runtime strlen). Prefer _SL() on hot paths when targ...
Definition strliteral.h:91
#define stvar(typen, val)
Definition stvar.h:162
bool rwlockReleaseWrite(RWLock *l)
void rwlockAcquireWrite(RWLock *l)
Definition rwlock.h:237
bool rwlockTryAcquireReadTimeout(RWLock *l, int64 timeout)
bool rwlockTryAcquireWriteTimeout(RWLock *l, int64 timeout)
bool rwlockTryAcquireWrite(RWLock *l)
Definition rwlock.h:163
bool rwlockReleaseRead(RWLock *l)
Definition rwlock.h:266
void rwlockDestroy(RWLock *l)
bool rwlockDowngradeWrite(RWLock *l)
bool rwlockTryAcquireRead(RWLock *l)
Definition rwlock.h:133
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:192
#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:15
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