SDL_atomic.c (9345B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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__) 56 SDL_COMPILE_TIME_ASSERT(intsize, 4==sizeof(int)); 57 #define HAVE_WATCOM_ATOMICS 58 extern _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 65 extern _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 73 extern _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 109 static SDL_SpinLock locks[32]; 110 111 static SDL_INLINE void 112 enterLock(void *a) 113 { 114 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f); 115 116 SDL_AtomicLock(&locks[index]); 117 } 118 119 static SDL_INLINE void 120 leaveLock(void *a) 121 { 122 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f); 123 124 SDL_AtomicUnlock(&locks[index]); 125 } 126 #endif 127 128 129 SDL_bool 130 SDL_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 160 SDL_bool 161 SDL_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 193 int 194 SDL_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 215 void* 216 SDL_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 237 int 238 SDL_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 264 int 265 SDL_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 278 void * 279 SDL_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 296 void 297 SDL_MemoryBarrierReleaseFunction(void) 298 { 299 SDL_MemoryBarrierRelease(); 300 } 301 302 void 303 SDL_MemoryBarrierAcquireFunction(void) 304 { 305 SDL_MemoryBarrierAcquire(); 306 } 307 308 /* vi: set ts=4 sw=4 expandtab: */