SDL 2.0
SDL_atomic.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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#include "../SDL_internal.h"
22
23#include "SDL_atomic.h"
24
25#if defined(_MSC_VER) && (_MSC_VER >= 1500)
26#include <intrin.h>
27#define HAVE_MSC_ATOMICS 1
28#endif
29
30#if defined(__MACOSX__) /* !!! FIXME: should we favor gcc atomics? */
31#include <libkern/OSAtomic.h>
32#endif
33
34#if !defined(HAVE_GCC_ATOMICS) && defined(__SOLARIS__)
35#include <atomic.h>
36#endif
37
38/* The __atomic_load_n() intrinsic showed up in different times for different compilers. */
39#if defined(HAVE_GCC_ATOMICS)
40# if defined(__clang__)
41# if __has_builtin(__atomic_load_n)
42 /* !!! FIXME: this advertises as available in the NDK but uses an external symbol we don't have.
43 It might be in a later NDK or we might need an extra library? --ryan. */
44# if !defined(__ANDROID__)
45# define HAVE_ATOMIC_LOAD_N 1
46# endif
47# endif
48# elif defined(__GNUC__)
49# if (__GNUC__ >= 5)
50# define HAVE_ATOMIC_LOAD_N 1
51# endif
52# endif
53#endif
54
55#if defined(__WATCOMC__) && defined(__386__)
56SDL_COMPILE_TIME_ASSERT(intsize, 4==sizeof(int));
57#define HAVE_WATCOM_ATOMICS
58extern _inline int _SDL_xchg_watcom(volatile int *a, int v);
59#pragma aux _SDL_xchg_watcom = \
60 "lock xchg [ecx], eax" \
61 parm [ecx] [eax] \
62 value [eax] \
63 modify exact [eax];
64
65extern _inline unsigned char _SDL_cmpxchg_watcom(volatile int *a, int newval, int oldval);
66#pragma aux _SDL_cmpxchg_watcom = \
67 "lock cmpxchg [edx], ecx" \
68 "setz al" \
69 parm [edx] [ecx] [eax] \
70 value [al] \
71 modify exact [eax];
72
73extern _inline int _SDL_xadd_watcom(volatile int *a, int v);
74#pragma aux _SDL_xadd_watcom = \
75 "lock xadd [ecx], eax" \
76 parm [ecx] [eax] \
77 value [eax] \
78 modify exact [eax];
79#endif /* __WATCOMC__ && __386__ */
80
81/*
82 If any of the operations are not provided then we must emulate some
83 of them. That means we need a nice implementation of spin locks
84 that avoids the "one big lock" problem. We use a vector of spin
85 locks and pick which one to use based on the address of the operand
86 of the function.
87
88 To generate the index of the lock we first shift by 3 bits to get
89 rid on the zero bits that result from 32 and 64 bit allignment of
90 data. We then mask off all but 5 bits and use those 5 bits as an
91 index into the table.
92
93 Picking the lock this way insures that accesses to the same data at
94 the same time will go to the same lock. OTOH, accesses to different
95 data have only a 1/32 chance of hitting the same lock. That should
96 pretty much eliminate the chances of several atomic operations on
97 different data from waiting on the same "big lock". If it isn't
98 then the table of locks can be expanded to a new size so long as
99 the new size is a power of two.
100
101 Contributed by Bob Pendleton, bob@pendleton.com
102*/
103
104#if !defined(HAVE_MSC_ATOMICS) && !defined(HAVE_GCC_ATOMICS) && !defined(__MACOSX__) && !defined(__SOLARIS__) && !defined(HAVE_WATCOM_ATOMICS)
105#define EMULATE_CAS 1
106#endif
107
108#if EMULATE_CAS
110
111static SDL_INLINE void
113{
114 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
115
117}
118
119static SDL_INLINE void
121{
122 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
123
125}
126#endif
127
128
130SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval)
131{
132#ifdef HAVE_MSC_ATOMICS
133 return (_InterlockedCompareExchange((long*)&a->value, (long)newval, (long)oldval) == (long)oldval);
134#elif defined(HAVE_WATCOM_ATOMICS)
135 return (SDL_bool) _SDL_cmpxchg_watcom(&a->value, newval, oldval);
136#elif defined(HAVE_GCC_ATOMICS)
137 return (SDL_bool) __sync_bool_compare_and_swap(&a->value, oldval, newval);
138#elif defined(__MACOSX__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
139 return (SDL_bool) OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);
140#elif defined(__SOLARIS__) && defined(_LP64)
141 return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)&a->value, (uint64_t)oldval, (uint64_t)newval) == oldval);
142#elif defined(__SOLARIS__) && !defined(_LP64)
143 return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)&a->value, (uint32_t)oldval, (uint32_t)newval) == oldval);
144#elif EMULATE_CAS
146
147 enterLock(a);
148 if (a->value == oldval) {
149 a->value = newval;
151 }
152 leaveLock(a);
153
154 return retval;
155#else
156 #error Please define your platform.
157#endif
158}
159
161SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
162{
163#if defined(HAVE_MSC_ATOMICS) && (_M_IX86)
164 return (_InterlockedCompareExchange((long*)a, (long)newval, (long)oldval) == (long)oldval);
165#elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86)
166 return (_InterlockedCompareExchangePointer(a, newval, oldval) == oldval);
167#elif defined(HAVE_WATCOM_ATOMICS)
168 return (SDL_bool) _SDL_cmpxchg_watcom((int *)a, (long)newval, (long)oldval);
169#elif defined(HAVE_GCC_ATOMICS)
170 return __sync_bool_compare_and_swap(a, oldval, newval);
171#elif defined(__MACOSX__) && defined(__LP64__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
172 return (SDL_bool) OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t*) a);
173#elif defined(__MACOSX__) && !defined(__LP64__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
174 return (SDL_bool) OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*) a);
175#elif defined(__SOLARIS__)
176 return (SDL_bool) (atomic_cas_ptr(a, oldval, newval) == oldval);
177#elif EMULATE_CAS
179
180 enterLock(a);
181 if (*a == oldval) {
182 *a = newval;
184 }
185 leaveLock(a);
186
187 return retval;
188#else
189 #error Please define your platform.
190#endif
191}
192
193int
195{
196#ifdef HAVE_MSC_ATOMICS
197 return _InterlockedExchange((long*)&a->value, v);
198#elif defined(HAVE_WATCOM_ATOMICS)
199 return _SDL_xchg_watcom(&a->value, v);
200#elif defined(HAVE_GCC_ATOMICS)
201 return __sync_lock_test_and_set(&a->value, v);
202#elif defined(__SOLARIS__) && defined(_LP64)
203 return (int) atomic_swap_64((volatile uint64_t*)&a->value, (uint64_t)v);
204#elif defined(__SOLARIS__) && !defined(_LP64)
205 return (int) atomic_swap_32((volatile uint32_t*)&a->value, (uint32_t)v);
206#else
207 int value;
208 do {
209 value = a->value;
210 } while (!SDL_AtomicCAS(a, value, v));
211 return value;
212#endif
213}
214
215void*
216SDL_AtomicSetPtr(void **a, void *v)
217{
218#if defined(HAVE_MSC_ATOMICS) && (_M_IX86)
219 return (void *) _InterlockedExchange((long *)a, (long) v);
220#elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86)
221 return _InterlockedExchangePointer(a, v);
222#elif defined(HAVE_WATCOM_ATOMICS)
223 return (void *) _SDL_xchg_watcom((int *)a, (long)v);
224#elif defined(HAVE_GCC_ATOMICS)
225 return __sync_lock_test_and_set(a, v);
226#elif defined(__SOLARIS__)
227 return atomic_swap_ptr(a, v);
228#else
229 void *value;
230 do {
231 value = *a;
232 } while (!SDL_AtomicCASPtr(a, value, v));
233 return value;
234#endif
235}
236
237int
239{
240#ifdef HAVE_MSC_ATOMICS
241 return _InterlockedExchangeAdd((long*)&a->value, v);
242#elif defined(HAVE_WATCOM_ATOMICS)
243 return _SDL_xadd_watcom(&a->value, v);
244#elif defined(HAVE_GCC_ATOMICS)
245 return __sync_fetch_and_add(&a->value, v);
246#elif defined(__SOLARIS__)
247 int pv = a->value;
248 membar_consumer();
249#if defined(_LP64)
250 atomic_add_64((volatile uint64_t*)&a->value, v);
251#elif !defined(_LP64)
252 atomic_add_32((volatile uint32_t*)&a->value, v);
253#endif
254 return pv;
255#else
256 int value;
257 do {
258 value = a->value;
259 } while (!SDL_AtomicCAS(a, value, (value + v)));
260 return value;
261#endif
262}
263
264int
266{
267#ifdef HAVE_ATOMIC_LOAD_N
268 return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
269#else
270 int value;
271 do {
272 value = a->value;
273 } while (!SDL_AtomicCAS(a, value, value));
274 return value;
275#endif
276}
277
278void *
280{
281#ifdef HAVE_ATOMIC_LOAD_N
282 return __atomic_load_n(a, __ATOMIC_SEQ_CST);
283#else
284 void *value;
285 do {
286 value = *a;
287 } while (!SDL_AtomicCASPtr(a, value, value));
288 return value;
289#endif
290}
291
292#ifdef SDL_MEMORY_BARRIER_USES_FUNCTION
293#error This file should be built in arm mode so the mcr instruction is available for memory barriers
294#endif
295
296void
298{
300}
301
302void
304{
306}
307
308/* vi: set ts=4 sw=4 expandtab: */
SDL_bool SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval)
Set an atomic variable to a new value if it is currently an old value.
Definition: SDL_atomic.c:130
void SDL_MemoryBarrierAcquireFunction(void)
Definition: SDL_atomic.c:303
SDL_bool SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
Set a pointer to a new value if it is currently an old value.
Definition: SDL_atomic.c:161
void * SDL_AtomicGetPtr(void **a)
Get the value of a pointer atomically.
Definition: SDL_atomic.c:279
static SDL_INLINE void leaveLock(void *a)
Definition: SDL_atomic.c:120
void SDL_MemoryBarrierReleaseFunction(void)
Definition: SDL_atomic.c:297
int SDL_AtomicGet(SDL_atomic_t *a)
Get the value of an atomic variable.
Definition: SDL_atomic.c:265
int SDL_AtomicAdd(SDL_atomic_t *a, int v)
Add to an atomic variable.
Definition: SDL_atomic.c:238
void * SDL_AtomicSetPtr(void **a, void *v)
Set a pointer to a value atomically.
Definition: SDL_atomic.c:216
static SDL_INLINE void enterLock(void *a)
Definition: SDL_atomic.c:112
int SDL_AtomicSet(SDL_atomic_t *a, int v)
Set an atomic variable to a value.
Definition: SDL_atomic.c:194
static SDL_SpinLock locks[32]
Definition: SDL_atomic.c:109
#define SDL_MemoryBarrierRelease()
Definition: SDL_atomic.h:207
int SDL_SpinLock
Definition: SDL_atomic.h:89
#define SDL_MemoryBarrierAcquire()
Definition: SDL_atomic.h:208
unsigned int uint32_t
unsigned int uintptr_t
unsigned long long uint64_t
signed int int32_t
signed long long int64_t
#define SDL_AtomicLock
#define SDL_AtomicUnlock
const GLdouble * v
Definition: SDL_opengl.h:2064
GLuint index
GLboolean GLboolean GLboolean GLboolean a
GLsizei const GLfloat * value
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
#define SDL_COMPILE_TIME_ASSERT(name, x)
Definition: SDL_stdinc.h:312
#define SDL_INLINE
Definition: begin_code.h:134
A type representing an atomic integer value. It is a struct so people don't accidentally use numeric ...
Definition: SDL_atomic.h:216
SDL_bool retval