xref: /php-src/Zend/zend_atomic.h (revision 1ab4520c)
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)
43 typedef struct zend_atomic_bool_s {
44 	volatile char value;
45 } zend_atomic_bool;
46 typedef struct zend_atomic_int_s {
47 # ifdef ZEND_WIN32
48 	volatile long value;
49 # else
50 	volatile int value;
51 # endif
52 } zend_atomic_int;
53 #elif defined(HAVE_C11_ATOMICS)
54 typedef struct zend_atomic_bool_s {
55 	_Atomic(bool) value;
56 } zend_atomic_bool;
57 typedef struct zend_atomic_int_s {
58 	_Atomic(int) value;
59 } zend_atomic_int;
60 #else
61 typedef struct zend_atomic_bool_s {
62 	volatile bool value;
63 } zend_atomic_bool;
64 typedef struct zend_atomic_int_s {
65 	volatile int value;
66 } zend_atomic_int;
67 #endif
68 
BEGIN_EXTERN_C()69 BEGIN_EXTERN_C()
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 
98 static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
99 	return InterlockedExchange8(&obj->value, desired);
100 }
101 
zend_atomic_int_exchange_ex(zend_atomic_int * obj,int desired)102 static zend_always_inline int zend_atomic_int_exchange_ex(zend_atomic_int *obj, int desired) {
103 	return (int) InterlockedExchange(&obj->value, desired);
104 }
105 
zend_atomic_bool_compare_exchange_ex(zend_atomic_bool * obj,bool * expected,bool desired)106 static 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 
zend_atomic_int_compare_exchange_ex(zend_atomic_int * obj,int * expected,int desired)116 static 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*/
zend_atomic_bool_load_ex(zend_atomic_bool * obj)127 static 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 
zend_atomic_int_load_ex(zend_atomic_int * obj)132 static 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 
zend_atomic_bool_store_ex(zend_atomic_bool * obj,bool desired)137 static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
138 	(void)InterlockedExchange8(&obj->value, desired);
139 }
140 
zend_atomic_int_store_ex(zend_atomic_int * obj,int desired)141 static 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 
153 static 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 
157 static 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 
161 static 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 
165 static 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 
169 static 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 
173 static 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 
177 static 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 
181 static 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 
195 static 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 
201 static 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 
207 static 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 
211 static 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 
215 static 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 
221 static 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 
227 static 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 
231 static 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 
243 static 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 
253 static 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 
263 static 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 
273 static 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 
283 static 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 
288 static 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 
293 static 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 
299 static 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 
315 static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
316 	obj->value = desired;
317 }
318 
319 static zend_always_inline void zend_atomic_int_store_ex(zend_atomic_int *obj, int desired) {
320 	obj->value = desired;
321 }
322 
323 static 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 
334 static 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 
345 static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
346 	return obj->value;
347 }
348 
349 static zend_always_inline int zend_atomic_int_load_ex(const zend_atomic_int *obj) {
350 	return obj->value;
351 }
352 
353 static 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 
359 static 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 
367 ZEND_API void zend_atomic_bool_init(zend_atomic_bool *obj, bool desired);
368 ZEND_API void zend_atomic_int_init(zend_atomic_int *obj, int desired);
369 
370 ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired);
371 ZEND_API int zend_atomic_int_exchange(zend_atomic_int *obj, int desired);
372 
373 ZEND_API bool zend_atomic_bool_compare_exchange(zend_atomic_bool *obj, bool *expected, bool desired);
374 ZEND_API bool zend_atomic_int_compare_exchange(zend_atomic_int *obj, int *expected, int desired);
375 
376 ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired);
377 ZEND_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. */
381 ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj);
382 ZEND_API int zend_atomic_int_load(zend_atomic_int *obj);
383 #else
384 ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj);
385 ZEND_API int zend_atomic_int_load(const zend_atomic_int *obj);
386 #endif
387 
388 END_EXTERN_C()
389 
390 #endif
391