Skip to content

Commit 1ab4520

Browse files
authored
Add zend_atomic_int (#14242)
Extend the zend_atomic API for ints. Also add `zend_atomic_*_compare_exchange()`, `ZEND_ATOMIC_*_INITIALIZER()`.
1 parent d8da8d8 commit 1ab4520

File tree

2 files changed

+240
-1
lines changed

2 files changed

+240
-1
lines changed

Zend/zend_atomic.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,56 @@
2020
* is also where the code will go.
2121
*/
2222

23-
/* Defined for FFI users; everyone else use ZEND_ATOMIC_BOOL_INIT.
23+
/* Defined for FFI users; everyone else use ZEND_ATOMIC_*_INIT.
2424
* This is NOT ATOMIC as it is meant for initialization.
2525
*/
2626
ZEND_API void zend_atomic_bool_init(zend_atomic_bool *obj, bool desired) {
2727
ZEND_ATOMIC_BOOL_INIT(obj, desired);
2828
}
2929

30+
ZEND_API void zend_atomic_int_init(zend_atomic_int *obj, int desired) {
31+
ZEND_ATOMIC_INT_INIT(obj, desired);
32+
}
33+
3034
ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) {
3135
return zend_atomic_bool_exchange_ex(obj, desired);
3236
}
3337

38+
ZEND_API int zend_atomic_int_exchange(zend_atomic_int *obj, int desired) {
39+
return zend_atomic_int_exchange_ex(obj, desired);
40+
}
41+
42+
ZEND_API bool zend_atomic_bool_compare_exchange(zend_atomic_bool *obj, bool *expected, bool desired)
43+
{
44+
return zend_atomic_bool_compare_exchange_ex(obj, expected, desired);
45+
}
46+
47+
ZEND_API bool zend_atomic_int_compare_exchange(zend_atomic_int *obj, int *expected, int desired)
48+
{
49+
return zend_atomic_int_compare_exchange_ex(obj, expected, desired);
50+
}
51+
3452
ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) {
3553
zend_atomic_bool_store_ex(obj, desired);
3654
}
3755

56+
ZEND_API void zend_atomic_int_store(zend_atomic_int *obj, int desired) {
57+
zend_atomic_int_store_ex(obj, desired);
58+
}
59+
3860
#if defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)
3961
/* On these platforms it is non-const due to underlying APIs. */
4062
ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj) {
4163
return zend_atomic_bool_load_ex(obj);
4264
}
65+
ZEND_API int zend_atomic_int_load(zend_atomic_int *obj) {
66+
return zend_atomic_int_load_ex(obj);
67+
}
4368
#else
4469
ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) {
4570
return zend_atomic_bool_load_ex(obj);
4671
}
72+
ZEND_API int zend_atomic_int_load(const zend_atomic_int *obj) {
73+
return zend_atomic_int_load_ex(obj);
74+
}
4775
#endif

Zend/zend_atomic.h

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,27 @@
4343
typedef struct zend_atomic_bool_s {
4444
volatile char value;
4545
} 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;
4653
#elif defined(HAVE_C11_ATOMICS)
4754
typedef struct zend_atomic_bool_s {
4855
_Atomic(bool) value;
4956
} zend_atomic_bool;
57+
typedef struct zend_atomic_int_s {
58+
_Atomic(int) value;
59+
} zend_atomic_int;
5060
#else
5161
typedef struct zend_atomic_bool_s {
5262
volatile bool value;
5363
} zend_atomic_bool;
64+
typedef struct zend_atomic_int_s {
65+
volatile int value;
66+
} zend_atomic_int;
5467
#endif
5568

5669
BEGIN_EXTERN_C()
@@ -63,62 +76,169 @@ BEGIN_EXTERN_C()
6376
#ifndef InterlockedOr8
6477
#define InterlockedOr8 _InterlockedOr8
6578
#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
6691

6792
#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)}
6897

6998
static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
7099
return InterlockedExchange8(&obj->value, desired);
71100
}
72101

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+
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+
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+
73126
/* On this platform it is non-const due to Iterlocked API*/
74127
static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) {
75128
/* Or'ing with false won't change the value. */
76129
return InterlockedOr8(&obj->value, false);
77130
}
78131

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+
79137
static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
80138
(void)InterlockedExchange8(&obj->value, desired);
81139
}
82140

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+
83145
#elif defined(HAVE_C11_ATOMICS)
84146

85147
#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)}
86152

87153
static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
88154
return __c11_atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST);
89155
}
90156

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+
91169
static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
92170
return __c11_atomic_load(&obj->value, __ATOMIC_SEQ_CST);
93171
}
94172

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+
95177
static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
96178
__c11_atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST);
97179
}
98180

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+
99185
#elif defined(HAVE_GNUC_ATOMICS)
100186

187+
/* bool */
188+
101189
#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)}
102194

103195
static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
104196
bool prev = false;
105197
__atomic_exchange(&obj->value, &desired, &prev, __ATOMIC_SEQ_CST);
106198
return prev;
107199
}
108200

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+
109215
static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
110216
bool prev = false;
111217
__atomic_load(&obj->value, &prev, __ATOMIC_SEQ_CST);
112218
return prev;
113219
}
114220

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+
115227
static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
116228
__atomic_store(&obj->value, &desired, __ATOMIC_SEQ_CST);
117229
}
118230

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+
119235
#elif defined(HAVE_SYNC_ATOMICS)
120236

121237
#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)}
122242

123243
static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
124244
bool prev = __sync_lock_test_and_set(&obj->value, desired);
@@ -130,48 +250,139 @@ static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *ob
130250
return prev;
131251
}
132252

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+
133283
static zend_always_inline bool zend_atomic_bool_load_ex(zend_atomic_bool *obj) {
134284
/* Or'ing false won't change the value */
135285
return __sync_fetch_and_or(&obj->value, false);
136286
}
137287

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+
138293
static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
139294
__sync_synchronize();
140295
obj->value = desired;
141296
__sync_synchronize();
142297
}
143298

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+
144305
#elif defined(HAVE_NO_ATOMICS)
145306

146307
#warning No atomics support detected. Please open an issue with platform details.
147308

148309
#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)}
149314

150315
static zend_always_inline void zend_atomic_bool_store_ex(zend_atomic_bool *obj, bool desired) {
151316
obj->value = desired;
152317
}
153318

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+
154345
static zend_always_inline bool zend_atomic_bool_load_ex(const zend_atomic_bool *obj) {
155346
return obj->value;
156347
}
157348

349+
static zend_always_inline int zend_atomic_int_load_ex(const zend_atomic_int *obj) {
350+
return obj->value;
351+
}
352+
158353
static zend_always_inline bool zend_atomic_bool_exchange_ex(zend_atomic_bool *obj, bool desired) {
159354
bool prev = obj->value;
160355
obj->value = desired;
161356
return prev;
162357
}
163358

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+
164365
#endif
165366

166367
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+
167370
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+
168376
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);
169378

170379
#if defined(ZEND_WIN32) || defined(HAVE_SYNC_ATOMICS)
171380
/* On these platforms it is non-const due to underlying APIs. */
172381
ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj);
382+
ZEND_API int zend_atomic_int_load(zend_atomic_int *obj);
173383
#else
174384
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);
175386
#endif
176387

177388
END_EXTERN_C()

0 commit comments

Comments
 (0)