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