]> git.sesse.net Git - vlc/blob - include/vlc_atomic.h
Implement atomic operations for MSVC using the Interlocked API.
[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 # elif defined (_MSC_VER)
306
307 /*** Use the Interlocked API. ***/
308
309 /* Define macros in order to dispatch to the correct function depending on the type.
310    Several ranges are need because some operations are not implemented for all types. */
311 #  define atomic_type_dispatch_32_64(operation, object, ...) \
312     (sizeof(*object) == 4 ? operation(object, __VA_ARGS__) : \
313      sizeof(*object) == 8 ? operation##64(object, __VA_ARGS__) : \
314      (abort(), 0))
315
316 #  define atomic_type_dispatch_16_64(operation, object, ...) \
317     (sizeof(*object) == 2 ? operation##16(object, __VA_ARGS__) : \
318      atomic_type_dispatch_32_64(operation, object, __VA_ARGS__))
319
320 #  define atomic_type_dispatch_8_64(operation, object, ...) \
321     (sizeof(*object) == 1 ? operation##8(object, __VA_ARGS__) : \
322      atomic_type_dispatch_16_64(operation, object, __VA_ARGS__))
323
324 #  define atomic_store(object,desired) \
325     atomic_type_dispatch_8_64(InterlockedExchange, object, desired)
326 #  define atomic_store_explicit(object,desired,order) \
327     atomic_store(object, desired)
328
329 #  define atomic_load(object) \
330     atomic_type_dispatch_16_64(InterlockedCompareExchange, object, 0, 0)
331 #  define atomic_load_explicit(object,order) \
332     atomic_load(object)
333
334 #  define atomic_exchange(object,desired) \
335     atomic_type_dispatch_8_64(InterlockedExchange, object, desired)
336 #  define atomic_exchange_explicit(object,desired,order) \
337     atomic_exchange(object, desired)
338
339 #  define atomic_compare_exchange_strong(object,expected,desired) \
340     atomic_type_dispatch_16_64(InterlockedCompareExchange, object, expected, desired) == expected
341 #  define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \
342     atomic_compare_exchange_strong(object, expected, desired)
343 #  define atomic_compare_exchange_weak(object,expected,desired) \
344     atomic_compare_exchange_strong(object, expected, desired)
345 #  define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \
346     atomic_compare_exchange_weak(object, expected, desired)
347
348 #  define atomic_fetch_add(object,operand) \
349     atomic_type_dispatch_32_64(InterlockedExchangeAdd, object, operand)
350 #  define atomic_fetch_add_explicit(object,operand,order) \
351     atomic_fetch_add(object, operand)
352
353 #  define atomic_fetch_sub(object,operand) \
354     atomic_type_dispatch_32_64(InterlockedExchangeAdd, object, -operand)
355 #  define atomic_fetch_sub_explicit(object,operand,order) \
356     atomic_fetch_sub(object, operand)
357
358 #  define atomic_fetch_or(object,operand) \
359     atomic_type_dispatch_8_64(InterlockedOr, object, operand)
360 #  define atomic_fetch_or_explicit(object,operand,order) \
361     atomic_fetch_or(object, operand)
362
363 #  define atomic_fetch_xor(object,operand) \
364     atomic_type_dispatch_8_64(InterlockedXor, object, operand)
365 #  define atomic_fetch_xor_explicit(object,operand,order) \
366     atomic_fetch_sub(object, operand)
367
368 #  define atomic_fetch_and(object,operand) \
369     atomic_type_dispatch_8_64(InterlockedAnd, object, operand)
370 #  define atomic_fetch_and_explicit(object,operand,order) \
371     atomic_fetch_and(object, operand)
372
373 #  define atomic_flag_test_and_set(object) \
374     atomic_exchange(object, true)
375
376 #  define atomic_flag_test_and_set_explicit(object,order) \
377     atomic_flag_test_and_set(object)
378
379 #  define atomic_flag_clear(object) \
380     atomic_store(object, false)
381
382 #  define atomic_flag_clear_explicit(object,order) \
383     atomic_flag_clear(object)
384
385 # else
386 #  error FIXME: implement atomic operations for this compiler.
387 # endif
388 # endif
389
390 /**
391  * Memory storage space for an atom. Never access it directly.
392  */
393 typedef union
394 {
395     atomic_uintptr_t u;
396 } vlc_atomic_t;
397
398 /** Static initializer for \ref vlc_atomic_t */
399 # define VLC_ATOMIC_INIT(val) { (val) }
400
401 /* All functions return the atom value _after_ the operation. */
402 static inline uintptr_t vlc_atomic_get(vlc_atomic_t *atom)
403 {
404     return atomic_load(&atom->u);
405 }
406
407 static inline uintptr_t vlc_atomic_set(vlc_atomic_t *atom, uintptr_t v)
408 {
409     atomic_store(&atom->u, v);
410     return v;
411 }
412
413 static inline uintptr_t vlc_atomic_add(vlc_atomic_t *atom, uintptr_t v)
414 {
415     return atomic_fetch_add(&atom->u, v) + v;
416 }
417
418 static inline uintptr_t vlc_atomic_sub (vlc_atomic_t *atom, uintptr_t v)
419 {
420     return atomic_fetch_sub (&atom->u, v) - v;
421 }
422
423 static inline uintptr_t vlc_atomic_inc (vlc_atomic_t *atom)
424 {
425     return vlc_atomic_add (atom, 1);
426 }
427
428 static inline uintptr_t vlc_atomic_dec (vlc_atomic_t *atom)
429 {
430     return vlc_atomic_sub (atom, 1);
431 }
432
433 static inline uintptr_t vlc_atomic_swap(vlc_atomic_t *atom, uintptr_t v)
434 {
435     return atomic_exchange(&atom->u, v);
436 }
437
438 static inline uintptr_t vlc_atomic_compare_swap(vlc_atomic_t *atom,
439                                                 uintptr_t u, uintptr_t v)
440 {
441     atomic_compare_exchange_strong(&atom->u, &u, v);
442     return u;
443 }
444
445 typedef atomic_uint_least32_t vlc_atomic_float;
446
447 /** Helper to retrieve a single precision from an atom. */
448 static inline float vlc_atomic_loadf(vlc_atomic_float *atom)
449 {
450     union { float f; uint32_t i; } u;
451     u.i = atomic_load(atom);
452     return u.f;
453 }
454
455 /** Helper to store a single precision into an atom. */
456 static inline void vlc_atomic_storef(vlc_atomic_float *atom, float f)
457 {
458     union { float f; uint32_t i; } u;
459     u.f = f;
460     atomic_store(atom, u.i);
461 }
462
463 #endif