]> git.sesse.net Git - vlc/blob - include/vlc_atomic.h
Provide C11-like atomics in <vlc_atomic.h>
[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 #  include <stdatomic.h>
32
33 # else /* if (???) */
34
35 #  define ATOMIC_FLAG_INIT false
36
37 #  define ATOMIC_VAR_INIT(value) (value)
38
39 #  define atomic_init(obj, value) \
40     do { *(obj) = (value); } while(0)
41
42 #  define kill_dependency(y) \
43     ((void)0)
44
45 #  define atomic_thread_fence(order) \
46     __sync_synchronize()
47
48 #  define atomic_signal_fence(order) \
49     ((void)0)
50
51 #  define atomic_is_lock_free(obj) \
52     false
53
54 /* In principles, __sync_*() only supports int, long and long long and their
55  * unsigned equivalents, i.e. 4-bytes and 8-bytes types, although GCC also
56  * supports 1 and 2-bytes types. Some non-x86 architectures do not support
57  * 8-byte atomic types (or not efficiently). So lets stick to (u)intptr_t. */
58 typedef  intptr_t          atomic_flag;
59 typedef  intptr_t          atomic_bool;
60 typedef  intptr_t          atomic_char;
61 typedef  intptr_t          atomic_schar;
62 typedef uintptr_t          atomic_uchar;
63 typedef  intptr_t          atomic_short;
64 typedef uintptr_t          atomic_ushort;
65 typedef  intptr_t          atomic_int;
66 typedef uintptr_t          atomic_uint;
67 //typedef   signed long atomic_long;
68 //typedef unsigned long atomic_ulong;
69 //typedef   signed long long atomic_llong;
70 //typedef unsigned long long atomic_ullong;
71 /* ... */
72 typedef  intptr_t          atomic_intptr_t;
73 typedef uintptr_t          atomic_uintptr_t;
74 typedef uintptr_t          atomic_size_t;
75 typedef  intptr_t          atomic_ptrdiff_t;
76 //typedef  intmax_t          atomic_intmax_t;
77 //typedef uintmax_t          atomic_uintmax_t;
78
79 #  define atomic_store(object,desired) \
80     do { \
81         *(object) = (desired); \
82         __sync_synchronize(); \
83     } while (0)
84
85 #  define atomic_store_explicit(object,desired,order) \
86     atomic_store(object,desired)
87
88 #  define atomic_load(object) \
89     (__sync_synchronize(), *(object))
90
91 #  define atomic_load_explicit(object,order) \
92     atomic_load(object)
93
94 static inline
95 intptr_t vlc_atomic_exchange(volatile void *object, intptr_t desired)
96 {
97     volatile intptr_t *ptr = (volatile intptr_t *)object;
98     intptr_t old;
99     /* NOTE: while __sync_lock_test_and_set() is an atomic exchange, its memory
100      * order is too weak (acquire instead of sequentially consistent).
101      * Because of that, for lack of both C11 _Generic() and GNU C compound
102      * statements, atomic exchange needs a helper function.
103      * Thus all atomic types must have the same size. */
104     do
105         old = atomic_load(ptr);
106     while (!__sync_bool_compare_and_swap(ptr, old, desired));
107
108     return old;
109 }
110
111 #  define atomic_exchange(object,desired) \
112     vlc_atomic_exchange(object,desired)
113
114 #  define atomic_exchange_explicit(object,desired,order) \
115     atomic_exchange(object,desired)
116
117 static inline
118 bool vlc_atomic_compare_exchange(volatile void *object, void *expected,
119                                  intptr_t desired)
120 {
121     volatile intptr_t *ptr = (volatile intptr_t *)object;
122     intptr_t old = *(intptr_t *)expected;
123     intptr_t val = __sync_val_compare_and_swap(ptr, old, desired);
124     if (old != val)
125     {
126         *(intptr_t *)expected = val;
127         return false;
128     }
129     return true;
130 }
131
132 #  define atomic_compare_exchange_strong(object,expected,desired) \
133     vlc_atomic_compare_exchange(object, expected, desired)
134
135 #  define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \
136     atomic_compare_exchange_strong(object, expected, desired)
137
138 #  define atomic_compare_exchange_weak(object,expected,desired) \
139     vlc_atomic_compare_exchange(object, expected, desired)
140
141 #  define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \
142     atomic_compare_exchange_weak(object, expected, desired)
143
144 #  define atomic_fetch_add(object,operand) \
145     __sync_fetch_and_add(object, operand)
146
147 #  define atomic_fetch_add_explicit(object,operand,order) \
148     atomic_fetch_add(object,operand)
149
150 #  define atomic_fetch_sub(object,operand) \
151     __sync_fetch_and_sub(object, operand)
152
153 #  define atomic_fetch_sub_explicit(object,operand,order) \
154     atomic_fetch_sub(object,operand)
155
156 #  define atomic_fetch_or(object,operand) \
157     __sync_fetch_and_or(object, operand)
158
159 #  define atomic_fetch_or_explicit(object,operand,order) \
160     atomic_fetch_or(object,operand)
161
162 #  define atomic_fetch_xor(object,operand) \
163     __sync_fetch_and_sub(object, operand)
164
165 #  define atomic_fetch_xor_explicit(object,operand,order) \
166     atomic_fetch_sub(object,operand)
167
168 #  define atomic_fetch_and(object,operand) \
169     __sync_fetch_and_and(object, operand)
170
171 #  define atomic_fetch_and_explicit(object,operand,order) \
172     atomic_fetch_and(object,operand)
173
174 #  define atomic_flag_test_and_set(object) \
175     atomic_exchange(object, true)
176
177 #  define atomic_flag_test_and_set_explicit(object,order) \
178     atomic_flag_test_and_set(object)
179
180 #  define atomic_flag_clear(object) \
181     atomic_store(object, false)
182
183 #  define atomic_flag_clear_explicit(object,order) \
184     atomic_flag_clear(object)
185
186 # endif
187
188 /** Static initializer for \ref vlc_atomic_t */
189 # define VLC_ATOMIC_INIT(val) { (val) }
190
191 /* All functions return the atom value _after_ the operation. */
192
193 VLC_API uintptr_t vlc_atomic_get(const vlc_atomic_t *);
194 VLC_API uintptr_t vlc_atomic_set(vlc_atomic_t *, uintptr_t);
195 VLC_API uintptr_t vlc_atomic_add(vlc_atomic_t *, uintptr_t);
196
197 static inline uintptr_t vlc_atomic_sub (vlc_atomic_t *atom, uintptr_t v)
198 {
199     return vlc_atomic_add (atom, -v);
200 }
201
202 static inline uintptr_t vlc_atomic_inc (vlc_atomic_t *atom)
203 {
204     return vlc_atomic_add (atom, 1);
205 }
206
207 static inline uintptr_t vlc_atomic_dec (vlc_atomic_t *atom)
208 {
209     return vlc_atomic_sub (atom, 1);
210 }
211
212 VLC_API uintptr_t vlc_atomic_swap(vlc_atomic_t *, uintptr_t);
213 VLC_API uintptr_t vlc_atomic_compare_swap(vlc_atomic_t *, uintptr_t, uintptr_t);
214
215 /** Helper to retrieve a single precision from an atom. */
216 static inline float vlc_atomic_getf(const vlc_atomic_t *atom)
217 {
218     union { float f; uintptr_t i; } u;
219     u.i = vlc_atomic_get(atom);
220     return u.f;
221 }
222
223 /** Helper to store a single precision into an atom. */
224 static inline float vlc_atomic_setf(vlc_atomic_t *atom, float f)
225 {
226     union { float f; uintptr_t i; } u;
227     u.f = f;
228     vlc_atomic_set(atom, u.i);
229     return f;
230 }
231
232 #endif