|
10 | 10 | #ifndef UMF_UTILS_CONCURRENCY_H |
11 | 11 | #define UMF_UTILS_CONCURRENCY_H 1 |
12 | 12 |
|
| 13 | +#include <stdbool.h> |
| 14 | +#include <stdint.h> |
13 | 15 | #include <stdio.h> |
14 | 16 | #include <stdlib.h> |
15 | 17 |
|
|
19 | 21 | #include "utils_windows_intrin.h" |
20 | 22 |
|
21 | 23 | #pragma intrinsic(_BitScanForward64) |
22 | | -#else |
| 24 | +#else /* !_WIN32 */ |
23 | 25 | #include <pthread.h> |
24 | 26 |
|
25 | 27 | #ifndef __cplusplus |
26 | 28 | #include <stdatomic.h> |
27 | 29 | #else /* __cplusplus */ |
28 | 30 | #include <atomic> |
29 | 31 | #define _Atomic(X) std::atomic<X> |
| 32 | + |
| 33 | +using std::memory_order_acq_rel; |
| 34 | +using std::memory_order_acquire; |
| 35 | +using std::memory_order_relaxed; |
| 36 | +using std::memory_order_release; |
| 37 | + |
30 | 38 | #endif /* __cplusplus */ |
31 | 39 |
|
32 | | -#endif /* _WIN32 */ |
| 40 | +#endif /* !_WIN32 */ |
33 | 41 |
|
| 42 | +#include "utils_common.h" |
34 | 43 | #include "utils_sanitizers.h" |
35 | 44 |
|
36 | 45 | #ifdef __cplusplus |
@@ -92,57 +101,123 @@ static __inline unsigned char utils_mssb_index(long long value) { |
92 | 101 | } |
93 | 102 |
|
94 | 103 | // There is no good way to do atomic_load on windows... |
95 | | -#define utils_atomic_load_acquire(object, dest) \ |
96 | | - do { \ |
97 | | - *(LONG64 *)dest = \ |
98 | | - InterlockedOr64Acquire((LONG64 volatile *)object, 0); \ |
99 | | - } while (0) |
| 104 | +static __inline void utils_atomic_load_acquire_u64(uint64_t *ptr, |
| 105 | + uint64_t *out) { |
| 106 | + // NOTE: Windows cl complains about direct accessing 'ptr' which is next |
| 107 | + // accessed using Interlocked* functions (warning 28112 - disabled) |
| 108 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 109 | + |
| 110 | + // On Windows, there is no equivalent to __atomic_load, so we use cmpxchg |
| 111 | + // with 0, 0 here. This will always return the value under the pointer |
| 112 | + // without writing anything. |
| 113 | + LONG64 ret = InterlockedCompareExchange64((LONG64 volatile *)ptr, 0, 0); |
| 114 | + *out = *(uint64_t *)&ret; |
| 115 | +} |
| 116 | + |
| 117 | +static __inline void utils_atomic_load_acquire_ptr(void **ptr, void **out) { |
| 118 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 119 | + uintptr_t ret = (uintptr_t)InterlockedCompareExchangePointer(ptr, 0, 0); |
| 120 | + *(uintptr_t *)out = ret; |
| 121 | +} |
100 | 122 |
|
101 | | -#define utils_atomic_store_release(object, desired) \ |
102 | | - InterlockedExchange64((LONG64 volatile *)object, (LONG64)desired) |
| 123 | +static __inline void utils_atomic_store_release_ptr(void **ptr, void *val) { |
| 124 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 125 | + InterlockedExchangePointer(ptr, val); |
| 126 | +} |
103 | 127 |
|
104 | | -#define utils_atomic_increment(object) \ |
105 | | - InterlockedIncrement64((LONG64 volatile *)object) |
| 128 | +static __inline uint64_t utils_atomic_increment_u64(uint64_t *ptr) { |
| 129 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 130 | + // return incremented value |
| 131 | + return InterlockedIncrement64((LONG64 volatile *)ptr); |
| 132 | +} |
106 | 133 |
|
107 | | -#define utils_atomic_decrement(object) \ |
108 | | - InterlockedDecrement64((LONG64 volatile *)object) |
| 134 | +static __inline uint64_t utils_atomic_decrement_u64(uint64_t *ptr) { |
| 135 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 136 | + // return decremented value |
| 137 | + return InterlockedDecrement64((LONG64 volatile *)ptr); |
| 138 | +} |
109 | 139 |
|
110 | | -#define utils_fetch_and_add64(ptr, value) \ |
111 | | - InterlockedExchangeAdd64((LONG64 *)(ptr), value) |
| 140 | +static __inline uint64_t utils_fetch_and_add_u64(uint64_t *ptr, uint64_t val) { |
| 141 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 142 | + // return the value that had previously been in *ptr |
| 143 | + return InterlockedExchangeAdd64((LONG64 volatile *)(ptr), val); |
| 144 | +} |
112 | 145 |
|
113 | | -// NOTE: windows version have different order of args |
114 | | -#define utils_compare_exchange(object, desired, expected) \ |
115 | | - InterlockedCompareExchange64((LONG64 volatile *)object, *expected, *desired) |
| 146 | +static __inline uint64_t utils_fetch_and_sub_u64(uint64_t *ptr, uint64_t val) { |
| 147 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 148 | + // return the value that had previously been in *ptr |
| 149 | + // NOTE: on Windows there is no *Sub* version of InterlockedExchange |
| 150 | + return InterlockedExchangeAdd64((LONG64 volatile *)(ptr), -(LONG64)val); |
| 151 | +} |
| 152 | + |
| 153 | +static __inline bool utils_compare_exchange_u64(uint64_t *ptr, |
| 154 | + uint64_t *expected, |
| 155 | + uint64_t *desired) { |
| 156 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 157 | + LONG64 out = InterlockedCompareExchange64( |
| 158 | + (LONG64 volatile *)ptr, *(LONG64 *)desired, *(LONG64 *)expected); |
| 159 | + if (out == *(LONG64 *)expected) { |
| 160 | + return true; |
| 161 | + } |
| 162 | + |
| 163 | + // else |
| 164 | + *expected = out; |
| 165 | + return false; |
| 166 | +} |
116 | 167 |
|
117 | 168 | #else // !defined(_WIN32) |
118 | 169 |
|
119 | 170 | #define utils_lssb_index(x) ((unsigned char)__builtin_ctzll(x)) |
120 | 171 | #define utils_mssb_index(x) ((unsigned char)(63 - __builtin_clzll(x))) |
121 | 172 |
|
122 | | -#define utils_atomic_load_acquire(object, dest) \ |
123 | | - do { \ |
124 | | - utils_annotate_acquire((void *)object); \ |
125 | | - __atomic_load(object, dest, memory_order_acquire); \ |
126 | | - } while (0) |
| 173 | +static inline void utils_atomic_load_acquire_u64(uint64_t *ptr, uint64_t *out) { |
| 174 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 175 | + ASSERT_IS_ALIGNED((uintptr_t)out, 8); |
| 176 | + __atomic_load(ptr, out, memory_order_acquire); |
| 177 | +} |
127 | 178 |
|
128 | | -#define utils_atomic_store_release(object, desired) \ |
129 | | - do { \ |
130 | | - __atomic_store_n(object, desired, memory_order_release); \ |
131 | | - utils_annotate_release((void *)object); \ |
132 | | - } while (0) |
| 179 | +static inline void utils_atomic_load_acquire_ptr(void **ptr, void **out) { |
| 180 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 181 | + ASSERT_IS_ALIGNED((uintptr_t)out, 8); |
| 182 | + __atomic_load((uintptr_t *)ptr, (uintptr_t *)out, memory_order_acquire); |
| 183 | +} |
133 | 184 |
|
134 | | -#define utils_atomic_increment(object) \ |
135 | | - __atomic_add_fetch(object, 1, memory_order_acq_rel) |
| 185 | +static inline void utils_atomic_store_release_ptr(void **ptr, void *val) { |
| 186 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 187 | + __atomic_store_n((uintptr_t *)ptr, (uintptr_t)val, memory_order_release); |
| 188 | +} |
136 | 189 |
|
137 | | -#define utils_atomic_decrement(object) \ |
138 | | - __atomic_sub_fetch(object, 1, memory_order_acq_rel) |
| 190 | +static inline uint64_t utils_atomic_increment_u64(uint64_t *val) { |
| 191 | + ASSERT_IS_ALIGNED((uintptr_t)val, 8); |
| 192 | + // return incremented value |
| 193 | + return __atomic_add_fetch(val, 1, memory_order_acq_rel); |
| 194 | +} |
139 | 195 |
|
140 | | -#define utils_fetch_and_add64(object, value) \ |
141 | | - __atomic_fetch_add(object, value, memory_order_acq_rel) |
| 196 | +static inline uint64_t utils_atomic_decrement_u64(uint64_t *val) { |
| 197 | + ASSERT_IS_ALIGNED((uintptr_t)val, 8); |
| 198 | + // return decremented value |
| 199 | + return __atomic_sub_fetch(val, 1, memory_order_acq_rel); |
| 200 | +} |
142 | 201 |
|
143 | | -#define utils_compare_exchange(object, expected, desired) \ |
144 | | - __atomic_compare_exchange(object, expected, desired, 0 /* strong */, \ |
145 | | - memory_order_acq_rel, memory_order_relaxed) |
| 202 | +static inline uint64_t utils_fetch_and_add_u64(uint64_t *ptr, uint64_t val) { |
| 203 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 204 | + // return the value that had previously been in *ptr |
| 205 | + return __atomic_fetch_add(ptr, val, memory_order_acq_rel); |
| 206 | +} |
| 207 | + |
| 208 | +static inline uint64_t utils_fetch_and_sub_u64(uint64_t *ptr, uint64_t val) { |
| 209 | + // return the value that had previously been in *ptr |
| 210 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 211 | + return __atomic_fetch_sub(ptr, val, memory_order_acq_rel); |
| 212 | +} |
| 213 | + |
| 214 | +static inline bool utils_compare_exchange_u64(uint64_t *ptr, uint64_t *expected, |
| 215 | + uint64_t *desired) { |
| 216 | + ASSERT_IS_ALIGNED((uintptr_t)ptr, 8); |
| 217 | + return __atomic_compare_exchange(ptr, expected, desired, 0 /* strong */, |
| 218 | + memory_order_acq_rel, |
| 219 | + memory_order_relaxed); |
| 220 | +} |
146 | 221 |
|
147 | 222 | #endif // !defined(_WIN32) |
148 | 223 |
|
|
0 commit comments