CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
clang_atomic.h
1
// GCC before 11.1 under-aligns _Atomic long long as a struct member on i386 (it aligns
2
// the standalone typedef correctly, but not the field inside cx_atomic_int64/uint64),
3
// which breaks both atomicity of 8-byte ops and the C/C++ ABI match below. There is no
4
// portable way to detect this from a static_assert, since the typedef alignment lies.
5
#if defined(__i386__) && defined(__GNUC__) && !defined(__clang__) && \
6
(__GNUC__ < 11 || (__GNUC__ == 11 && __GNUC_MINOR__ < 1))
7
#error "GCC < 11.1 on i386 misaligns 64-bit atomics; upgrade the compiler to use int64/uint64 atomics"
8
#endif
9
10
#ifndef __cplusplus
11
#pragma once
12
13
#include <stdatomic.h>
14
15
#define atomicInit(...) (__VA_ARGS__)
16
17
#define AtomicMemoryOrder memory_order
18
#define ATOMIC_MO_Relaxed memory_order_relaxed
19
#define ATOMIC_MO_Acquire memory_order_acquire
20
#define ATOMIC_MO_Release memory_order_release
21
#define ATOMIC_MO_AcqRel memory_order_acq_rel
22
#define ATOMIC_MO_SeqCst memory_order_seq_cst
23
24
#define atomicFence(order) atomic_thread_fence(ATOMIC_MO_##order)
25
26
#define atomic(type) cx_atomic_##type
27
#define atomicLoad(type, atomic_ptr, order) _atomicLoad_##type(atomic_ptr, ATOMIC_MO_##order)
28
#define atomicStore(type, atomic_ptr, val, order) \
29
_atomicStore_##type(atomic_ptr, val, ATOMIC_MO_##order)
30
#define atomicExchange(type, atomic_ptr, val, order) \
31
_atomicExchange_##type(atomic_ptr, val, ATOMIC_MO_##order)
32
#define atomicCompareExchange(type, \
33
semantics, \
34
atomic_ptr, \
35
expected_ptr, \
36
desired, \
37
successorder, \
38
failorder) \
39
_atomicCompareExchange_##semantics##_##type(atomic_ptr, \
40
expected_ptr, \
41
desired, \
42
ATOMIC_MO_##successorder, \
43
ATOMIC_MO_##failorder)
44
45
#define atomicFetchAdd(type, atomic_ptr, val, order) \
46
_atomicFetchAdd_##type(atomic_ptr, val, ATOMIC_MO_##order)
47
#define atomicFetchSub(type, atomic_ptr, val, order) \
48
_atomicFetchSub_##type(atomic_ptr, val, ATOMIC_MO_##order)
49
#define atomicFetchAnd(type, atomic_ptr, val, order) \
50
_atomicFetchAnd_##type(atomic_ptr, val, ATOMIC_MO_##order)
51
#define atomicFetchOr(type, atomic_ptr, val, order) \
52
_atomicFetchOr_##type(atomic_ptr, val, ATOMIC_MO_##order)
53
#define atomicFetchXor(type, atomic_ptr, val, order) \
54
_atomicFetchXor_##type(atomic_ptr, val, ATOMIC_MO_##order)
55
56
#define CX_GENERATE_ATOMICS(type, short_type,
/* unused */
lg_size) \
57
typedef _Atomic(type) cx_atomic_##short_type; \
58
\
59
_meta_inline type _atomicLoad_##short_type(const cx_atomic_##short_type* a, \
60
AtomicMemoryOrder mo) \
61
{ \
62
/* \
63
* A strict interpretation of the C standard prevents \
64
* atomic_load from taking a const argument, but it's \
65
* convenient for our purposes. This cast is a workaround. \
66
*/
\
67
cx_atomic_##short_type* a_nonconst = (cx_atomic_##short_type*)a; \
68
return atomic_load_explicit(a_nonconst, mo); \
69
} \
70
\
71
_meta_inline void _atomicStore_##short_type(cx_atomic_##short_type* a, \
72
type val, \
73
AtomicMemoryOrder mo) \
74
{ \
75
atomic_store_explicit(a, val, mo); \
76
} \
77
\
78
_meta_inline type \
79
_atomicExchange_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
80
{ \
81
return atomic_exchange_explicit(a, val, mo); \
82
} \
83
\
84
_meta_inline bool _atomicCompareExchange_weak_##short_type(cx_atomic_##short_type* a, \
85
type* expected, \
86
type desired, \
87
AtomicMemoryOrder success_mo, \
88
AtomicMemoryOrder failure_mo) \
89
{ \
90
return atomic_compare_exchange_weak_explicit(a, \
91
expected, \
92
desired, \
93
success_mo, \
94
failure_mo); \
95
} \
96
\
97
_meta_inline bool _atomicCompareExchange_strong_##short_type(cx_atomic_##short_type* a, \
98
type* expected, \
99
type desired, \
100
AtomicMemoryOrder success_mo, \
101
AtomicMemoryOrder failure_mo) \
102
{ \
103
return atomic_compare_exchange_strong_explicit(a, \
104
expected, \
105
desired, \
106
success_mo, \
107
failure_mo); \
108
}
109
110
#define CX_EXTERN_ATOMICS(type, short_type) \
111
extern inline type _atomicLoad_##short_type(const cx_atomic_##short_type* a, \
112
AtomicMemoryOrder mo); \
113
\
114
extern inline void _atomicStore_##short_type(cx_atomic_##short_type* a, \
115
type val, \
116
AtomicMemoryOrder mo); \
117
\
118
extern inline type _atomicExchange_##short_type(cx_atomic_##short_type* a, \
119
type val, \
120
AtomicMemoryOrder mo); \
121
\
122
extern inline bool _atomicCompareExchange_weak_##short_type(cx_atomic_##short_type* a, \
123
type* expected, \
124
type desired, \
125
AtomicMemoryOrder success_mo, \
126
AtomicMemoryOrder failure_mo); \
127
\
128
extern inline bool _atomicCompareExchange_strong_##short_type(cx_atomic_##short_type* a, \
129
type* expected, \
130
type desired, \
131
AtomicMemoryOrder success_mo, \
132
AtomicMemoryOrder failure_mo);
133
134
/*
135
* Integral types have some special operations available that non-integral ones
136
* lack.
137
*/
138
#define CX_GENERATE_INT_ATOMICS(type, short_type,
/* unused */
lg_size) \
139
CX_GENERATE_ATOMICS(type, short_type,
/* unused */
lg_size) \
140
\
141
_meta_inline type \
142
_atomicFetchAdd_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
143
{ \
144
return atomic_fetch_add_explicit(a, val, mo); \
145
} \
146
_meta_inline type \
147
_atomicFetchSub_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
148
{ \
149
return atomic_fetch_sub_explicit(a, val, mo); \
150
} \
151
_meta_inline type \
152
_atomicFetchAnd_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
153
{ \
154
return atomic_fetch_and_explicit(a, val, mo); \
155
} \
156
_meta_inline type \
157
_atomicFetchOr_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
158
{ \
159
return atomic_fetch_or_explicit(a, val, mo); \
160
} \
161
_meta_inline type \
162
_atomicFetchXor_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
163
{ \
164
return atomic_fetch_xor_explicit(a, val, mo); \
165
}
166
167
#define CX_EXTERN_INT_ATOMICS(type, short_type) \
168
CX_EXTERN_ATOMICS(type, short_type) \
169
\
170
extern inline type _atomicFetchAdd_##short_type(cx_atomic_##short_type* a, \
171
type val, \
172
AtomicMemoryOrder mo); \
173
extern inline type _atomicFetchSub_##short_type(cx_atomic_##short_type* a, \
174
type val, \
175
AtomicMemoryOrder mo); \
176
extern inline type _atomicFetchAnd_##short_type(cx_atomic_##short_type* a, \
177
type val, \
178
AtomicMemoryOrder mo); \
179
extern inline type _atomicFetchOr_##short_type(cx_atomic_##short_type* a, \
180
type val, \
181
AtomicMemoryOrder mo); \
182
extern inline type _atomicFetchXor_##short_type(cx_atomic_##short_type* a, \
183
type val, \
184
AtomicMemoryOrder mo);
185
#else
// __cplusplus
186
187
/*
188
* g++ and pre-C++23 clang++ can't use C11 <stdatomic.h> and the _Atomic keyword.
189
*
190
* For C++ we need a std::atomic-based implementation that is ABI-compatible with the C branch
191
* above. The C source files are still compiled as C and use the _Atomic types; C++ only references
192
* the same struct layout and call these inline wrappers, so the two must agree on size and
193
* alignment.
194
*
195
* This header is always included outside any extern "C" block, so <atomic> is safe to pull in here.
196
* <atomic> additionally carries its own include guard.
197
*/
198
199
#pragma once
200
201
#include <atomic>
202
203
#define atomicInit(...) (__VA_ARGS__)
204
205
#define AtomicMemoryOrder std::memory_order
206
#define ATOMIC_MO_Relaxed std::memory_order_relaxed
207
#define ATOMIC_MO_Acquire std::memory_order_acquire
208
#define ATOMIC_MO_Release std::memory_order_release
209
#define ATOMIC_MO_AcqRel std::memory_order_acq_rel
210
#define ATOMIC_MO_SeqCst std::memory_order_seq_cst
211
212
#define atomicFence(order) std::atomic_thread_fence(ATOMIC_MO_##order)
213
214
#define atomic(type) cx_atomic_##type
215
#define atomicLoad(type, atomic_ptr, order) _atomicLoad_##type(atomic_ptr, ATOMIC_MO_##order)
216
#define atomicStore(type, atomic_ptr, val, order) \
217
_atomicStore_##type(atomic_ptr, val, ATOMIC_MO_##order)
218
#define atomicExchange(type, atomic_ptr, val, order) \
219
_atomicExchange_##type(atomic_ptr, val, ATOMIC_MO_##order)
220
#define atomicCompareExchange(type, \
221
semantics, \
222
atomic_ptr, \
223
expected_ptr, \
224
desired, \
225
successorder, \
226
failorder) \
227
_atomicCompareExchange_##semantics##_##type(atomic_ptr, \
228
expected_ptr, \
229
desired, \
230
ATOMIC_MO_##successorder, \
231
ATOMIC_MO_##failorder)
232
233
#define atomicFetchAdd(type, atomic_ptr, val, order) \
234
_atomicFetchAdd_##type(atomic_ptr, val, ATOMIC_MO_##order)
235
#define atomicFetchSub(type, atomic_ptr, val, order) \
236
_atomicFetchSub_##type(atomic_ptr, val, ATOMIC_MO_##order)
237
#define atomicFetchAnd(type, atomic_ptr, val, order) \
238
_atomicFetchAnd_##type(atomic_ptr, val, ATOMIC_MO_##order)
239
#define atomicFetchOr(type, atomic_ptr, val, order) \
240
_atomicFetchOr_##type(atomic_ptr, val, ATOMIC_MO_##order)
241
#define atomicFetchXor(type, atomic_ptr, val, order) \
242
_atomicFetchXor_##type(atomic_ptr, val, ATOMIC_MO_##order)
243
244
/*
245
* The is_always_lock_free ABI check is only available with the C++17 library
246
* feature. It is gated so this header stays clean under -std=c++14. A macro
247
* body cannot contain preprocessor directives, so the conditional lives here.
248
*/
249
#if defined(__cpp_lib_atomic_is_always_lock_free)
250
#define CX_ATOMIC_LOCK_FREE_ASSERT(type, short_type) \
251
static_assert(std::atomic<type>::is_always_lock_free, \
252
"cx_atomic_" #short_type " must be always lock-free " \
253
"for C ABI compatibility");
254
#else
255
#define CX_ATOMIC_LOCK_FREE_ASSERT(type, short_type)
256
#endif
257
258
#define CX_GENERATE_ATOMICS(type, short_type,
/* unused */
lg_size) \
259
typedef std::atomic<type> cx_atomic_##short_type; \
260
static_assert(sizeof(cx_atomic_##short_type) == sizeof(type), \
261
"cx_atomic_" #short_type " size must match " #type " for C ABI compat"); \
262
static_assert(alignof(cx_atomic_##short_type) == \
263
(sizeof(type) > alignof(type) ? sizeof(type) : alignof(type)), \
264
"cx_atomic_" #short_type " alignment must match " #type " for C ABI compat"); \
265
CX_ATOMIC_LOCK_FREE_ASSERT(type, short_type) \
266
\
267
_meta_inline type _atomicLoad_##short_type(const cx_atomic_##short_type* a, \
268
AtomicMemoryOrder mo) \
269
{ \
270
/* \
271
* A strict interpretation of the C standard prevents \
272
* atomic_load from taking a const argument, but it's \
273
* convenient for our purposes. This cast is a workaround. \
274
*/
\
275
cx_atomic_##short_type* a_nonconst = (cx_atomic_##short_type*)a; \
276
return a_nonconst->load(mo); \
277
} \
278
\
279
_meta_inline void _atomicStore_##short_type(cx_atomic_##short_type* a, \
280
type val, \
281
AtomicMemoryOrder mo) \
282
{ \
283
a->store(val, mo); \
284
} \
285
\
286
_meta_inline type \
287
_atomicExchange_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
288
{ \
289
return a->exchange(val, mo); \
290
} \
291
\
292
_meta_inline bool _atomicCompareExchange_weak_##short_type(cx_atomic_##short_type* a, \
293
type* expected, \
294
type desired, \
295
AtomicMemoryOrder success_mo, \
296
AtomicMemoryOrder failure_mo) \
297
{ \
298
return a->compare_exchange_weak(*expected, desired, success_mo, failure_mo); \
299
} \
300
\
301
_meta_inline bool _atomicCompareExchange_strong_##short_type(cx_atomic_##short_type* a, \
302
type* expected, \
303
type desired, \
304
AtomicMemoryOrder success_mo, \
305
AtomicMemoryOrder failure_mo) \
306
{ \
307
return a->compare_exchange_strong(*expected, desired, success_mo, failure_mo); \
308
}
309
310
#define CX_EXTERN_ATOMICS(type, short_type)
311
312
/*
313
* Integral types have some special operations available that non-integral ones
314
* lack.
315
*/
316
#define CX_GENERATE_INT_ATOMICS(type, short_type,
/* unused */
lg_size) \
317
CX_GENERATE_ATOMICS(type, short_type,
/* unused */
lg_size) \
318
\
319
_meta_inline type \
320
_atomicFetchAdd_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
321
{ \
322
return a->fetch_add(val, mo); \
323
} \
324
_meta_inline type \
325
_atomicFetchSub_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
326
{ \
327
return a->fetch_sub(val, mo); \
328
} \
329
_meta_inline type \
330
_atomicFetchAnd_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
331
{ \
332
return a->fetch_and(val, mo); \
333
} \
334
_meta_inline type \
335
_atomicFetchOr_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
336
{ \
337
return a->fetch_or(val, mo); \
338
} \
339
_meta_inline type \
340
_atomicFetchXor_##short_type(cx_atomic_##short_type* a, type val, AtomicMemoryOrder mo) \
341
{ \
342
return a->fetch_xor(val, mo); \
343
}
344
345
#define CX_EXTERN_INT_ATOMICS(type, short_type)
346
347
#endif
// __cplusplus
cx
platform
clang
clang_atomic.h
Generated by
1.9.8