1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * generic-gcc.h |
4 | * Atomic operations, implemented using gcc (or compatible) intrinsics. |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | * |
9 | * NOTES: |
10 | * |
11 | * Documentation: |
12 | * * Legacy __sync Built-in Functions for Atomic Memory Access |
13 | * http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/_005f_005fsync-Builtins.html |
14 | * * Built-in functions for memory model aware atomic operations |
15 | * http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/_005f_005fatomic-Builtins.html |
16 | * |
17 | * src/include/port/atomics/generic-gcc.h |
18 | * |
19 | *------------------------------------------------------------------------- |
20 | */ |
21 | |
22 | /* intentionally no include guards, should only be included by atomics.h */ |
23 | #ifndef INSIDE_ATOMICS_H |
24 | #error "should be included via atomics.h" |
25 | #endif |
26 | |
27 | /* |
28 | * An empty asm block should be a sufficient compiler barrier. |
29 | */ |
30 | #define pg_compiler_barrier_impl() __asm__ __volatile__("" ::: "memory") |
31 | |
32 | /* |
33 | * If we're on GCC 4.1.0 or higher, we should be able to get a memory barrier |
34 | * out of this compiler built-in. But we prefer to rely on platform specific |
35 | * definitions where possible, and use this only as a fallback. |
36 | */ |
37 | #if !defined(pg_memory_barrier_impl) |
38 | # if defined(HAVE_GCC__ATOMIC_INT32_CAS) |
39 | # define pg_memory_barrier_impl() __atomic_thread_fence(__ATOMIC_SEQ_CST) |
40 | # elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) |
41 | # define pg_memory_barrier_impl() __sync_synchronize() |
42 | # endif |
43 | #endif /* !defined(pg_memory_barrier_impl) */ |
44 | |
45 | #if !defined(pg_read_barrier_impl) && defined(HAVE_GCC__ATOMIC_INT32_CAS) |
46 | /* acquire semantics include read barrier semantics */ |
47 | # define pg_read_barrier_impl() __atomic_thread_fence(__ATOMIC_ACQUIRE) |
48 | #endif |
49 | |
50 | #if !defined(pg_write_barrier_impl) && defined(HAVE_GCC__ATOMIC_INT32_CAS) |
51 | /* release semantics include write barrier semantics */ |
52 | # define pg_write_barrier_impl() __atomic_thread_fence(__ATOMIC_RELEASE) |
53 | #endif |
54 | |
55 | |
56 | #ifdef HAVE_ATOMICS |
57 | |
58 | /* generic gcc based atomic flag implementation */ |
59 | #if !defined(PG_HAVE_ATOMIC_FLAG_SUPPORT) \ |
60 | && (defined(HAVE_GCC__SYNC_INT32_TAS) || defined(HAVE_GCC__SYNC_CHAR_TAS)) |
61 | |
62 | #define PG_HAVE_ATOMIC_FLAG_SUPPORT |
63 | typedef struct pg_atomic_flag |
64 | { |
65 | /* |
66 | * If we have a choice, use int-width TAS, because that is more efficient |
67 | * and/or more reliably implemented on most non-Intel platforms. (Note |
68 | * that this code isn't used on x86[_64]; see arch-x86.h for that.) |
69 | */ |
70 | #ifdef HAVE_GCC__SYNC_INT32_TAS |
71 | volatile int value; |
72 | #else |
73 | volatile char value; |
74 | #endif |
75 | } pg_atomic_flag; |
76 | |
77 | #endif /* !ATOMIC_FLAG_SUPPORT && SYNC_INT32_TAS */ |
78 | |
79 | /* generic gcc based atomic uint32 implementation */ |
80 | #if !defined(PG_HAVE_ATOMIC_U32_SUPPORT) \ |
81 | && (defined(HAVE_GCC__ATOMIC_INT32_CAS) || defined(HAVE_GCC__SYNC_INT32_CAS)) |
82 | |
83 | #define PG_HAVE_ATOMIC_U32_SUPPORT |
84 | typedef struct pg_atomic_uint32 |
85 | { |
86 | volatile uint32 value; |
87 | } pg_atomic_uint32; |
88 | |
89 | #endif /* defined(HAVE_GCC__ATOMIC_INT32_CAS) || defined(HAVE_GCC__SYNC_INT32_CAS) */ |
90 | |
91 | /* generic gcc based atomic uint64 implementation */ |
92 | #if !defined(PG_HAVE_ATOMIC_U64_SUPPORT) \ |
93 | && !defined(PG_DISABLE_64_BIT_ATOMICS) \ |
94 | && (defined(HAVE_GCC__ATOMIC_INT64_CAS) || defined(HAVE_GCC__SYNC_INT64_CAS)) |
95 | |
96 | #define PG_HAVE_ATOMIC_U64_SUPPORT |
97 | |
98 | typedef struct pg_atomic_uint64 |
99 | { |
100 | volatile uint64 value pg_attribute_aligned(8); |
101 | } pg_atomic_uint64; |
102 | |
103 | #endif /* defined(HAVE_GCC__ATOMIC_INT64_CAS) || defined(HAVE_GCC__SYNC_INT64_CAS) */ |
104 | |
105 | #ifdef PG_HAVE_ATOMIC_FLAG_SUPPORT |
106 | |
107 | #if defined(HAVE_GCC__SYNC_CHAR_TAS) || defined(HAVE_GCC__SYNC_INT32_TAS) |
108 | |
109 | #ifndef PG_HAVE_ATOMIC_TEST_SET_FLAG |
110 | #define PG_HAVE_ATOMIC_TEST_SET_FLAG |
111 | static inline bool |
112 | pg_atomic_test_set_flag_impl(volatile pg_atomic_flag *ptr) |
113 | { |
114 | /* NB: only an acquire barrier, not a full one */ |
115 | /* some platform only support a 1 here */ |
116 | return __sync_lock_test_and_set(&ptr->value, 1) == 0; |
117 | } |
118 | #endif |
119 | |
120 | #endif /* defined(HAVE_GCC__SYNC_*_TAS) */ |
121 | |
122 | #ifndef PG_HAVE_ATOMIC_UNLOCKED_TEST_FLAG |
123 | #define PG_HAVE_ATOMIC_UNLOCKED_TEST_FLAG |
124 | static inline bool |
125 | pg_atomic_unlocked_test_flag_impl(volatile pg_atomic_flag *ptr) |
126 | { |
127 | return ptr->value == 0; |
128 | } |
129 | #endif |
130 | |
131 | #ifndef PG_HAVE_ATOMIC_CLEAR_FLAG |
132 | #define PG_HAVE_ATOMIC_CLEAR_FLAG |
133 | static inline void |
134 | pg_atomic_clear_flag_impl(volatile pg_atomic_flag *ptr) |
135 | { |
136 | __sync_lock_release(&ptr->value); |
137 | } |
138 | #endif |
139 | |
140 | #ifndef PG_HAVE_ATOMIC_INIT_FLAG |
141 | #define PG_HAVE_ATOMIC_INIT_FLAG |
142 | static inline void |
143 | pg_atomic_init_flag_impl(volatile pg_atomic_flag *ptr) |
144 | { |
145 | pg_atomic_clear_flag_impl(ptr); |
146 | } |
147 | #endif |
148 | |
149 | #endif /* defined(PG_HAVE_ATOMIC_FLAG_SUPPORT) */ |
150 | |
151 | /* prefer __atomic, it has a better API */ |
152 | #if !defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) && defined(HAVE_GCC__ATOMIC_INT32_CAS) |
153 | #define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32 |
154 | static inline bool |
155 | pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, |
156 | uint32 *expected, uint32 newval) |
157 | { |
158 | /* FIXME: we can probably use a lower consistency model */ |
159 | return __atomic_compare_exchange_n(&ptr->value, expected, newval, false, |
160 | __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); |
161 | } |
162 | #endif |
163 | |
164 | #if !defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) |
165 | #define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U32 |
166 | static inline bool |
167 | pg_atomic_compare_exchange_u32_impl(volatile pg_atomic_uint32 *ptr, |
168 | uint32 *expected, uint32 newval) |
169 | { |
170 | bool ret; |
171 | uint32 current; |
172 | current = __sync_val_compare_and_swap(&ptr->value, *expected, newval); |
173 | ret = current == *expected; |
174 | *expected = current; |
175 | return ret; |
176 | } |
177 | #endif |
178 | |
179 | /* if we have 32-bit __sync_val_compare_and_swap, assume we have these too: */ |
180 | |
181 | #if !defined(PG_HAVE_ATOMIC_FETCH_ADD_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) |
182 | #define PG_HAVE_ATOMIC_FETCH_ADD_U32 |
183 | static inline uint32 |
184 | pg_atomic_fetch_add_u32_impl(volatile pg_atomic_uint32 *ptr, int32 add_) |
185 | { |
186 | return __sync_fetch_and_add(&ptr->value, add_); |
187 | } |
188 | #endif |
189 | |
190 | #if !defined(PG_HAVE_ATOMIC_FETCH_SUB_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) |
191 | #define PG_HAVE_ATOMIC_FETCH_SUB_U32 |
192 | static inline uint32 |
193 | pg_atomic_fetch_sub_u32_impl(volatile pg_atomic_uint32 *ptr, int32 sub_) |
194 | { |
195 | return __sync_fetch_and_sub(&ptr->value, sub_); |
196 | } |
197 | #endif |
198 | |
199 | #if !defined(PG_HAVE_ATOMIC_FETCH_AND_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) |
200 | #define PG_HAVE_ATOMIC_FETCH_AND_U32 |
201 | static inline uint32 |
202 | pg_atomic_fetch_and_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 and_) |
203 | { |
204 | return __sync_fetch_and_and(&ptr->value, and_); |
205 | } |
206 | #endif |
207 | |
208 | #if !defined(PG_HAVE_ATOMIC_FETCH_OR_U32) && defined(HAVE_GCC__SYNC_INT32_CAS) |
209 | #define PG_HAVE_ATOMIC_FETCH_OR_U32 |
210 | static inline uint32 |
211 | pg_atomic_fetch_or_u32_impl(volatile pg_atomic_uint32 *ptr, uint32 or_) |
212 | { |
213 | return __sync_fetch_and_or(&ptr->value, or_); |
214 | } |
215 | #endif |
216 | |
217 | |
218 | #if !defined(PG_DISABLE_64_BIT_ATOMICS) |
219 | |
220 | #if !defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) && defined(HAVE_GCC__ATOMIC_INT64_CAS) |
221 | #define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 |
222 | static inline bool |
223 | pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, |
224 | uint64 *expected, uint64 newval) |
225 | { |
226 | return __atomic_compare_exchange_n(&ptr->value, expected, newval, false, |
227 | __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); |
228 | } |
229 | #endif |
230 | |
231 | #if !defined(PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) |
232 | #define PG_HAVE_ATOMIC_COMPARE_EXCHANGE_U64 |
233 | static inline bool |
234 | pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr, |
235 | uint64 *expected, uint64 newval) |
236 | { |
237 | bool ret; |
238 | uint64 current; |
239 | current = __sync_val_compare_and_swap(&ptr->value, *expected, newval); |
240 | ret = current == *expected; |
241 | *expected = current; |
242 | return ret; |
243 | } |
244 | #endif |
245 | |
246 | /* if we have 64-bit __sync_val_compare_and_swap, assume we have these too: */ |
247 | |
248 | #if !defined(PG_HAVE_ATOMIC_FETCH_ADD_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) |
249 | #define PG_HAVE_ATOMIC_FETCH_ADD_U64 |
250 | static inline uint64 |
251 | pg_atomic_fetch_add_u64_impl(volatile pg_atomic_uint64 *ptr, int64 add_) |
252 | { |
253 | return __sync_fetch_and_add(&ptr->value, add_); |
254 | } |
255 | #endif |
256 | |
257 | #if !defined(PG_HAVE_ATOMIC_FETCH_SUB_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) |
258 | #define PG_HAVE_ATOMIC_FETCH_SUB_U64 |
259 | static inline uint64 |
260 | pg_atomic_fetch_sub_u64_impl(volatile pg_atomic_uint64 *ptr, int64 sub_) |
261 | { |
262 | return __sync_fetch_and_sub(&ptr->value, sub_); |
263 | } |
264 | #endif |
265 | |
266 | #if !defined(PG_HAVE_ATOMIC_FETCH_AND_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) |
267 | #define PG_HAVE_ATOMIC_FETCH_AND_U64 |
268 | static inline uint64 |
269 | pg_atomic_fetch_and_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 and_) |
270 | { |
271 | return __sync_fetch_and_and(&ptr->value, and_); |
272 | } |
273 | #endif |
274 | |
275 | #if !defined(PG_HAVE_ATOMIC_FETCH_OR_U64) && defined(HAVE_GCC__SYNC_INT64_CAS) |
276 | #define PG_HAVE_ATOMIC_FETCH_OR_U64 |
277 | static inline uint64 |
278 | pg_atomic_fetch_or_u64_impl(volatile pg_atomic_uint64 *ptr, uint64 or_) |
279 | { |
280 | return __sync_fetch_and_or(&ptr->value, or_); |
281 | } |
282 | #endif |
283 | |
284 | #endif /* !defined(PG_DISABLE_64_BIT_ATOMICS) */ |
285 | |
286 | #endif /* defined(HAVE_ATOMICS) */ |
287 | |