]>
Commit | Line | Data |
---|---|---|
7407ac7f BB |
1 | /* |
2 | Simple DirectMedia Layer | |
3 | Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org> | |
4 | ||
5 | This software is provided 'as-is', without any express or implied | |
6 | warranty. In no event will the authors be held liable for any damages | |
7 | arising from the use of this software. | |
8 | ||
9 | Permission is granted to anyone to use this software for any purpose, | |
10 | including commercial applications, and to alter it and redistribute it | |
11 | freely, subject to the following restrictions: | |
12 | ||
13 | 1. The origin of this software must not be misrepresented; you must not | |
14 | claim that you wrote the original software. If you use this software | |
15 | in a product, an acknowledgment in the product documentation would be | |
16 | appreciated but is not required. | |
17 | 2. Altered source versions must be plainly marked as such, and must not be | |
18 | misrepresented as being the original software. | |
19 | 3. This notice may not be removed or altered from any source distribution. | |
20 | */ | |
21 | ||
22 | /** | |
23 | * \file SDL_atomic.h | |
24 | * | |
25 | * Atomic operations. | |
26 | * | |
27 | * IMPORTANT: | |
28 | * If you are not an expert in concurrent lockless programming, you should | |
29 | * only be using the atomic lock and reference counting functions in this | |
30 | * file. In all other cases you should be protecting your data structures | |
31 | * with full mutexes. | |
32 | * | |
33 | * The list of "safe" functions to use are: | |
34 | * SDL_AtomicLock() | |
35 | * SDL_AtomicUnlock() | |
36 | * SDL_AtomicIncRef() | |
37 | * SDL_AtomicDecRef() | |
38 | * | |
39 | * Seriously, here be dragons! | |
40 | * ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
41 | * | |
42 | * You can find out a little more about lockless programming and the | |
43 | * subtle issues that can arise here: | |
44 | * http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx | |
45 | * | |
46 | * There's also lots of good information here: | |
47 | * http://www.1024cores.net/home/lock-free-algorithms | |
48 | * http://preshing.com/ | |
49 | * | |
50 | * These operations may or may not actually be implemented using | |
51 | * processor specific atomic operations. When possible they are | |
52 | * implemented as true processor specific atomic operations. When that | |
53 | * is not possible the are implemented using locks that *do* use the | |
54 | * available atomic operations. | |
55 | * | |
56 | * All of the atomic operations that modify memory are full memory barriers. | |
57 | */ | |
58 | ||
59 | #ifndef _SDL_atomic_h_ | |
60 | #define _SDL_atomic_h_ | |
61 | ||
62 | #include "SDL_stdinc.h" | |
63 | #include "SDL_platform.h" | |
64 | ||
65 | #include "begin_code.h" | |
66 | ||
67 | /* Need to do this here because intrin.h has C++ code in it */ | |
68 | /* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */ | |
69 | #if defined(_MSC_VER) && (_MSC_VER >= 1500) | |
70 | #include <intrin.h> | |
71 | #define HAVE_MSC_ATOMICS 1 | |
72 | #endif | |
73 | ||
74 | /* Set up for C function definitions, even when using C++ */ | |
75 | #ifdef __cplusplus | |
76 | extern "C" { | |
77 | #endif | |
78 | ||
79 | /** | |
80 | * \name SDL AtomicLock | |
81 | * | |
82 | * The atomic locks are efficient spinlocks using CPU instructions, | |
83 | * but are vulnerable to starvation and can spin forever if a thread | |
84 | * holding a lock has been terminated. For this reason you should | |
85 | * minimize the code executed inside an atomic lock and never do | |
86 | * expensive things like API or system calls while holding them. | |
87 | * | |
88 | * The atomic locks are not safe to lock recursively. | |
89 | * | |
90 | * Porting Note: | |
91 | * The spin lock functions and type are required and can not be | |
92 | * emulated because they are used in the atomic emulation code. | |
93 | */ | |
94 | /*@{*/ | |
95 | ||
96 | typedef int SDL_SpinLock; | |
97 | ||
98 | /** | |
99 | * \brief Try to lock a spin lock by setting it to a non-zero value. | |
100 | * | |
101 | * \param lock Points to the lock. | |
102 | * | |
103 | * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held. | |
104 | */ | |
105 | extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock); | |
106 | ||
107 | /** | |
108 | * \brief Lock a spin lock by setting it to a non-zero value. | |
109 | * | |
110 | * \param lock Points to the lock. | |
111 | */ | |
112 | extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock); | |
113 | ||
114 | /** | |
115 | * \brief Unlock a spin lock by setting it to 0. Always returns immediately | |
116 | * | |
117 | * \param lock Points to the lock. | |
118 | */ | |
119 | extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock); | |
120 | ||
121 | /*@}*//*SDL AtomicLock*/ | |
122 | ||
123 | ||
124 | /** | |
125 | * The compiler barrier prevents the compiler from reordering | |
126 | * reads and writes to globally visible variables across the call. | |
127 | */ | |
128 | #if defined(_MSC_VER) && (_MSC_VER > 1200) | |
129 | void _ReadWriteBarrier(void); | |
130 | #pragma intrinsic(_ReadWriteBarrier) | |
131 | #define SDL_CompilerBarrier() _ReadWriteBarrier() | |
132 | #elif defined(__GNUC__) | |
133 | #define SDL_CompilerBarrier() __asm__ __volatile__ ("" : : : "memory") | |
134 | #else | |
135 | #define SDL_CompilerBarrier() \ | |
136 | { SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); } | |
137 | #endif | |
138 | ||
139 | /** | |
140 | * Memory barriers are designed to prevent reads and writes from being | |
141 | * reordered by the compiler and being seen out of order on multi-core CPUs. | |
142 | * | |
143 | * A typical pattern would be for thread A to write some data and a flag, | |
144 | * and for thread B to read the flag and get the data. In this case you | |
145 | * would insert a release barrier between writing the data and the flag, | |
146 | * guaranteeing that the data write completes no later than the flag is | |
147 | * written, and you would insert an acquire barrier between reading the | |
148 | * flag and reading the data, to ensure that all the reads associated | |
149 | * with the flag have completed. | |
150 | * | |
151 | * In this pattern you should always see a release barrier paired with | |
152 | * an acquire barrier and you should gate the data reads/writes with a | |
153 | * single flag variable. | |
154 | * | |
155 | * For more information on these semantics, take a look at the blog post: | |
156 | * http://preshing.com/20120913/acquire-and-release-semantics | |
157 | */ | |
158 | #if defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) | |
159 | #define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("lwsync" : : : "memory") | |
160 | #define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("lwsync" : : : "memory") | |
161 | #elif defined(__GNUC__) && defined(__arm__) | |
162 | #if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) | |
163 | #define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("dmb ish" : : : "memory") | |
164 | #define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("dmb ish" : : : "memory") | |
165 | #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) | |
166 | #ifdef __thumb__ | |
167 | /* The mcr instruction isn't available in thumb mode, use real functions */ | |
168 | extern DECLSPEC void SDLCALL SDL_MemoryBarrierRelease(); | |
169 | extern DECLSPEC void SDLCALL SDL_MemoryBarrierAcquire(); | |
170 | #else | |
171 | #define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r"(0) : "memory") | |
172 | #define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r"(0) : "memory") | |
173 | #endif /* __thumb__ */ | |
174 | #else | |
175 | #define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("" : : : "memory") | |
176 | #define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("" : : : "memory") | |
177 | #endif /* __GNUC__ && __arm__ */ | |
178 | #else | |
179 | /* This is correct for the x86 and x64 CPUs, and we'll expand this over time. */ | |
180 | #define SDL_MemoryBarrierRelease() SDL_CompilerBarrier() | |
181 | #define SDL_MemoryBarrierAcquire() SDL_CompilerBarrier() | |
182 | #endif | |
183 | ||
184 | ||
185 | /* Platform specific optimized versions of the atomic functions, | |
186 | * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE | |
187 | */ | |
188 | #if defined(SDL_ATOMIC_DISABLED) && SDL_ATOMIC_DISABLED | |
189 | #define SDL_DISABLE_ATOMIC_INLINE | |
190 | #endif | |
191 | #ifndef SDL_DISABLE_ATOMIC_INLINE | |
192 | ||
193 | #ifdef HAVE_MSC_ATOMICS | |
194 | ||
195 | #define SDL_AtomicSet(a, v) _InterlockedExchange((long*)&(a)->value, (v)) | |
196 | #define SDL_AtomicAdd(a, v) _InterlockedExchangeAdd((long*)&(a)->value, (v)) | |
197 | #define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval)) | |
198 | #define SDL_AtomicSetPtr(a, v) _InterlockedExchangePointer((a), (v)) | |
199 | #if _M_IX86 | |
200 | #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval)) | |
201 | #else | |
202 | #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval)) | |
203 | #endif | |
204 | ||
205 | #elif defined(__MACOSX__) | |
206 | #include <libkern/OSAtomic.h> | |
207 | ||
208 | #define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value) | |
209 | #ifdef __LP64__ | |
210 | #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a)) | |
211 | #else | |
212 | #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a)) | |
213 | #endif | |
214 | ||
215 | #elif defined(HAVE_GCC_ATOMICS) | |
216 | ||
217 | #define SDL_AtomicSet(a, v) __sync_lock_test_and_set(&(a)->value, v) | |
218 | #define SDL_AtomicAdd(a, v) __sync_fetch_and_add(&(a)->value, v) | |
219 | #define SDL_AtomicSetPtr(a, v) __sync_lock_test_and_set(a, v) | |
220 | #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval) | |
221 | #define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval) | |
222 | ||
223 | #endif | |
224 | ||
225 | #endif /* !SDL_DISABLE_ATOMIC_INLINE */ | |
226 | ||
227 | ||
228 | /** | |
229 | * \brief A type representing an atomic integer value. It is a struct | |
230 | * so people don't accidentally use numeric operations on it. | |
231 | */ | |
232 | #ifndef SDL_atomic_t_defined | |
233 | typedef struct { int value; } SDL_atomic_t; | |
234 | #endif | |
235 | ||
236 | /** | |
237 | * \brief Set an atomic variable to a new value if it is currently an old value. | |
238 | * | |
239 | * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise. | |
240 | * | |
241 | * \note If you don't know what this function is for, you shouldn't use it! | |
242 | */ | |
243 | #ifndef SDL_AtomicCAS | |
244 | extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval); | |
245 | #endif | |
246 | ||
247 | /** | |
248 | * \brief Set an atomic variable to a value. | |
249 | * | |
250 | * \return The previous value of the atomic variable. | |
251 | */ | |
252 | #ifndef SDL_AtomicSet | |
253 | SDL_FORCE_INLINE int SDL_AtomicSet(SDL_atomic_t *a, int v) | |
254 | { | |
255 | int value; | |
256 | do { | |
257 | value = a->value; | |
258 | } while (!SDL_AtomicCAS(a, value, v)); | |
259 | return value; | |
260 | } | |
261 | #endif | |
262 | ||
263 | /** | |
264 | * \brief Get the value of an atomic variable | |
265 | */ | |
266 | #ifndef SDL_AtomicGet | |
267 | SDL_FORCE_INLINE int SDL_AtomicGet(SDL_atomic_t *a) | |
268 | { | |
269 | int value = a->value; | |
270 | SDL_CompilerBarrier(); | |
271 | return value; | |
272 | } | |
273 | #endif | |
274 | ||
275 | /** | |
276 | * \brief Add to an atomic variable. | |
277 | * | |
278 | * \return The previous value of the atomic variable. | |
279 | * | |
280 | * \note This same style can be used for any number operation | |
281 | */ | |
282 | #ifndef SDL_AtomicAdd | |
283 | SDL_FORCE_INLINE int SDL_AtomicAdd(SDL_atomic_t *a, int v) | |
284 | { | |
285 | int value; | |
286 | do { | |
287 | value = a->value; | |
288 | } while (!SDL_AtomicCAS(a, value, (value + v))); | |
289 | return value; | |
290 | } | |
291 | #endif | |
292 | ||
293 | /** | |
294 | * \brief Increment an atomic variable used as a reference count. | |
295 | */ | |
296 | #ifndef SDL_AtomicIncRef | |
297 | #define SDL_AtomicIncRef(a) SDL_AtomicAdd(a, 1) | |
298 | #endif | |
299 | ||
300 | /** | |
301 | * \brief Decrement an atomic variable used as a reference count. | |
302 | * | |
303 | * \return SDL_TRUE if the variable reached zero after decrementing, | |
304 | * SDL_FALSE otherwise | |
305 | */ | |
306 | #ifndef SDL_AtomicDecRef | |
307 | #define SDL_AtomicDecRef(a) (SDL_AtomicAdd(a, -1) == 1) | |
308 | #endif | |
309 | ||
310 | /** | |
311 | * \brief Set a pointer to a new value if it is currently an old value. | |
312 | * | |
313 | * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise. | |
314 | * | |
315 | * \note If you don't know what this function is for, you shouldn't use it! | |
316 | */ | |
317 | #ifndef SDL_AtomicCASPtr | |
318 | extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void* *a, void *oldval, void *newval); | |
319 | #endif | |
320 | ||
321 | /** | |
322 | * \brief Set a pointer to a value atomically. | |
323 | * | |
324 | * \return The previous value of the pointer. | |
325 | */ | |
326 | #ifndef SDL_AtomicSetPtr | |
327 | SDL_FORCE_INLINE void* SDL_AtomicSetPtr(void* *a, void* v) | |
328 | { | |
329 | void* value; | |
330 | do { | |
331 | value = *a; | |
332 | } while (!SDL_AtomicCASPtr(a, value, v)); | |
333 | return value; | |
334 | } | |
335 | #endif | |
336 | ||
337 | /** | |
338 | * \brief Get the value of a pointer atomically. | |
339 | */ | |
340 | #ifndef SDL_AtomicGetPtr | |
341 | SDL_FORCE_INLINE void* SDL_AtomicGetPtr(void* *a) | |
342 | { | |
343 | void* value = *a; | |
344 | SDL_CompilerBarrier(); | |
345 | return value; | |
346 | } | |
347 | #endif | |
348 | ||
349 | ||
350 | /* Ends C function definitions when using C++ */ | |
351 | #ifdef __cplusplus | |
352 | } | |
353 | #endif | |
354 | ||
355 | #include "close_code.h" | |
356 | ||
357 | #endif /* _SDL_atomic_h_ */ | |
358 | ||
359 | /* vi: set ts=4 sw=4 expandtab: */ |