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