php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
zend_atomic.h
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | This source file is subject to version 3.01 of the PHP license, |
4 | that is bundled with this package in the file LICENSE, and is |
5 | available through the world-wide-web at the following url: |
6 | https://www.php.net/license/3_01.txt |
7 | If you did not receive a copy of the PHP license and are unable to |
8 | obtain it through the world-wide-web, please send a note to |
9 | license@php.net so we can mail you a copy immediately. |
10 +----------------------------------------------------------------------+
11 | Authors: Levi Morrison <morrison.levi@gmail.com> |
12 +----------------------------------------------------------------------+
13 */
14
15#ifndef ZEND_ATOMIC_H
16#define ZEND_ATOMIC_H
17
18#include "zend_portability.h"
19
20#include <stdbool.h>
21
22#define ZEND_GCC_PREREQ(x, y) \
23 ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || (__GNUC__ > (x)))
24
25/* Builtins are used to avoid library linkage */
26#if __has_feature(c_atomic) && defined(__clang__)
27#define HAVE_C11_ATOMICS 1
28#elif ZEND_GCC_PREREQ(4, 7)
29#define HAVE_GNUC_ATOMICS 1
30#elif defined(__GNUC__)
31#define HAVE_SYNC_ATOMICS 1
32#elif !defined(ZEND_WIN32)
33#define HAVE_NO_ATOMICS 1
34#endif
35
36#undef ZEND_GCC_PREREQ
37
38/* Treat zend_atomic_* types as opaque. They have definitions only for size
39 * and alignment purposes.
40 */
41
42#if defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)
43typedef struct zend_atomic_bool_s {
44 volatile char value;
46typedef struct zend_atomic_int_s {
47# ifdef ZEND_WIN32
48 volatile long value;
49# else
50 volatile int value;
51# endif
53#elif defined(HAVE_C11_ATOMICS)
54typedef struct zend_atomic_bool_s {
55 _Atomic(bool) value;
57typedef struct zend_atomic_int_s {
58 _Atomic(int) value;
60#else
61typedef struct zend_atomic_bool_s {
62 volatile bool value;
64typedef struct zend_atomic_int_s {
65 volatile int value;
67#endif
68
70
71#ifdef ZEND_WIN32
72
73#ifndef InterlockedExchange8
74#define InterlockedExchange8 _InterlockedExchange8
75#endif
76#ifndef InterlockedOr8
77#define InterlockedOr8 _InterlockedOr8
78#endif
79#ifndef InterlockedCompareExchange8
80#define InterlockedCompareExchange8 _InterlockedCompareExchange8
81#endif
82#ifndef InterlockedExchange
83#define InterlockedExchange _InterlockedExchange
84#endif
85#ifndef InterlockedOr
86#define InterlockedOr _InterlockedOr
87#endif
88#ifndef InterlockedCompareExchange
89#define InterlockedCompareExchange _InterlockedCompareExchange
90#endif
91
92#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
93#define ZEND_ATOMIC_INT_INIT(obj, desired) ((obj)->value = (desired))
94
95#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)}
96#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)}
97
98static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
99 return InterlockedExchange8(&obj->value, desired);
100}
101
102static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) {
103 return (int) InterlockedExchange(&obj->value, desired);
104}
105
106static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_bool *obj, bool *expected, bool desired) {
107 bool prev = (bool) InterlockedCompareExchange8(&obj->value, *expected, desired);
108 if (prev == *expected) {
109 return true;
110 } else {
111 *expected = prev;
112 return false;
113 }
114}
115
116static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) {
117 int prev = (int) InterlockedCompareExchange(&obj->value, *expected, desired);
118 if (prev == *expected) {
119 return true;
120 } else {
121 *expected = prev;
122 return false;
123 }
124}
125
126/* On this platform it is non-const due to Iterlocked API*/
127static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) {
128 /* Or'ing with false won't change the value. */
129 return InterlockedOr8(&obj->value, false);
130}
131
132static zend_always_inline int zend_atomic_int_load_ex(zend_atomic_int *obj) {
133 /* Or'ing with 0 won't change the value. */
134 return (int) InterlockedOr(&obj->value, 0);
135}
136
137static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
138 (void)InterlockedExchange8(&obj->value, desired);
139}
140
141static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) {
142 (void)InterlockedExchange(&obj->value, desired);
143}
144
145#elif defined(HAVE_C11_ATOMICS)
146
147#define ZEND_ATOMIC_BOOL_INIT(obj, desired) __c11_atomic_init(&(obj)->value, (desired))
148#define ZEND_ATOMIC_INT_INIT(obj, desired) __c11_atomic_init(&(obj)->value, (desired))
149
150#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)}
151#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)}
152
153static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
154 return __c11_atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST);
155}
156
157static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) {
158 return __c11_atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST);
159}
160
161static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_bool *obj, bool *expected, bool desired) {
162 return __c11_atomic_compare_exchange_strong(&obj->value, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
163}
164
165static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) {
166 return __c11_atomic_compare_exchange_strong(&obj->value, expected, desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
167}
168
169static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
170 return __c11_atomic_load(&obj->value, __ATOMIC_SEQ_CST);
171}
172
173static zend_always_inline int zend_atomic_int_load_ex(const zend_atomic_int *obj) {
174 return __c11_atomic_load(&obj->value, __ATOMIC_SEQ_CST);
175}
176
177static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
178 __c11_atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST);
179}
180
181static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) {
182 __c11_atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST);
183}
184
185#elif defined(HAVE_GNUC_ATOMICS)
186
187/* bool */
188
189#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
190#define ZEND_ATOMIC_INT_INIT(obj, desired) ((obj)->value = (desired))
191
192#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)}
193#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)}
194
195static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
196 bool prev = false;
197 __atomic_exchange(&obj->value, &desired, &prev, __ATOMIC_SEQ_CST);
198 return prev;
199}
200
201static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) {
202 int prev = false;
203 __atomic_exchange(&obj->value, &desired, &prev, __ATOMIC_SEQ_CST);
204 return prev;
205}
206
207static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_bool *obj, bool *expected, bool desired) {
208 return __atomic_compare_exchange(&obj->value, expected, &desired, /* weak */ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
209}
210
211static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) {
212 return __atomic_compare_exchange(&obj->value, expected, &desired, /* weak */ false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
213}
214
215static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
216 bool prev = false;
217 __atomic_load(&obj->value, &prev, __ATOMIC_SEQ_CST);
218 return prev;
219}
220
221static zend_always_inline int zend_atomic_int_load_ex(const zend_atomic_int *obj) {
222 int prev = false;
223 __atomic_load(&obj->value, &prev, __ATOMIC_SEQ_CST);
224 return prev;
225}
226
227static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
228 __atomic_store(&obj->value, &desired, __ATOMIC_SEQ_CST);
229}
230
231static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) {
232 __atomic_store(&obj->value, &desired, __ATOMIC_SEQ_CST);
233}
234
235#elif defined(HAVE_SYNC_ATOMICS)
236
237#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
238#define ZEND_ATOMIC_INT_INIT(obj, desired) ((obj)->value = (desired))
239
240#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)}
241#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)}
242
243static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
244 bool prev = __sync_lock_test_and_set(&obj->value, desired);
245
246 /* __sync_lock_test_and_set only does an acquire barrier, so sync
247 * immediately after.
248 */
249 __sync_synchronize();
250 return prev;
251}
252
253static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) {
254 int prev = __sync_lock_test_and_set(&obj->value, desired);
255
256 /* __sync_lock_test_and_set only does an acquire barrier, so sync
257 * immediately after.
258 */
259 __sync_synchronize();
260 return prev;
261}
262
263static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_bool *obj, bool *expected, bool desired) {
264 bool prev = __sync_val_compare_and_swap(&obj->value, *expected, desired);
265 if (prev == *expected) {
266 return true;
267 } else {
268 *expected = prev;
269 return false;
270 }
271}
272
273static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) {
274 int prev = __sync_val_compare_and_swap(&obj->value, *expected, desired);
275 if (prev == *expected) {
276 return true;
277 } else {
278 *expected = prev;
279 return false;
280 }
281}
282
283static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) {
284 /* Or'ing false won't change the value */
285 return __sync_fetch_and_or(&obj->value, false);
286}
287
288static zend_always_inline int zend_atomic_int_load_ex(zend_atomic_int *obj) {
289 /* Or'ing 0 won't change the value */
290 return __sync_fetch_and_or(&obj->value, 0);
291}
292
293static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
294 __sync_synchronize();
295 obj->value = desired;
296 __sync_synchronize();
297}
298
299static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) {
300 __sync_synchronize();
301 obj->value = desired;
302 __sync_synchronize();
303}
304
305#elif defined(HAVE_NO_ATOMICS)
306
307#warning No atomics support detected. Please open an issue with platform details.
308
309#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
310#define ZEND_ATOMIC_INT_INIT(obj, desired) ((obj)->value = (desired))
311
312#define ZEND_ATOMIC_BOOL_INITIALIZER(desired) {.value = (desired)}
313#define ZEND_ATOMIC_INT_INITIALIZER(desired) {.value = (desired)}
314
315static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
316 obj->value = desired;
317}
318
319static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) {
320 obj->value = desired;
321}
322
323static zend_always_inline bool zend_atomic_bool_compare_exchange_ex(zend_atomic_int *obj, bool *expected, bool desired) {
324 bool prev = obj->value;
325 if (prev == *expected) {
326 obj->value = desired;
327 return true;
328 } else {
329 *expected = prev;
330 return false;
331 }
332}
333
334static zend_always_inline bool zend_atomic_int_compare_exchange_ex(zend_atomic_int *obj, int *expected, int desired) {
335 int prev = obj->value;
336 if (prev == *expected) {
337 obj->value = desired;
338 return true;
339 } else {
340 *expected = prev;
341 return false;
342 }
343}
344
345static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
346 return obj->value;
347}
348
349static zend_always_inline int zend_atomic_int_load_ex(const zend_atomic_int *obj) {
350 return obj->value;
351}
352
353static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
354 bool prev = obj->value;
355 obj->value = desired;
356 return prev;
357}
358
359static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) {
360 int prev = obj->value;
361 obj->value = desired;
362 return prev;
363}
364
365#endif
366
367ZEND_API void zend_atomic_bool_init(zend_atomic_bool *obj, bool desired);
368ZEND_API void zend_atomic_int_init(zend_atomic_int *obj, int desired);
369
372
373ZEND_API bool zend_atomic_bool_compare_exchange(zend_atomic_bool *obj, bool *expected, bool desired);
374ZEND_API bool zend_atomic_int_compare_exchange(zend_atomic_int *obj, int *expected, int desired);
375
376ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired);
377ZEND_API void zend_atomic_int_store(zend_atomic_int *obj, int desired);
378
379#if defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)
380/* On these platforms it is non-const due to underlying APIs. */
383#else
386#endif
387
389
390#endif
prev(array|object &$array)
volatile bool value
Definition zend_atomic.h:62
volatile int value
Definition zend_atomic.h:65
ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired)
Definition zend_atomic.c:52
ZEND_API int zend_atomic_int_load(const zend_atomic_int *obj)
Definition zend_atomic.c:72
ZEND_API bool zend_atomic_int_compare_exchange(zend_atomic_int *obj, int *expected, int desired)
Definition zend_atomic.c:47
ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired)
Definition zend_atomic.c:34
struct zend_atomic_int_s zend_atomic_int
ZEND_API void zend_atomic_int_store(zend_atomic_int *obj, int desired)
Definition zend_atomic.c:56
ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj)
Definition zend_atomic.c:69
ZEND_API void zend_atomic_bool_init(zend_atomic_bool *obj, bool desired)
Definition zend_atomic.c:26
ZEND_API int zend_atomic_int_exchange(zend_atomic_int *obj, int desired)
Definition zend_atomic.c:38
ZEND_API void zend_atomic_int_init(zend_atomic_int *obj, int desired)
Definition zend_atomic.c:30
ZEND_API bool zend_atomic_bool_compare_exchange(zend_atomic_bool *obj, bool *expected, bool desired)
Definition zend_atomic.c:42
struct zend_atomic_bool_s zend_atomic_bool
#define ZEND_API
ZEND_API void(ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data)
#define END_EXTERN_C()
#define zend_always_inline
#define BEGIN_EXTERN_C()
value