]> git.sesse.net Git - vlc/blob - include/vlc_atomic.h
Rewrite vlc_atomic_* using the new atomic operations
[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 static inline uintptr_t vlc_atomic_get(const vlc_atomic_t *atom)
193 {
194     return atomic_load(&atom->u);
195 }
196
197 static inline uintptr_t vlc_atomic_set(vlc_atomic_t *atom, uintptr_t v)
198 {
199     atomic_store(&atom->u, v);
200     return v;
201 }
202
203 static inline uintptr_t vlc_atomic_add(vlc_atomic_t *atom, uintptr_t v)
204 {
205     return atomic_fetch_add(&atom->u, v) + v;
206 }
207
208 static inline uintptr_t vlc_atomic_sub (vlc_atomic_t *atom, uintptr_t v)
209 {
210     return atomic_fetch_sub (&atom->u, v) - v;
211 }
212
213 static inline uintptr_t vlc_atomic_inc (vlc_atomic_t *atom)
214 {
215     return vlc_atomic_add (atom, 1);
216 }
217
218 static inline uintptr_t vlc_atomic_dec (vlc_atomic_t *atom)
219 {
220     return vlc_atomic_sub (atom, 1);
221 }
222
223 static inline uintptr_t vlc_atomic_swap(vlc_atomic_t *atom, uintptr_t v)
224 {
225     return atomic_exchange(&atom->u, v);
226 }
227
228 static inline uintptr_t vlc_atomic_compare_swap(vlc_atomic_t *atom,
229                                                 uintptr_t u, uintptr_t v)
230 {
231     return atomic_compare_exchange_strong(&atom->u, &u, v);
232 }
233
234 /** Helper to retrieve a single precision from an atom. */
235 static inline float vlc_atomic_getf(const vlc_atomic_t *atom)
236 {
237     union { float f; uintptr_t i; } u;
238     u.i = vlc_atomic_get(atom);
239     return u.f;
240 }
241
242 /** Helper to store a single precision into an atom. */
243 static inline float vlc_atomic_setf(vlc_atomic_t *atom, float f)
244 {
245     union { float f; uintptr_t i; } u;
246     u.f = f;
247     vlc_atomic_set(atom, u.i);
248     return f;
249 }
250
251 #endif