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