xref: /php-src/Zend/zend_atomic.h (revision 72526609)
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 #elif defined(HAVE_C11_ATOMICS)
47 typedef struct zend_atomic_bool_s {
48 	_Atomic(bool) value;
49 } zend_atomic_bool;
50 #else
51 typedef struct zend_atomic_bool_s {
52 	volatile bool value;
53 } zend_atomic_bool;
54 #endif
55 
BEGIN_EXTERN_C()56 BEGIN_EXTERN_C()
57 
58 #ifdef ZEND_WIN32
59 
60 #ifndef InterlockedExchange8
61 #define InterlockedExchange8 _InterlockedExchange8
62 #endif
63 #ifndef InterlockedOr8
64 #define InterlockedOr8 _InterlockedOr8
65 #endif
66 
67 #define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
68 
69 static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
70 	return InterlockedExchange8(&obj->value, desired);
71 }
72 
73 /* On this platform it is non-const due to Iterlocked API*/
zend_atomic_bool_load_ex(zend_atomic_bool * obj)74 static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) {
75 	/* Or'ing with false won't change the value. */
76 	return InterlockedOr8(&obj->value, false);
77 }
78 
zend_atomic_bool_store_ex(zend_atomic_bool * obj,bool desired)79 static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
80 	(void)InterlockedExchange8(&obj->value, desired);
81 }
82 
83 #elif defined(HAVE_C11_ATOMICS)
84 
85 #define ZEND_ATOMIC_BOOL_INIT(obj, desired) __c11_atomic_init(&(obj)->value, (desired))
86 
87 static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
88 	return __c11_atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST);
89 }
90 
91 static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
92 	return __c11_atomic_load(&obj->value, __ATOMIC_SEQ_CST);
93 }
94 
95 static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
96 	__c11_atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST);
97 }
98 
99 #elif defined(HAVE_GNUC_ATOMICS)
100 
101 #define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
102 
103 static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
104 	bool prev = false;
105 	__atomic_exchange(&obj->value, &desired, &prev, __ATOMIC_SEQ_CST);
106 	return prev;
107 }
108 
109 static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
110 	bool prev = false;
111 	__atomic_load(&obj->value, &prev, __ATOMIC_SEQ_CST);
112 	return prev;
113 }
114 
115 static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
116 	__atomic_store(&obj->value, &desired, __ATOMIC_SEQ_CST);
117 }
118 
119 #elif defined(HAVE_SYNC_ATOMICS)
120 
121 #define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
122 
123 static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
124 	bool prev = __sync_lock_test_and_set(&obj->value, desired);
125 
126 	/* __sync_lock_test_and_set only does an acquire barrier, so sync
127 	 * immediately after.
128 	 */
129 	__sync_synchronize();
130 	return prev;
131 }
132 
133 static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) {
134 	/* Or'ing false won't change the value */
135 	return __sync_fetch_and_or(&obj->value, false);
136 }
137 
138 static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
139 	__sync_synchronize();
140 	obj->value = desired;
141 	__sync_synchronize();
142 }
143 
144 #elif defined(HAVE_NO_ATOMICS)
145 
146 #warning No atomics support detected. Please open an issue with platform details.
147 
148 #define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired))
149 
150 static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
151 	obj->value = desired;
152 }
153 
154 static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
155 	return obj->value;
156 }
157 
158 static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
159 	bool prev = obj->value;
160 	obj->value = desired;
161 	return prev;
162 }
163 
164 #endif
165 
166 ZEND_API void zend_atomic_bool_init(zend_atomic_bool *obj, bool desired);
167 ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired);
168 ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired);
169 
170 #if defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)
171 /* On these platforms it is non-const due to underlying APIs. */
172 ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj);
173 #else
174 ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj);
175 #endif
176 
177 END_EXTERN_C()
178 
179 #endif
180