|
19 | 19 |
|
20 | 20 | #include <stdbool.h>
|
21 | 21 |
|
22 |
| -/* These functions correspond to C11's stdatomic.h functions but are type- |
23 |
| - * specialized because it's difficult to provide portable routines such as |
24 |
| - * exchange without making functions. |
25 |
| - * |
26 |
| - * Treat zend_atomic_* types as opaque. They have definitions only for size |
| 22 | +#ifndef __has_feature |
| 23 | +#define __has_feature(x) 0 |
| 24 | +#endif |
| 25 | + |
| 26 | +/* Builtins are used to avoid library linkage */ |
| 27 | +#if __has_feature(c_atomic) |
| 28 | +#define HAVE_C11_ATOMICS 1 |
| 29 | +#elif __GNUC_PREREQ__(4, 7) |
| 30 | +#define HAVE_GNUC_ATOMICS 1 |
| 31 | +#elif defined(__GNUC__) |
| 32 | +#define HAVE_SYNC_ATOMICS 1 |
| 33 | +#elif !defined(ZEND_WIN32) |
| 34 | +#define HAVE_NO_ATOMICS 1 |
| 35 | +#endif |
| 36 | + |
| 37 | +/* Treat zend_atomic_* types as opaque. They have definitions only for size |
27 | 38 | * and alignment purposes.
|
28 | 39 | */
|
29 | 40 |
|
| 41 | +#if ZEND_WIN32 |
| 42 | +typedef struct zend_atomic_bool_s { |
| 43 | + volatile char value; |
| 44 | +} zend_atomic_bool; |
| 45 | +#elif HAVE_C11_ATOMICS |
| 46 | +typedef struct zend_atomic_bool_s { |
| 47 | + _Atomic(bool) value; |
| 48 | +} zend_atomic_bool; |
| 49 | +#else |
30 | 50 | typedef struct zend_atomic_bool_s {
|
31 |
| - volatile bool bytes; |
| 51 | + volatile bool value; |
32 | 52 | } zend_atomic_bool;
|
| 53 | +#endif |
33 | 54 |
|
34 | 55 | BEGIN_EXTERN_C()
|
35 | 56 |
|
36 |
| -ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj); |
37 |
| -ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired); |
38 |
| -ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired); |
| 57 | +#if ZEND_WIN32 |
| 58 | + |
| 59 | +#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired)) |
| 60 | + |
| 61 | +inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) { |
| 62 | + return InterlockedExchange8(&obj->value, desired); |
| 63 | +} |
| 64 | + |
| 65 | +/* On this platform it is non-const due to Iterlocked API*/ |
| 66 | +inline ZEND_API bool zend_atomic_bool_load(zend_atomic_bool *obj) { |
| 67 | + /* Or'ing with false won't change the value. */ |
| 68 | + return InterlockedOr8(&obj->value, false); |
| 69 | +} |
| 70 | + |
| 71 | +inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) { |
| 72 | + (void)InterlockedExchange8(&obj->value, desired); |
| 73 | +} |
| 74 | + |
| 75 | +#elif HAVE_C11_ATOMICS |
| 76 | + |
| 77 | +#define ZEND_ATOMIC_BOOL_INIT(obj, desired) __c11_atomic_init(&(obj)->value, (desired)) |
| 78 | + |
| 79 | +inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) { |
| 80 | + return __c11_atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST); |
| 81 | +} |
| 82 | + |
| 83 | +inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) { |
| 84 | + return __c11_atomic_load(&obj->value, __ATOMIC_SEQ_CST); |
| 85 | +} |
| 86 | + |
| 87 | +inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) { |
| 88 | + __c11_atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST); |
| 89 | +} |
| 90 | + |
| 91 | +#elif HAVE_GNUC_ATOMICS |
| 92 | + |
| 93 | +#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired)) |
| 94 | + |
| 95 | +inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) { |
| 96 | + return __atomic_exchange(&obj->value, desired, __ATOMIC_SEQ_CST); |
| 97 | +} |
| 98 | + |
| 99 | +inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) { |
| 100 | + return __atomic_load(&obj->value, __ATOMIC_SEQ_CST); |
| 101 | +} |
| 102 | + |
| 103 | +inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) { |
| 104 | + __atomic_store(&obj->value, desired, __ATOMIC_SEQ_CST); |
| 105 | +} |
| 106 | + |
| 107 | +#elif HAVE_SYNC_ATOMICS |
| 108 | + |
| 109 | +#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired)) |
| 110 | + |
| 111 | +inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) { |
| 112 | + bool prev = __sync_lock_test_and_set(&obj->value, desired); |
| 113 | + |
| 114 | + /* __sync_lock_test_and_set only does an acquire barrier, so sync |
| 115 | + * immediately after. |
| 116 | + */ |
| 117 | + __sync_synchronize(); |
| 118 | + return prev; |
| 119 | +} |
| 120 | + |
| 121 | +inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) { |
| 122 | + /* Or'ing false won't change the value */ |
| 123 | + return __sync_fetch_and_or(&obj->value, false); |
| 124 | +} |
| 125 | + |
| 126 | +inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) { |
| 127 | + __sync_synchronize(); |
| 128 | + obj->value = desired; |
| 129 | + __sync_synchronize(); |
| 130 | +} |
| 131 | + |
| 132 | +#elif HAVE_NO_ATOMICS |
| 133 | + |
| 134 | +#define ZEND_ATOMIC_BOOL_INIT(obj, desired) ((obj)->value = (desired)) |
| 135 | + |
| 136 | +/* Yes, these are not guaranteed to be atomic. Understand that previously |
| 137 | + * atomics were never used, so the fact they are sometimes used is an |
| 138 | + * improvement. As more platforms support C11 atomics, or as we add support |
| 139 | + * for more platforms through intrinsics/asm, this should be used less and |
| 140 | + * less until it can be removed. |
| 141 | + */ |
| 142 | + |
| 143 | +inline ZEND_API void zend_atomic_bool_store(zend_atomic_bool *obj, bool desired) { |
| 144 | + obj->value = desired; |
| 145 | +} |
| 146 | + |
| 147 | +inline ZEND_API bool zend_atomic_bool_load(const zend_atomic_bool *obj) { |
| 148 | + return obj->value; |
| 149 | +} |
| 150 | + |
| 151 | +inline ZEND_API bool zend_atomic_bool_exchange(zend_atomic_bool *obj, bool desired) { |
| 152 | + bool prev = obj->value; |
| 153 | + obj->value = true; |
| 154 | + return prev; |
| 155 | +} |
| 156 | + |
| 157 | +#error Just seeing if any platforms in CI are using this fallback. |
| 158 | +#endif |
39 | 159 |
|
40 | 160 | END_EXTERN_C()
|
41 | 161 |
|
|
0 commit comments