Path: blob/master/thirdparty/sdl/atomic/SDL_atomic.c
10278 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2025 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/20#include "SDL_internal.h"2122#if defined(_MSC_VER) && (_MSC_VER >= 1900)23#include <intrin.h>24#define HAVE_MSC_ATOMICS 125#endif2627#ifdef SDL_PLATFORM_MACOS // !!! FIXME: should we favor gcc atomics?28#include <libkern/OSAtomic.h>29#endif3031#if !defined(HAVE_GCC_ATOMICS) && defined(SDL_PLATFORM_SOLARIS)32#include <atomic.h>33#endif3435// The __atomic_load_n() intrinsic showed up in different times for different compilers.36#ifdef __clang__37#if __has_builtin(__atomic_load_n) || defined(HAVE_GCC_ATOMICS)38/* !!! FIXME: this advertises as available in the NDK but uses an external symbol we don't have.39It might be in a later NDK or we might need an extra library? --ryan. */40#ifndef SDL_PLATFORM_ANDROID41#define HAVE_ATOMIC_LOAD_N 142#endif43#endif44#elif defined(__GNUC__)45#if (__GNUC__ >= 5)46#define HAVE_ATOMIC_LOAD_N 147#endif48#endif4950/* *INDENT-OFF* */ // clang-format off51#if defined(__WATCOMC__) && defined(__386__)52SDL_COMPILE_TIME_ASSERT(intsize, 4==sizeof(int));53#define HAVE_WATCOM_ATOMICS54extern __inline int _SDL_xchg_watcom(volatile int *a, int v);55#pragma aux _SDL_xchg_watcom = \56"lock xchg [ecx], eax" \57parm [ecx] [eax] \58value [eax] \59modify exact [eax];6061extern __inline unsigned char _SDL_cmpxchg_watcom(volatile int *a, int newval, int oldval);62#pragma aux _SDL_cmpxchg_watcom = \63"lock cmpxchg [edx], ecx" \64"setz al" \65parm [edx] [ecx] [eax] \66value [al] \67modify exact [eax];6869extern __inline int _SDL_xadd_watcom(volatile int *a, int v);70#pragma aux _SDL_xadd_watcom = \71"lock xadd [ecx], eax" \72parm [ecx] [eax] \73value [eax] \74modify exact [eax];7576#endif // __WATCOMC__ && __386__77/* *INDENT-ON* */ // clang-format on7879/*80If any of the operations are not provided then we must emulate some81of them. That means we need a nice implementation of spin locks82that avoids the "one big lock" problem. We use a vector of spin83locks and pick which one to use based on the address of the operand84of the function.8586To generate the index of the lock we first shift by 3 bits to get87rid on the zero bits that result from 32 and 64 bit alignment of88data. We then mask off all but 5 bits and use those 5 bits as an89index into the table.9091Picking the lock this way insures that accesses to the same data at92the same time will go to the same lock. OTOH, accesses to different93data have only a 1/32 chance of hitting the same lock. That should94pretty much eliminate the chances of several atomic operations on95different data from waiting on the same "big lock". If it isn't96then the table of locks can be expanded to a new size so long as97the new size is a power of two.9899Contributed by Bob Pendleton, [email protected]100*/101102#if !defined(HAVE_MSC_ATOMICS) && !defined(HAVE_GCC_ATOMICS) && !defined(SDL_PLATFORM_MACOS) && !defined(SDL_PLATFORM_SOLARIS) && !defined(HAVE_WATCOM_ATOMICS)103#define EMULATE_CAS 1104#endif105106#ifdef EMULATE_CAS107static SDL_SpinLock locks[32];108109static SDL_INLINE void enterLock(void *a)110{111uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);112113SDL_LockSpinlock(&locks[index]);114}115116static SDL_INLINE void leaveLock(void *a)117{118uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);119120SDL_UnlockSpinlock(&locks[index]);121}122#endif123124bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval)125{126#ifdef HAVE_MSC_ATOMICS127SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value));128return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval;129#elif defined(HAVE_WATCOM_ATOMICS)130return _SDL_cmpxchg_watcom((volatile int *)&a->value, newval, oldval);131#elif defined(HAVE_GCC_ATOMICS)132return __sync_bool_compare_and_swap(&a->value, oldval, newval);133#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.134return OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);135#elif defined(SDL_PLATFORM_SOLARIS)136SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value));137return ((int)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval);138#elif defined(EMULATE_CAS)139bool result = false;140141enterLock(a);142if (a->value == oldval) {143a->value = newval;144result = true;145}146leaveLock(a);147148return result;149#else150#error Please define your platform.151#endif152}153154bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval)155{156#ifdef HAVE_MSC_ATOMICS157SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value));158return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval;159#elif defined(HAVE_WATCOM_ATOMICS)160SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(int) == sizeof(a->value));161return _SDL_cmpxchg_watcom((volatile int *)&a->value, (int)newval, (int)oldval);162#elif defined(HAVE_GCC_ATOMICS)163return __sync_bool_compare_and_swap(&a->value, oldval, newval);164#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.165return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*)&a->value);166#elif defined(SDL_PLATFORM_SOLARIS)167SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value));168return ((Uint32)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval);169#elif defined(EMULATE_CAS)170bool result = false;171172enterLock(a);173if (a->value == oldval) {174a->value = newval;175result = true;176}177leaveLock(a);178179return result;180#else181#error Please define your platform.182#endif183}184185bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval)186{187#ifdef HAVE_MSC_ATOMICS188return _InterlockedCompareExchangePointer(a, newval, oldval) == oldval;189#elif defined(HAVE_WATCOM_ATOMICS)190return _SDL_cmpxchg_watcom((int *)a, (long)newval, (long)oldval);191#elif defined(HAVE_GCC_ATOMICS)192return __sync_bool_compare_and_swap(a, oldval, newval);193#elif defined(SDL_PLATFORM_MACOS) && defined(__LP64__) // this is deprecated in 10.12 sdk; favor gcc atomics.194return OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t *)a);195#elif defined(SDL_PLATFORM_MACOS) && !defined(__LP64__) // this is deprecated in 10.12 sdk; favor gcc atomics.196return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t *)a);197#elif defined(SDL_PLATFORM_SOLARIS)198return (atomic_cas_ptr(a, oldval, newval) == oldval);199#elif defined(EMULATE_CAS)200bool result = false;201202enterLock(a);203if (*a == oldval) {204*a = newval;205result = true;206}207leaveLock(a);208209return result;210#else211#error Please define your platform.212#endif213}214215int SDL_SetAtomicInt(SDL_AtomicInt *a, int v)216{217#ifdef HAVE_MSC_ATOMICS218SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value));219return _InterlockedExchange((long *)&a->value, v);220#elif defined(HAVE_WATCOM_ATOMICS)221return _SDL_xchg_watcom(&a->value, v);222#elif defined(HAVE_GCC_ATOMICS)223return __sync_lock_test_and_set(&a->value, v);224#elif defined(SDL_PLATFORM_SOLARIS)225SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value));226return (int)atomic_swap_uint((volatile uint_t *)&a->value, v);227#else228int value;229do {230value = a->value;231} while (!SDL_CompareAndSwapAtomicInt(a, value, v));232return value;233#endif234}235236Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v)237{238#ifdef HAVE_MSC_ATOMICS239SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value));240return _InterlockedExchange((long *)&a->value, v);241#elif defined(HAVE_WATCOM_ATOMICS)242return _SDL_xchg_watcom(&a->value, v);243#elif defined(HAVE_GCC_ATOMICS)244return __sync_lock_test_and_set(&a->value, v);245#elif defined(SDL_PLATFORM_SOLARIS)246SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value));247return (Uint32)atomic_swap_uint((volatile uint_t *)&a->value, v);248#else249Uint32 value;250do {251value = a->value;252} while (!SDL_CompareAndSwapAtomicU32(a, value, v));253return value;254#endif255}256257void *SDL_SetAtomicPointer(void **a, void *v)258{259#ifdef HAVE_MSC_ATOMICS260return _InterlockedExchangePointer(a, v);261#elif defined(HAVE_WATCOM_ATOMICS)262return (void *)_SDL_xchg_watcom((int *)a, (long)v);263#elif defined(HAVE_GCC_ATOMICS)264return __sync_lock_test_and_set(a, v);265#elif defined(SDL_PLATFORM_SOLARIS)266return atomic_swap_ptr(a, v);267#else268void *value;269do {270value = *a;271} while (!SDL_CompareAndSwapAtomicPointer(a, value, v));272return value;273#endif274}275276int SDL_AddAtomicInt(SDL_AtomicInt *a, int v)277{278#ifdef HAVE_MSC_ATOMICS279SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value));280return _InterlockedExchangeAdd((long *)&a->value, v);281#elif defined(HAVE_WATCOM_ATOMICS)282SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(int) == sizeof(a->value));283return _SDL_xadd_watcom((volatile int *)&a->value, v);284#elif defined(HAVE_GCC_ATOMICS)285return __sync_fetch_and_add(&a->value, v);286#elif defined(SDL_PLATFORM_SOLARIS)287int pv = a->value;288membar_consumer();289atomic_add_int((volatile uint_t *)&a->value, v);290return pv;291#else292int value;293do {294value = a->value;295} while (!SDL_CompareAndSwapAtomicInt(a, value, (value + v)));296return value;297#endif298}299300int SDL_GetAtomicInt(SDL_AtomicInt *a)301{302#ifdef HAVE_ATOMIC_LOAD_N303return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);304#elif defined(HAVE_MSC_ATOMICS)305SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value));306return _InterlockedOr((long *)&a->value, 0);307#elif defined(HAVE_WATCOM_ATOMICS)308return _SDL_xadd_watcom(&a->value, 0);309#elif defined(HAVE_GCC_ATOMICS)310return __sync_or_and_fetch(&a->value, 0);311#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.312return sizeof(a->value) == sizeof(uint32_t) ? OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value) : OSAtomicAdd64Barrier(0, (volatile int64_t *)&a->value);313#elif defined(SDL_PLATFORM_SOLARIS)314return atomic_or_uint_nv((volatile uint_t *)&a->value, 0);315#else316int value;317do {318value = a->value;319} while (!SDL_CompareAndSwapAtomicInt(a, value, value));320return value;321#endif322}323324Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a)325{326#ifdef HAVE_ATOMIC_LOAD_N327return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);328#elif defined(HAVE_MSC_ATOMICS)329SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value));330return (Uint32)_InterlockedOr((long *)&a->value, 0);331#elif defined(HAVE_WATCOM_ATOMICS)332SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(int) == sizeof(a->value));333return (Uint32)_SDL_xadd_watcom((volatile int *)&a->value, 0);334#elif defined(HAVE_GCC_ATOMICS)335return __sync_or_and_fetch(&a->value, 0);336#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.337return OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value);338#elif defined(SDL_PLATFORM_SOLARIS)339SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(uint_t) == sizeof(a->value));340return (Uint32)atomic_or_uint_nv((volatile uint_t *)&a->value, 0);341#else342Uint32 value;343do {344value = a->value;345} while (!SDL_CompareAndSwapAtomicU32(a, value, value));346return value;347#endif348}349350void *SDL_GetAtomicPointer(void **a)351{352#ifdef HAVE_ATOMIC_LOAD_N353return __atomic_load_n(a, __ATOMIC_SEQ_CST);354#elif defined(HAVE_MSC_ATOMICS)355return _InterlockedCompareExchangePointer(a, NULL, NULL);356#elif defined(HAVE_GCC_ATOMICS)357return __sync_val_compare_and_swap(a, (void *)0, (void *)0);358#elif defined(SDL_PLATFORM_SOLARIS)359return atomic_cas_ptr(a, (void *)0, (void *)0);360#else361void *value;362do {363value = *a;364} while (!SDL_CompareAndSwapAtomicPointer(a, value, value));365return value;366#endif367}368369#ifdef SDL_MEMORY_BARRIER_USES_FUNCTION370#error This file should be built in arm mode so the mcr instruction is available for memory barriers371#endif372373void SDL_MemoryBarrierReleaseFunction(void)374{375SDL_MemoryBarrierRelease();376}377378void SDL_MemoryBarrierAcquireFunction(void)379{380SDL_MemoryBarrierAcquire();381}382383384