SDL_atomic.c (7427B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 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/* 39 If any of the operations are not provided then we must emulate some 40 of them. That means we need a nice implementation of spin locks 41 that avoids the "one big lock" problem. We use a vector of spin 42 locks and pick which one to use based on the address of the operand 43 of the function. 44 45 To generate the index of the lock we first shift by 3 bits to get 46 rid on the zero bits that result from 32 and 64 bit allignment of 47 data. We then mask off all but 5 bits and use those 5 bits as an 48 index into the table. 49 50 Picking the lock this way insures that accesses to the same data at 51 the same time will go to the same lock. OTOH, accesses to different 52 data have only a 1/32 chance of hitting the same lock. That should 53 pretty much eliminate the chances of several atomic operations on 54 different data from waiting on the same "big lock". If it isn't 55 then the table of locks can be expanded to a new size so long as 56 the new size is a power of two. 57 58 Contributed by Bob Pendleton, bob@pendleton.com 59*/ 60 61#if !defined(HAVE_MSC_ATOMICS) && !defined(HAVE_GCC_ATOMICS) && !defined(__MACOSX__) && !defined(__SOLARIS__) 62#define EMULATE_CAS 1 63#endif 64 65#if EMULATE_CAS 66static SDL_SpinLock locks[32]; 67 68static SDL_INLINE void 69enterLock(void *a) 70{ 71 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f); 72 73 SDL_AtomicLock(&locks[index]); 74} 75 76static SDL_INLINE void 77leaveLock(void *a) 78{ 79 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f); 80 81 SDL_AtomicUnlock(&locks[index]); 82} 83#endif 84 85 86SDL_bool 87SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval) 88{ 89#ifdef HAVE_MSC_ATOMICS 90 return (_InterlockedCompareExchange((long*)&a->value, (long)newval, (long)oldval) == (long)oldval); 91#elif defined(__MACOSX__) /* !!! FIXME: should we favor gcc atomics? */ 92 return (SDL_bool) OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value); 93#elif defined(HAVE_GCC_ATOMICS) 94 return (SDL_bool) __sync_bool_compare_and_swap(&a->value, oldval, newval); 95#elif defined(__SOLARIS__) && defined(_LP64) 96 return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)&a->value, (uint64_t)oldval, (uint64_t)newval) == oldval); 97#elif defined(__SOLARIS__) && !defined(_LP64) 98 return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)&a->value, (uint32_t)oldval, (uint32_t)newval) == oldval); 99#elif EMULATE_CAS 100 SDL_bool retval = SDL_FALSE; 101 102 enterLock(a); 103 if (a->value == oldval) { 104 a->value = newval; 105 retval = SDL_TRUE; 106 } 107 leaveLock(a); 108 109 return retval; 110#else 111 #error Please define your platform. 112#endif 113} 114 115SDL_bool 116SDL_AtomicCASPtr(void **a, void *oldval, void *newval) 117{ 118#if defined(HAVE_MSC_ATOMICS) && (_M_IX86) 119 return (_InterlockedCompareExchange((long*)a, (long)newval, (long)oldval) == (long)oldval); 120#elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86) 121 return (_InterlockedCompareExchangePointer(a, newval, oldval) == oldval); 122#elif defined(__MACOSX__) && defined(__LP64__) /* !!! FIXME: should we favor gcc atomics? */ 123 return (SDL_bool) OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t*) a); 124#elif defined(__MACOSX__) && !defined(__LP64__) /* !!! FIXME: should we favor gcc atomics? */ 125 return (SDL_bool) OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*) a); 126#elif defined(HAVE_GCC_ATOMICS) 127 return __sync_bool_compare_and_swap(a, oldval, newval); 128#elif defined(__SOLARIS__) 129 return (SDL_bool) (atomic_cas_ptr(a, oldval, newval) == oldval); 130#elif EMULATE_CAS 131 SDL_bool retval = SDL_FALSE; 132 133 enterLock(a); 134 if (*a == oldval) { 135 *a = newval; 136 retval = SDL_TRUE; 137 } 138 leaveLock(a); 139 140 return retval; 141#else 142 #error Please define your platform. 143#endif 144} 145 146int 147SDL_AtomicSet(SDL_atomic_t *a, int v) 148{ 149#ifdef HAVE_MSC_ATOMICS 150 return _InterlockedExchange((long*)&a->value, v); 151#elif defined(HAVE_GCC_ATOMICS) 152 return __sync_lock_test_and_set(&a->value, v); 153#elif defined(__SOLARIS__) && defined(_LP64) 154 return (int) atomic_swap_64((volatile uint64_t*)&a->value, (uint64_t)v); 155#elif defined(__SOLARIS__) && !defined(_LP64) 156 return (int) atomic_swap_32((volatile uint32_t*)&a->value, (uint32_t)v); 157#else 158 int value; 159 do { 160 value = a->value; 161 } while (!SDL_AtomicCAS(a, value, v)); 162 return value; 163#endif 164} 165 166void* 167SDL_AtomicSetPtr(void **a, void *v) 168{ 169#if defined(HAVE_MSC_ATOMICS) && (_M_IX86) 170 return (void *) _InterlockedExchange((long *)a, (long) v); 171#elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86) 172 return _InterlockedExchangePointer(a, v); 173#elif defined(HAVE_GCC_ATOMICS) 174 return __sync_lock_test_and_set(a, v); 175#elif defined(__SOLARIS__) 176 return atomic_swap_ptr(a, v); 177#else 178 void *value; 179 do { 180 value = *a; 181 } while (!SDL_AtomicCASPtr(a, value, v)); 182 return value; 183#endif 184} 185 186int 187SDL_AtomicAdd(SDL_atomic_t *a, int v) 188{ 189#ifdef HAVE_MSC_ATOMICS 190 return _InterlockedExchangeAdd((long*)&a->value, v); 191#elif defined(HAVE_GCC_ATOMICS) 192 return __sync_fetch_and_add(&a->value, v); 193#elif defined(__SOLARIS__) 194 int pv = a->value; 195 membar_consumer(); 196#if defined(_LP64) 197 atomic_add_64((volatile uint64_t*)&a->value, v); 198#elif !defined(_LP64) 199 atomic_add_32((volatile uint32_t*)&a->value, v); 200#endif 201 return pv; 202#else 203 int value; 204 do { 205 value = a->value; 206 } while (!SDL_AtomicCAS(a, value, (value + v))); 207 return value; 208#endif 209} 210 211int 212SDL_AtomicGet(SDL_atomic_t *a) 213{ 214 int value; 215 do { 216 value = a->value; 217 } while (!SDL_AtomicCAS(a, value, value)); 218 return value; 219} 220 221void * 222SDL_AtomicGetPtr(void **a) 223{ 224 void *value; 225 do { 226 value = *a; 227 } while (!SDL_AtomicCASPtr(a, value, value)); 228 return value; 229} 230 231#ifdef __thumb__ 232#if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) 233__asm__( 234" .align 2\n" 235" .globl _SDL_MemoryBarrierRelease\n" 236" .globl _SDL_MemoryBarrierAcquire\n" 237"_SDL_MemoryBarrierRelease:\n" 238"_SDL_MemoryBarrierAcquire:\n" 239" mov r0, #0\n" 240" mcr p15, 0, r0, c7, c10, 5\n" 241" bx lr\n" 242); 243#endif 244#endif 245 246/* vi: set ts=4 sw=4 expandtab: */