From d0edf6877413750e507bf1d9149cd29080d4ae09 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 17 Sep 2024 03:27:11 -0700 Subject: [PATCH] Added Uint32 versions of the atomic functions --- include/SDL3/SDL_atomic.h | 90 ++++++++++++++++++++++++++++++- src/atomic/SDL_atomic.c | 85 ++++++++++++++++++++++++++++- src/dynapi/SDL_dynapi.sym | 3 ++ src/dynapi/SDL_dynapi_overrides.h | 3 ++ src/dynapi/SDL_dynapi_procs.h | 3 ++ 5 files changed, 180 insertions(+), 4 deletions(-) diff --git a/include/SDL3/SDL_atomic.h b/include/SDL3/SDL_atomic.h index f76bcbb43e..3a8be35646 100644 --- a/include/SDL3/SDL_atomic.h +++ b/include/SDL3/SDL_atomic.h @@ -316,7 +316,7 @@ typedef void (*SDL_KernelMemoryBarrierFunc)(); * this!). * * This is a struct so people don't accidentally use numeric operations on it - * directly. You have to use SDL_Atomic* functions. + * directly. You have to use SDL atomic functions. * * \since This struct is available since SDL 3.0.0. * @@ -342,7 +342,8 @@ typedef struct SDL_AtomicInt { int value; } SDL_AtomicInt; * * \since This function is available since SDL 3.0.0. * - * \sa SDL_CompareAndSwapAtomicPointer + * \sa SDL_GetAtomicInt + * \sa SDL_SetAtomicInt */ extern SDL_DECLSPEC SDL_bool SDLCALL SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval); @@ -439,6 +440,91 @@ extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicInt(SDL_AtomicInt *a, int v); #define SDL_AtomicDecRef(a) (SDL_AddAtomicInt(a, -1) == 1) #endif +/** + * A type representing an atomic unsigned 32-bit value. + * + * This can be used to manage a value that is synchronized across multiple + * CPUs without a race condition; when an app sets a value with SDL_SetAtomicU32 + * all other threads, regardless of the CPU it is running on, will see that + * value when retrieved with SDL_GetAtomicU32, regardless of CPU caches, etc. + * + * This is also useful for atomic compare-and-swap operations: a thread can + * change the value as long as its current value matches expectations. When + * done in a loop, one can guarantee data consistency across threads without a + * lock (but the usual warnings apply: if you don't know what you're doing, or + * you don't do it carefully, you can confidently cause any number of + * disasters with this, so in most cases, you _should_ use a mutex instead of + * this!). + * + * This is a struct so people don't accidentally use numeric operations on it + * directly. You have to use SDL atomic functions. + * + * \since This struct is available since SDL 3.0.0. + * + * \sa SDL_CompareAndSwapAtomicU32 + * \sa SDL_GetAtomicU32 + * \sa SDL_SetAtomicU32 + * \sa SDL_AddAtomicU32 + */ +typedef struct SDL_AtomicU32 { Uint32 value; } SDL_AtomicU32; + +/** + * Set an atomic variable to a new value if it is currently an old value. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to an SDL_AtomicU32 variable to be modified. + * \param oldval the old value. + * \param newval the new value. + * \returns SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetAtomicU32 + * \sa SDL_SetAtomicU32 + */ +extern SDL_DECLSPEC SDL_bool SDLCALL SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval); + +/** + * Set an atomic variable to a value. + * + * This function also acts as a full memory barrier. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to an SDL_AtomicU32 variable to be modified. + * \param v the desired value. + * \returns the previous value of the atomic variable. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetAtomicU32 + */ +extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v); + +/** + * Get the value of an atomic variable. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to an SDL_AtomicU32 variable. + * \returns the current value of an atomic variable. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_SetAtomicU32 + */ +extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetAtomicU32(SDL_AtomicU32 *a); + /** * Set a pointer to a new value if it is currently an old value. * diff --git a/src/atomic/SDL_atomic.c b/src/atomic/SDL_atomic.c index 1eb1a90ca5..aceb237853 100644 --- a/src/atomic/SDL_atomic.c +++ b/src/atomic/SDL_atomic.c @@ -128,12 +128,13 @@ SDL_bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval) SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value)); return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval; #elif defined(HAVE_WATCOM_ATOMICS) - return _SDL_cmpxchg_watcom(&a->value, newval, oldval); + return _SDL_cmpxchg_watcom((volatile int *)&a->value, newval, oldval); #elif defined(HAVE_GCC_ATOMICS) return __sync_bool_compare_and_swap(&a->value, oldval, newval); #elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. return OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value); #elif defined(SDL_PLATFORM_SOLARIS) + SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value)); return ((int)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval); #elif defined(EMULATE_CAS) bool result = false; @@ -151,6 +152,37 @@ SDL_bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval) #endif } +SDL_bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval) +{ +#ifdef HAVE_MSC_ATOMICS + SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value)); + return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval; +#elif defined(HAVE_WATCOM_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(int) == sizeof(a->value)); + return _SDL_cmpxchg_watcom((volatile int *)&a->value, (int)newval, (int)oldval); +#elif defined(HAVE_GCC_ATOMICS) + return __sync_bool_compare_and_swap(&a->value, oldval, newval); +#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. + return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*)&a->value); +#elif defined(SDL_PLATFORM_SOLARIS) + SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value)); + return ((Uint32)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval); +#elif defined(EMULATE_CAS) + bool result = false; + + enterLock(a); + if (a->value == oldval) { + a->value = newval; + result = true; + } + leaveLock(a); + + return result; +#else +#error Please define your platform. +#endif +} + SDL_bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval) { #ifdef HAVE_MSC_ATOMICS @@ -191,6 +223,7 @@ int SDL_SetAtomicInt(SDL_AtomicInt *a, int v) #elif defined(HAVE_GCC_ATOMICS) return __sync_lock_test_and_set(&a->value, v); #elif defined(SDL_PLATFORM_SOLARIS) + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value)); return (int)atomic_swap_uint((volatile uint_t *)&a->value, v); #else int value; @@ -201,6 +234,27 @@ int SDL_SetAtomicInt(SDL_AtomicInt *a, int v) #endif } +Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v) +{ +#ifdef HAVE_MSC_ATOMICS + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); + return _InterlockedExchange((long *)&a->value, v); +#elif defined(HAVE_WATCOM_ATOMICS) + return _SDL_xchg_watcom(&a->value, v); +#elif defined(HAVE_GCC_ATOMICS) + return __sync_lock_test_and_set(&a->value, v); +#elif defined(SDL_PLATFORM_SOLARIS) + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value)); + return (Uint32)atomic_swap_uint((volatile uint_t *)&a->value, v); +#else + Uint32 value; + do { + value = a->value; + } while (!SDL_CompareAndSwapAtomicU32(a, value, v)); + return value; +#endif +} + void *SDL_SetAtomicPointer(void **a, void *v) { #ifdef HAVE_MSC_ATOMICS @@ -226,7 +280,8 @@ int SDL_AddAtomicInt(SDL_AtomicInt *a, int v) SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); return _InterlockedExchangeAdd((long *)&a->value, v); #elif defined(HAVE_WATCOM_ATOMICS) - return _SDL_xadd_watcom(&a->value, v); + SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(int) == sizeof(a->value)); + return _SDL_xadd_watcom((volatile int *)&a->value, v); #elif defined(HAVE_GCC_ATOMICS) return __sync_fetch_and_add(&a->value, v); #elif defined(SDL_PLATFORM_SOLARIS) @@ -267,6 +322,32 @@ int SDL_GetAtomicInt(SDL_AtomicInt *a) #endif } +Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a) +{ +#ifdef HAVE_ATOMIC_LOAD_N + return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); +#elif defined(HAVE_MSC_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); + return (Uint32)_InterlockedOr((long *)&a->value, 0); +#elif defined(HAVE_WATCOM_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(int) == sizeof(a->value)); + return (Uint32)_SDL_xadd_watcom((volatile int *)&a->value, 0); +#elif defined(HAVE_GCC_ATOMICS) + return __sync_or_and_fetch(&a->value, 0); +#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics. + return OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value); +#elif defined(SDL_PLATFORM_SOLARIS) + SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(uint_t) == sizeof(a->value)); + return (Uint32)atomic_or_uint_nv((volatile uint_t *)&a->value, 0); +#else + Uint32 value; + do { + value = a->value; + } while (!SDL_CompareAndSwapAtomicU32(a, value, value)); + return value; +#endif +} + void *SDL_GetAtomicPointer(void **a) { #ifdef HAVE_ATOMIC_LOAD_N diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 8545534fb6..1b22f05440 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -63,6 +63,7 @@ SDL3_0.0.0 { SDL_CloseStorage; SDL_CompareAndSwapAtomicInt; SDL_CompareAndSwapAtomicPointer; + SDL_CompareAndSwapAtomicU32; SDL_ComposeCustomBlendMode; SDL_ConvertAudioSamples; SDL_ConvertEventToRenderCoordinates; @@ -215,6 +216,7 @@ SDL3_0.0.0 { SDL_GetAssertionReport; SDL_GetAtomicInt; SDL_GetAtomicPointer; + SDL_GetAtomicU32; SDL_GetAudioDeviceChannelMap; SDL_GetAudioDeviceFormat; SDL_GetAudioDeviceGain; @@ -780,6 +782,7 @@ SDL3_0.0.0 { SDL_SetAssertionHandler; SDL_SetAtomicInt; SDL_SetAtomicPointer; + SDL_SetAtomicU32; SDL_SetAudioDeviceGain; SDL_SetAudioPostmixCallback; SDL_SetAudioStreamFormat; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index b6e89d6ebc..4b59a825aa 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -88,6 +88,7 @@ #define SDL_CloseStorage SDL_CloseStorage_REAL #define SDL_CompareAndSwapAtomicInt SDL_CompareAndSwapAtomicInt_REAL #define SDL_CompareAndSwapAtomicPointer SDL_CompareAndSwapAtomicPointer_REAL +#define SDL_CompareAndSwapAtomicU32 SDL_CompareAndSwapAtomicU32_REAL #define SDL_ComposeCustomBlendMode SDL_ComposeCustomBlendMode_REAL #define SDL_ConvertAudioSamples SDL_ConvertAudioSamples_REAL #define SDL_ConvertEventToRenderCoordinates SDL_ConvertEventToRenderCoordinates_REAL @@ -240,6 +241,7 @@ #define SDL_GetAssertionReport SDL_GetAssertionReport_REAL #define SDL_GetAtomicInt SDL_GetAtomicInt_REAL #define SDL_GetAtomicPointer SDL_GetAtomicPointer_REAL +#define SDL_GetAtomicU32 SDL_GetAtomicU32_REAL #define SDL_GetAudioDeviceChannelMap SDL_GetAudioDeviceChannelMap_REAL #define SDL_GetAudioDeviceFormat SDL_GetAudioDeviceFormat_REAL #define SDL_GetAudioDeviceGain SDL_GetAudioDeviceGain_REAL @@ -805,6 +807,7 @@ #define SDL_SetAssertionHandler SDL_SetAssertionHandler_REAL #define SDL_SetAtomicInt SDL_SetAtomicInt_REAL #define SDL_SetAtomicPointer SDL_SetAtomicPointer_REAL +#define SDL_SetAtomicU32 SDL_SetAtomicU32_REAL #define SDL_SetAudioDeviceGain SDL_SetAudioDeviceGain_REAL #define SDL_SetAudioPostmixCallback SDL_SetAudioPostmixCallback_REAL #define SDL_SetAudioStreamFormat SDL_SetAudioStreamFormat_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index b32aa91696..6649326c6d 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -109,6 +109,7 @@ SDL_DYNAPI_PROC(void,SDL_CloseSensor,(SDL_Sensor *a),(a),) SDL_DYNAPI_PROC(SDL_bool,SDL_CloseStorage,(SDL_Storage *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_CompareAndSwapAtomicInt,(SDL_AtomicInt *a, int b, int c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_bool,SDL_CompareAndSwapAtomicPointer,(void **a, void *b, void *c),(a,b,c),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_CompareAndSwapAtomicU32,(SDL_AtomicU32 *a, Uint32 b, Uint32 c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_BlendMode,SDL_ComposeCustomBlendMode,(SDL_BlendFactor a, SDL_BlendFactor b, SDL_BlendOperation c, SDL_BlendFactor d, SDL_BlendFactor e, SDL_BlendOperation f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ConvertAudioSamples,(const SDL_AudioSpec *a, const Uint8 *b, int c, const SDL_AudioSpec *d, Uint8 **e, int *f),(a,b,c,d,e,f),return) SDL_DYNAPI_PROC(SDL_bool,SDL_ConvertEventToRenderCoordinates,(SDL_Renderer *a, SDL_Event *b),(a,b),return) @@ -261,6 +262,7 @@ SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetAssertionHandler,(void **a),(a),retu SDL_DYNAPI_PROC(const SDL_AssertData*,SDL_GetAssertionReport,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetAtomicInt,(SDL_AtomicInt *a),(a),return) SDL_DYNAPI_PROC(void*,SDL_GetAtomicPointer,(void **a),(a),return) +SDL_DYNAPI_PROC(Uint32,SDL_GetAtomicU32,(SDL_AtomicU32 *a),(a),return) SDL_DYNAPI_PROC(int*,SDL_GetAudioDeviceChannelMap,(SDL_AudioDeviceID a, int *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_GetAudioDeviceFormat,(SDL_AudioDeviceID a, SDL_AudioSpec *b, int *c),(a,b,c),return) SDL_DYNAPI_PROC(float,SDL_GetAudioDeviceGain,(SDL_AudioDeviceID a),(a),return) @@ -816,6 +818,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_SetAppMetadataProperty,(const char *a, const char * SDL_DYNAPI_PROC(void,SDL_SetAssertionHandler,(SDL_AssertionHandler a, void *b),(a,b),) SDL_DYNAPI_PROC(int,SDL_SetAtomicInt,(SDL_AtomicInt *a, int b),(a,b),return) SDL_DYNAPI_PROC(void*,SDL_SetAtomicPointer,(void **a, void *b),(a,b),return) +SDL_DYNAPI_PROC(Uint32,SDL_SetAtomicU32,(SDL_AtomicU32 *a, Uint32 b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetAudioDeviceGain,(SDL_AudioDeviceID a, float b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetAudioPostmixCallback,(SDL_AudioDeviceID a, SDL_AudioPostmixCallback b, void *c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_bool,SDL_SetAudioStreamFormat,(SDL_AudioStream *a, const SDL_AudioSpec *b, const SDL_AudioSpec *c),(a,b,c),return)