Atlas - SDL_atomic.c

Home / ext / SDL / src / atomic Lines: 1 | Size: 11711 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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#if defined(_MSC_VER) && (_MSC_VER >= 1900) 24#include <intrin.h> 25#define HAVE_MSC_ATOMICS 1 26#endif 27 28#ifdef SDL_PLATFORM_MACOS // !!! FIXME: should we favor gcc atomics? 29#include <libkern/OSAtomic.h> 30#endif 31 32#if !defined(HAVE_GCC_ATOMICS) && defined(SDL_PLATFORM_SOLARIS) 33#include <atomic.h> 34#endif 35 36// The __atomic_load_n() intrinsic showed up in different times for different compilers. 37#ifdef __clang__ 38#if __has_builtin(__atomic_load_n) || defined(HAVE_GCC_ATOMICS) 39/* !!! FIXME: this advertises as available in the NDK but uses an external symbol we don't have. 40 It might be in a later NDK or we might need an extra library? --ryan. */ 41#ifndef SDL_PLATFORM_ANDROID 42#define HAVE_ATOMIC_LOAD_N 1 43#endif 44#endif 45#elif defined(__GNUC__) 46#if (__GNUC__ >= 5) 47#define HAVE_ATOMIC_LOAD_N 1 48#endif 49#endif 50 51/* 52 If any of the operations are not provided then we must emulate some 53 of them. That means we need a nice implementation of spin locks 54 that avoids the "one big lock" problem. We use a vector of spin 55 locks and pick which one to use based on the address of the operand 56 of the function. 57 58 To generate the index of the lock we first shift by 3 bits to get 59 rid on the zero bits that result from 32 and 64 bit alignment of 60 data. We then mask off all but 5 bits and use those 5 bits as an 61 index into the table. 62 63 Picking the lock this way insures that accesses to the same data at 64 the same time will go to the same lock. OTOH, accesses to different 65 data have only a 1/32 chance of hitting the same lock. That should 66 pretty much eliminate the chances of several atomic operations on 67 different data from waiting on the same "big lock". If it isn't 68 then the table of locks can be expanded to a new size so long as 69 the new size is a power of two. 70 71 Contributed by Bob Pendleton, [email protected] 72*/ 73 74#if !defined(HAVE_MSC_ATOMICS) && !defined(HAVE_GCC_ATOMICS) && !defined(SDL_PLATFORM_MACOS) && !defined(SDL_PLATFORM_SOLARIS) 75#define EMULATE_CAS 1 76#endif 77 78#ifdef EMULATE_CAS 79static SDL_SpinLock locks[32]; 80 81static SDL_INLINE void enterLock(void *a) 82{ 83 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f); 84 85 SDL_LockSpinlock(&locks[index]); 86} 87 88static SDL_INLINE void leaveLock(void *a) 89{ 90 uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f); 91 92 SDL_UnlockSpinlock(&locks[index]); 93} 94#endif 95 96bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval) 97{ 98#ifdef HAVE_MSC_ATOMICS 99 SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value)); 100 return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval; 101#elif defined(HAVE_GCC_ATOMICS) 102 return __sync_bool_compare_and_swap(&a->value, oldval, newval); 103#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. 104 return OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value); 105#elif defined(SDL_PLATFORM_SOLARIS) 106 SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value)); 107 return ((int)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval); 108#elif defined(EMULATE_CAS) 109 bool result = false; 110 111 enterLock(a); 112 if (a->value == oldval) { 113 a->value = newval; 114 result = true; 115 } 116 leaveLock(a); 117 118 return result; 119#else 120#error Please define your platform. 121#endif 122} 123 124bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval) 125{ 126#ifdef HAVE_MSC_ATOMICS 127 SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value)); 128 return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval; 129#elif defined(HAVE_GCC_ATOMICS) 130 return __sync_bool_compare_and_swap(&a->value, oldval, newval); 131#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. 132 return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t *)&a->value); 133#elif defined(SDL_PLATFORM_SOLARIS) 134 SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value)); 135 return ((Uint32)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval); 136#elif defined(EMULATE_CAS) 137 bool result = false; 138 139 enterLock(a); 140 if (a->value == oldval) { 141 a->value = newval; 142 result = true; 143 } 144 leaveLock(a); 145 146 return result; 147#else 148#error Please define your platform. 149#endif 150} 151 152bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval) 153{ 154#ifdef HAVE_MSC_ATOMICS 155 return _InterlockedCompareExchangePointer(a, newval, oldval) == oldval; 156#elif defined(HAVE_GCC_ATOMICS) 157 return __sync_bool_compare_and_swap(a, oldval, newval); 158#elif defined(SDL_PLATFORM_MACOS) && defined(__LP64__) // this is deprecated in 10.12 sdk; favor gcc atomics. 159 return OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t *)a); 160#elif defined(SDL_PLATFORM_MACOS) && !defined(__LP64__) // this is deprecated in 10.12 sdk; favor gcc atomics. 161 return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t *)a); 162#elif defined(SDL_PLATFORM_SOLARIS) 163 return (atomic_cas_ptr(a, oldval, newval) == oldval); 164#elif defined(EMULATE_CAS) 165 bool result = false; 166 167 enterLock(a); 168 if (*a == oldval) { 169 *a = newval; 170 result = true; 171 } 172 leaveLock(a); 173 174 return result; 175#else 176#error Please define your platform. 177#endif 178} 179 180int SDL_SetAtomicInt(SDL_AtomicInt *a, int v) 181{ 182#ifdef HAVE_MSC_ATOMICS 183 SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); 184 return _InterlockedExchange((long *)&a->value, v); 185#elif defined(HAVE_GCC_ATOMICS) 186 return __sync_lock_test_and_set(&a->value, v); 187#elif defined(SDL_PLATFORM_SOLARIS) 188 SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value)); 189 return (int)atomic_swap_uint((volatile uint_t *)&a->value, v); 190#else 191 int value; 192 do { 193 value = a->value; 194 } while (!SDL_CompareAndSwapAtomicInt(a, value, v)); 195 return value; 196#endif 197} 198 199Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v) 200{ 201#ifdef HAVE_MSC_ATOMICS 202 SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); 203 return _InterlockedExchange((long *)&a->value, v); 204#elif defined(HAVE_GCC_ATOMICS) 205 return __sync_lock_test_and_set(&a->value, v); 206#elif defined(SDL_PLATFORM_SOLARIS) 207 SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value)); 208 return (Uint32)atomic_swap_uint((volatile uint_t *)&a->value, v); 209#else 210 Uint32 value; 211 do { 212 value = a->value; 213 } while (!SDL_CompareAndSwapAtomicU32(a, value, v)); 214 return value; 215#endif 216} 217 218void *SDL_SetAtomicPointer(void **a, void *v) 219{ 220#ifdef HAVE_MSC_ATOMICS 221 return _InterlockedExchangePointer(a, v); 222#elif defined(HAVE_GCC_ATOMICS) 223 return __sync_lock_test_and_set(a, v); 224#elif defined(SDL_PLATFORM_SOLARIS) 225 return atomic_swap_ptr(a, v); 226#else 227 void *value; 228 do { 229 value = *a; 230 } while (!SDL_CompareAndSwapAtomicPointer(a, value, v)); 231 return value; 232#endif 233} 234 235int SDL_AddAtomicInt(SDL_AtomicInt *a, int v) 236{ 237#ifdef HAVE_MSC_ATOMICS 238 SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); 239 return _InterlockedExchangeAdd((long *)&a->value, v); 240#elif defined(HAVE_GCC_ATOMICS) 241 return __sync_fetch_and_add(&a->value, v); 242#elif defined(SDL_PLATFORM_SOLARIS) 243 int pv = a->value; 244 membar_consumer(); 245 atomic_add_int((volatile uint_t *)&a->value, v); 246 return pv; 247#else 248 int value; 249 do { 250 value = a->value; 251 } while (!SDL_CompareAndSwapAtomicInt(a, value, (value + v))); 252 return value; 253#endif 254} 255 256Uint32 SDL_AddAtomicU32(SDL_AtomicU32 *a, int v) 257{ 258#ifdef HAVE_MSC_ATOMICS 259 SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); 260 return (Uint32)_InterlockedExchangeAdd((long *)&a->value, v); 261#elif defined(HAVE_GCC_ATOMICS) 262 return __sync_fetch_and_add(&a->value, v); 263#elif defined(SDL_PLATFORM_SOLARIS) 264 Uint32 pv = a->value; 265 membar_consumer(); 266 atomic_add_int((volatile uint_t *)&a->value, v); 267 return pv; 268#else 269 Uint32 value; 270 do { 271 value = a->value; 272 } while (!SDL_CompareAndSwapAtomicU32(a, value, (value + v))); 273 return value; 274#endif 275} 276 277int SDL_GetAtomicInt(SDL_AtomicInt *a) 278{ 279#ifdef HAVE_ATOMIC_LOAD_N 280 return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); 281#elif defined(HAVE_MSC_ATOMICS) 282 SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); 283 return _InterlockedOr((long *)&a->value, 0); 284#elif defined(HAVE_GCC_ATOMICS) 285 return __sync_or_and_fetch(&a->value, 0); 286#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. 287 return sizeof(a->value) == sizeof(uint32_t) ? OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value) : OSAtomicAdd64Barrier(0, (volatile int64_t *)&a->value); 288#elif defined(SDL_PLATFORM_SOLARIS) 289 return atomic_or_uint_nv((volatile uint_t *)&a->value, 0); 290#else 291 int value; 292 do { 293 value = a->value; 294 } while (!SDL_CompareAndSwapAtomicInt(a, value, value)); 295 return value; 296#endif 297} 298 299Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a) 300{ 301#ifdef HAVE_ATOMIC_LOAD_N 302 return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); 303#elif defined(HAVE_MSC_ATOMICS) 304 SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); 305 return (Uint32)_InterlockedOr((long *)&a->value, 0); 306#elif defined(HAVE_GCC_ATOMICS) 307 return __sync_or_and_fetch(&a->value, 0); 308#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. 309 return OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value); 310#elif defined(SDL_PLATFORM_SOLARIS) 311 SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(uint_t) == sizeof(a->value)); 312 return (Uint32)atomic_or_uint_nv((volatile uint_t *)&a->value, 0); 313#else 314 Uint32 value; 315 do { 316 value = a->value; 317 } while (!SDL_CompareAndSwapAtomicU32(a, value, value)); 318 return value; 319#endif 320} 321 322void *SDL_GetAtomicPointer(void **a) 323{ 324#ifdef HAVE_ATOMIC_LOAD_N 325 return __atomic_load_n(a, __ATOMIC_SEQ_CST); 326#elif defined(HAVE_MSC_ATOMICS) 327 return _InterlockedCompareExchangePointer(a, NULL, NULL); 328#elif defined(HAVE_GCC_ATOMICS) 329 return __sync_val_compare_and_swap(a, (void *)0, (void *)0); 330#elif defined(SDL_PLATFORM_SOLARIS) 331 return atomic_cas_ptr(a, (void *)0, (void *)0); 332#else 333 void *value; 334 do { 335 value = *a; 336 } while (!SDL_CompareAndSwapAtomicPointer(a, value, value)); 337 return value; 338#endif 339} 340 341#ifdef SDL_MEMORY_BARRIER_USES_FUNCTION 342#error This file should be built in arm mode so the mcr instruction is available for memory barriers 343#endif 344 345void SDL_MemoryBarrierReleaseFunction(void) 346{ 347 SDL_MemoryBarrierRelease(); 348} 349 350void SDL_MemoryBarrierAcquireFunction(void) 351{ 352 SDL_MemoryBarrierAcquire(); 353} 354
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.