]> git.sesse.net Git - vlc/blob - include/vlc_atomic.h
Fix mutex-emulated atomic operations on GCC
[vlc] / include / vlc_atomic.h
1 /*****************************************************************************
2  * vlc_atomic.h:
3  *****************************************************************************
4  * Copyright (C) 2010 RĂ©mi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifndef VLC_ATOMIC_H
22 # define VLC_ATOMIC_H
23
24 /**
25  * \file
26  * Atomic operations do not require locking, but they are not very powerful.
27  */
28
29 # if !defined (__cplusplus) && (__STDC_VERSION__ >= 201112L) \
30   && !defined (__STDC_NO_ATOMICS__)
31
32 /*** Native C11 atomics ***/
33 #  include <stdatomic.h>
34
35 # else
36
37 #  define ATOMIC_FLAG_INIT false
38
39 #  define ATOMIC_VAR_INIT(value) (value)
40
41 #  define atomic_init(obj, value) \
42     do { *(obj) = (value); } while(0)
43
44 #  define kill_dependency(y) \
45     ((void)0)
46
47 #  define atomic_thread_fence(order) \
48     __sync_synchronize()
49
50 #  define atomic_signal_fence(order) \
51     ((void)0)
52
53 #  define atomic_is_lock_free(obj) \
54     false
55
56 /* In principles, __sync_*() only supports int, long and long long and their
57  * unsigned equivalents, i.e. 4-bytes and 8-bytes types, although GCC also
58  * supports 1 and 2-bytes types. Some non-x86 architectures do not support
59  * 8-byte atomic types (or not efficiently). */
60 typedef          bool      atomic_flag;
61 typedef          bool      atomic_bool;
62 typedef          char      atomic_char;
63 typedef   signed char      atomic_schar;
64 typedef unsigned char      atomic_uchar;
65 typedef          short     atomic_short;
66 typedef unsigned short     atomic_ushort;
67 typedef          int       atomic_int;
68 typedef unsigned int       atomic_uint;
69 typedef          long      atomic_long;
70 typedef unsigned long      atomic_ulong;
71 typedef          long long atomic_llong;
72 typedef unsigned long long atomic_ullong;
73 //typedef          char16_t  atomic_char16_t;
74 //typedef          char32_t  atomic_char32_t;
75 typedef          wchar_t   atomic_wchar_t;
76 typedef       int_least8_t atomic_int_least8_t;
77 typedef      uint_least8_t atomic_uint_least8_t;
78 typedef      int_least16_t atomic_int_least16_t;
79 typedef     uint_least16_t atomic_uint_least16_t;
80 typedef      int_least32_t atomic_int_least32_t;
81 typedef     uint_least32_t atomic_uint_least32_t;
82 typedef      int_least64_t atomic_int_least64_t;
83 typedef     uint_least64_t atomic_uint_least64_t;
84 typedef       int_fast8_t atomic_int_fast8_t;
85 typedef      uint_fast8_t atomic_uint_fast8_t;
86 typedef      int_fast16_t atomic_int_fast16_t;
87 typedef     uint_fast16_t atomic_uint_fast16_t;
88 typedef      int_fast32_t atomic_int_fast32_t;
89 typedef     uint_fast32_t atomic_uint_fast32_t;
90 typedef      int_fast64_t atomic_int_fast64_t;
91 typedef     uint_fast64_t atomic_uint_fast64_t;
92 typedef          intptr_t atomic_intptr_t;
93 typedef         uintptr_t atomic_uintptr_t;
94 typedef            size_t atomic_size_t;
95 typedef         ptrdiff_t atomic_ptrdiff_t;
96 typedef          intmax_t atomic_intmax_t;
97 typedef         uintmax_t atomic_uintmax_t;
98
99 # if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || (defined (__clang__) && (defined (__x86_64__) || defined (__i386__)))
100
101 /*** Intel/GCC atomics ***/
102
103 #  define atomic_store(object,desired) \
104     do { \
105         *(object) = (desired); \
106         __sync_synchronize(); \
107     } while (0)
108
109 #  define atomic_store_explicit(object,desired,order) \
110     atomic_store(object,desired)
111
112 #  define atomic_load(object) \
113     (__sync_synchronize(), *(object))
114
115 #  define atomic_load_explicit(object,order) \
116     atomic_load(object)
117
118 #  define atomic_exchange(object,desired) \
119 ({  \
120     typeof (object) _obj = (object); \
121     typeof (*object) _old; \
122     do \
123         _old = atomic_load(_obj); \
124     while (!__sync_bool_compare_and_swap(_obj, _old, (desired))); \
125     _old; \
126 })
127
128 #  define atomic_exchange_explicit(object,desired,order) \
129     atomic_exchange(object,desired)
130
131 #  define atomic_compare_exchange(object,expected,desired) \
132 ({  \
133     typeof (object) _exp = (expected); \
134     typeof (*object) _old = *_exp; \
135     *_exp = __sync_val_compare_and_swap((object), _old, (desired)); \
136     *_exp == _old; \
137 })
138
139 #  define atomic_compare_exchange_strong(object,expected,desired) \
140     atomic_compare_exchange(object, expected, desired)
141
142 #  define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \
143     atomic_compare_exchange_strong(object, expected, desired)
144
145 #  define atomic_compare_exchange_weak(object,expected,desired) \
146     atomic_compare_exchange(object, expected, desired)
147
148 #  define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \
149     atomic_compare_exchange_weak(object, expected, desired)
150
151 #  define atomic_fetch_add(object,operand) \
152     __sync_fetch_and_add(object, operand)
153
154 #  define atomic_fetch_add_explicit(object,operand,order) \
155     atomic_fetch_add(object,operand)
156
157 #  define atomic_fetch_sub(object,operand) \
158     __sync_fetch_and_sub(object, operand)
159
160 #  define atomic_fetch_sub_explicit(object,operand,order) \
161     atomic_fetch_sub(object,operand)
162
163 #  define atomic_fetch_or(object,operand) \
164     __sync_fetch_and_or(object, operand)
165
166 #  define atomic_fetch_or_explicit(object,operand,order) \
167     atomic_fetch_or(object,operand)
168
169 #  define atomic_fetch_xor(object,operand) \
170     __sync_fetch_and_sub(object, operand)
171
172 #  define atomic_fetch_xor_explicit(object,operand,order) \
173     atomic_fetch_sub(object,operand)
174
175 #  define atomic_fetch_and(object,operand) \
176     __sync_fetch_and_and(object, operand)
177
178 #  define atomic_fetch_and_explicit(object,operand,order) \
179     atomic_fetch_and(object,operand)
180
181 #  define atomic_flag_test_and_set(object) \
182     atomic_exchange(object, true)
183
184 #  define atomic_flag_test_and_set_explicit(object,order) \
185     atomic_flag_test_and_set(object)
186
187 #  define atomic_flag_clear(object) \
188     atomic_store(object, false)
189
190 #  define atomic_flag_clear_explicit(object,order) \
191     atomic_flag_clear(object)
192
193 # elif defined (__GNUC__)
194
195 /*** No atomics ***/
196
197 #  define atomic_store(object,desired) \
198     do { \
199         typeof (object) _obj = (object); \
200         typeof (*object) _des = (desired); \
201         vlc_global_lock(VLC_ATOMIC_MUTEX); \
202         *_obj = _des; \
203         vlc_global_unlock(VLC_ATOMIC_MUTEX); \
204     } while (0)
205 #  define atomic_store_explicit(object,desired,order) \
206     atomic_store(object,desired)
207
208 #  define atomic_load(object) \
209 ({ \
210     typeof (object) _obj = (object); \
211     typeof (*object) _old; \
212     vlc_global_lock(VLC_ATOMIC_MUTEX); \
213     _old = *_obj; \
214     vlc_global_unlock(VLC_ATOMIC_MUTEX); \
215     _old; \
216 })
217 #  define atomic_load_explicit(object,order) \
218     atomic_load(object)
219
220 #  define atomic_exchange(object,desired) \
221 ({ \
222     typeof (object) _obj = (object); \
223     typeof (*object) _des = (desired); \
224     typeof (*object) _old; \
225     vlc_global_lock(VLC_ATOMIC_MUTEX); \
226     _old = *_obj; \
227     *_obj = _des; \
228     vlc_global_unlock(VLC_ATOMIC_MUTEX); \
229     _old; \
230 })
231 #  define atomic_exchange_explicit(object,desired,order) \
232     atomic_exchange(object,desired)
233
234 #  define atomic_compare_exchange_strong(object,expected,desired) \
235 ({ \
236     typeof (object) _obj = (object); \
237     typeof (object) _exp = (expected); \
238     typeof (*object) _des = (desired); \
239     bool ret; \
240     vlc_global_lock(VLC_ATOMIC_MUTEX); \
241     ret = *_obj == *_exp; \
242     if (ret) \
243         *_obj = _des; \
244     else \
245         *_exp = *_obj; \
246     vlc_global_unlock(VLC_ATOMIC_MUTEX); \
247     ret; \
248 })
249 #  define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \
250     atomic_compare_exchange_strong(object, expected, desired)
251 #  define atomic_compare_exchange_weak(object,expected,desired) \
252     atomic_compare_exchange_strong(object, expected, desired)
253 #  define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \
254     atomic_compare_exchange_weak(object, expected, desired)
255
256 #  define atomic_fetch_OP(object,desired,op) \
257 ({ \
258     typeof (object) _obj = (object); \
259     typeof (*object) _des = (desired); \
260     typeof (*object) _old; \
261     vlc_global_lock(VLC_ATOMIC_MUTEX); \
262     _old = *_obj; \
263     *_obj = (*_obj) op (_des); \
264     vlc_global_unlock(VLC_ATOMIC_MUTEX); \
265     _old; \
266 })
267
268 #  define atomic_fetch_add(object,operand) \
269     atomic_fetch_OP(object,operand,+)
270 #  define atomic_fetch_add_explicit(object,operand,order) \
271     atomic_fetch_add(object,operand)
272
273 #  define atomic_fetch_sub(object,operand) \
274     atomic_fetch_OP(object,operand,-)
275 #  define atomic_fetch_sub_explicit(object,operand,order) \
276     atomic_fetch_sub(object,operand)
277
278 #  define atomic_fetch_or(object,operand) \
279     atomic_fetch_OP(object,operand,|)
280 #  define atomic_fetch_or_explicit(object,operand,order) \
281     atomic_fetch_or(object,operand)
282
283 #  define atomic_fetch_xor(object,operand) \
284     atomic_fetch_OP(object,operand,^)
285 #  define atomic_fetch_xor_explicit(object,operand,order) \
286     atomic_fetch_sub(object,operand)
287
288 #  define atomic_fetch_and(object,operand) \
289     atomic_fetch_OP(object,operand,&)
290 #  define atomic_fetch_and_explicit(object,operand,order) \
291     atomic_fetch_and(object,operand)
292
293 #  define atomic_flag_test_and_set(object) \
294     atomic_exchange(object, true)
295
296 #  define atomic_flag_test_and_set_explicit(object,order) \
297     atomic_flag_test_and_set(object)
298
299 #  define atomic_flag_clear(object) \
300     atomic_store(object, false)
301
302 #  define atomic_flag_clear_explicit(object,order) \
303     atomic_flag_clear(object)
304
305 # else
306 #  error FIXME: implement atomic operations for this compiler.
307 # endif
308 # endif
309
310 /**
311  * Memory storage space for an atom. Never access it directly.
312  */
313 typedef union
314 {
315     atomic_uintptr_t u;
316 } vlc_atomic_t;
317
318 /** Static initializer for \ref vlc_atomic_t */
319 # define VLC_ATOMIC_INIT(val) { (val) }
320
321 /* All functions return the atom value _after_ the operation. */
322 static inline uintptr_t vlc_atomic_get(vlc_atomic_t *atom)
323 {
324     return atomic_load(&atom->u);
325 }
326
327 static inline uintptr_t vlc_atomic_set(vlc_atomic_t *atom, uintptr_t v)
328 {
329     atomic_store(&atom->u, v);
330     return v;
331 }
332
333 static inline uintptr_t vlc_atomic_add(vlc_atomic_t *atom, uintptr_t v)
334 {
335     return atomic_fetch_add(&atom->u, v) + v;
336 }
337
338 static inline uintptr_t vlc_atomic_sub (vlc_atomic_t *atom, uintptr_t v)
339 {
340     return atomic_fetch_sub (&atom->u, v) - v;
341 }
342
343 static inline uintptr_t vlc_atomic_inc (vlc_atomic_t *atom)
344 {
345     return vlc_atomic_add (atom, 1);
346 }
347
348 static inline uintptr_t vlc_atomic_dec (vlc_atomic_t *atom)
349 {
350     return vlc_atomic_sub (atom, 1);
351 }
352
353 static inline uintptr_t vlc_atomic_swap(vlc_atomic_t *atom, uintptr_t v)
354 {
355     return atomic_exchange(&atom->u, v);
356 }
357
358 static inline uintptr_t vlc_atomic_compare_swap(vlc_atomic_t *atom,
359                                                 uintptr_t u, uintptr_t v)
360 {
361     atomic_compare_exchange_strong(&atom->u, &u, v);
362     return u;
363 }
364
365 /** Helper to retrieve a single precision from an atom. */
366 static inline float vlc_atomic_getf(vlc_atomic_t *atom)
367 {
368     union { float f; uintptr_t i; } u;
369     u.i = vlc_atomic_get(atom);
370     return u.f;
371 }
372
373 /** Helper to store a single precision into an atom. */
374 static inline float vlc_atomic_setf(vlc_atomic_t *atom, float f)
375 {
376     union { float f; uintptr_t i; } u;
377     u.f = f;
378     vlc_atomic_set(atom, u.i);
379     return f;
380 }
381
382 #endif