1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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
109static SDL_SpinLock locks[32];
110
111static SDL_INLINE void
112enterLock(void *a)
113{
114 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
115
116 SDL_AtomicLock(&locks[index]);
117}
118
119static SDL_INLINE void
120leaveLock(void *a)
121{
122 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
123
124 SDL_AtomicUnlock(&locks[index]);
125}
126#endif
127
128
129SDL_bool
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
145 SDL_bool retval = SDL_FALSE;
146
147 enterLock(a);
148 if (a->value == oldval) {
149 a->value = newval;
150 retval = SDL_TRUE;
151 }
152 leaveLock(a);
153
154 return retval;
155#else
156 #error Please define your platform.
157#endif
158}
159
160SDL_bool
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
178 SDL_bool retval = SDL_FALSE;
179
180 enterLock(a);
181 if (*a == oldval) {
182 *a = newval;
183 retval = SDL_TRUE;
184 }
185 leaveLock(a);
186
187 return retval;
188#else
189 #error Please define your platform.
190#endif
191}
192
193int
194SDL_AtomicSet(SDL_atomic_t *a, int v)
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
238SDL_AtomicAdd(SDL_atomic_t *a, int v)
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
265SDL_AtomicGet(SDL_atomic_t *a)
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 *
279SDL_AtomicGetPtr(void **a)
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
297SDL_MemoryBarrierReleaseFunction(void)
298{
299 SDL_MemoryBarrierRelease();
300}
301
302void
303SDL_MemoryBarrierAcquireFunction(void)
304{
305 SDL_MemoryBarrierAcquire();
306}
307
308/* vi: set ts=4 sw=4 expandtab: */
309