From a324e3993c588c81d76755748defd9ebb2c2a5ca Mon Sep 17 00:00:00 2001 From: Maia Date: Thu, 28 Aug 2025 14:17:19 +0200 Subject: [PATCH 1/2] Add relaxed atomics --- include/SDL3/SDL_atomic.h | 245 ++++++++++++++++++++++++++++++ src/atomic/SDL_atomic.c | 187 ++++++++++++++++++++++- src/dynapi/SDL_dynapi.sym | 11 ++ src/dynapi/SDL_dynapi_overrides.h | 11 ++ src/dynapi/SDL_dynapi_procs.h | 11 ++ 5 files changed, 457 insertions(+), 8 deletions(-) diff --git a/include/SDL3/SDL_atomic.h b/include/SDL3/SDL_atomic.h index bfcf81ee067ba..d02728ec25a4a 100644 --- a/include/SDL3/SDL_atomic.h +++ b/include/SDL3/SDL_atomic.h @@ -414,6 +414,27 @@ typedef struct SDL_AtomicInt { int value; } SDL_AtomicInt; */ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval); +/** + * Set an atomic variable to a new value if it is currently an old value, + * using relaxed memory ordering. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to an SDL_AtomicInt variable to be modified. + * \param oldval the old value. + * \param newval the new value. + * \returns true if the atomic variable was set, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_GetRelaxedAtomicInt + * \sa SDL_SetRelaxedAtomicInt + */ +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicInt(SDL_AtomicInt *a, int oldval, int newval); + /** * Set an atomic variable to a value. * @@ -434,6 +455,24 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, i */ extern SDL_DECLSPEC int SDLCALL SDL_SetAtomicInt(SDL_AtomicInt *a, int v); +/** + * Set an atomic variable to a value using relaxed memory ordering. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to an SDL_AtomicInt 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.4.0. + * + * \sa SDL_GetRelaxedAtomicInt + */ +extern SDL_DECLSPEC int SDLCALL SDL_SetRelaxedAtomicInt(SDL_AtomicInt *a, int v); + /** * Get the value of an atomic variable. * @@ -451,6 +490,23 @@ extern SDL_DECLSPEC int SDLCALL SDL_SetAtomicInt(SDL_AtomicInt *a, int v); */ extern SDL_DECLSPEC int SDLCALL SDL_GetAtomicInt(SDL_AtomicInt *a); +/** + * Get the value of an atomic variable using relaxed memory ordering. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to an SDL_AtomicInt 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.4.0. + * + * \sa SDL_SetRelaxedAtomicInt + */ +extern SDL_DECLSPEC int SDLCALL SDL_GetRelaxedAtomicInt(SDL_AtomicInt *a); + /** * Add to an atomic variable. * @@ -472,6 +528,25 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetAtomicInt(SDL_AtomicInt *a); */ extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicInt(SDL_AtomicInt *a, int v); +/** + * Add to an atomic variable using relaxed memory ordering. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to an SDL_AtomicInt variable to be modified. + * \param v the desired value to add. + * \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.4.0. + * + * \sa SDL_RelaxedAtomicDecRef + * \sa SDL_RelaxedAtomicIncRef + */ +extern SDL_DECLSPEC int SDLCALL SDL_AddRelaxedAtomicInt(SDL_AtomicInt *a, int v); + #ifndef SDL_AtomicIncRef /** @@ -491,6 +566,25 @@ extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicInt(SDL_AtomicInt *a, int v); #define SDL_AtomicIncRef(a) SDL_AddAtomicInt(a, 1) #endif +#ifndef SDL_RelaxedAtomicIncRef + +/** + * Increment an atomic variable used as a reference count, using relaxed memory ordering. + * + * ***Note: If you don't know what this macro is for, you shouldn't use it!*** + * + * \param a a pointer to an SDL_AtomicInt to increment. + * \returns the previous value of the atomic variable. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.4.0. + * + * \sa SDL_RelaxedAtomicDecRef + */ +#define SDL_RelaxedAtomicIncRef(a) SDL_AddRelaxedAtomicInt(a, 1) +#endif + #ifndef SDL_AtomicDecRef /** @@ -511,6 +605,26 @@ extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicInt(SDL_AtomicInt *a, int v); #define SDL_AtomicDecRef(a) (SDL_AddAtomicInt(a, -1) == 1) #endif +#ifndef SDL_RelaxedAtomicDecRef + +/** + * Decrement an atomic variable used as a reference count, using relaxed memory ordering. + * + * ***Note: If you don't know what this macro is for, you shouldn't use it!*** + * + * \param a a pointer to an SDL_AtomicInt to decrement. + * \returns true if the variable reached zero after decrementing, false + * otherwise. + * + * \threadsafety It is safe to call this macro from any thread. + * + * \since This macro is available since SDL 3.4.0. + * + * \sa SDL_AtomicIncRef + */ +#define SDL_RelaxedAtomicDecRef(a) (SDL_AddRelaxedAtomicInt(a, -1) == 1) +#endif + /** * A type representing an atomic unsigned 32-bit value. * @@ -559,6 +673,27 @@ typedef struct SDL_AtomicU32 { Uint32 value; } SDL_AtomicU32; */ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval); +/** + * Set an atomic variable to a new value if it is currently an old value, + * using relaxed memory ordering. + * + * ***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 true if the atomic variable was set, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_GetRelaxedAtomicU32 + * \sa SDL_SetRelaxedAtomicU32 + */ +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval); + /** * Set an atomic variable to a value. * @@ -579,6 +714,24 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, U */ extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v); +/** + * Set an atomic variable to a value using relaxed memory ordering. + * + * ***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.4.0. + * + * \sa SDL_GetRelaxedAtomicU32 + */ +extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetRelaxedAtomicU32(SDL_AtomicU32 *a, Uint32 v); + /** * Get the value of an atomic variable. * @@ -596,6 +749,23 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v); */ extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetAtomicU32(SDL_AtomicU32 *a); +/** + * Get the value of an atomic variable using relaxed memory ordering. + * + * ***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.4.0. + * + * \sa SDL_SetRelaxedAtomicU32 + */ +extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetRelaxedAtomicU32(SDL_AtomicU32 *a); + /** * Add to an atomic variable. * @@ -614,6 +784,22 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetAtomicU32(SDL_AtomicU32 *a); */ extern SDL_DECLSPEC Uint32 SDLCALL SDL_AddAtomicU32(SDL_AtomicU32 *a, int v); +/** + * Add to an atomic variable using relaxed memory ordering. + * + * ***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 to add or subtract. + * \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.4.0. + */ +extern SDL_DECLSPEC Uint32 SDLCALL SDL_AddRelaxedAtomicU32(SDL_AtomicU32 *a, int v); + /** * Set a pointer to a new value if it is currently an old value. * @@ -635,6 +821,28 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_AddAtomicU32(SDL_AtomicU32 *a, int v); */ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval); +/** + * Set a pointer to a new value if it is currently an old value, + * using relaxed memory ordering. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to a pointer. + * \param oldval the old pointer value. + * \param newval the new pointer value. + * \returns true if the pointer was set, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CompareAndSwapRelaxedAtomicInt + * \sa SDL_GetRelaxedAtomicPointer + * \sa SDL_SetRelaxedAtomicPointer + */ +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicPointer(void **a, void *oldval, void *newval); + /** * Set a pointer to a value atomically. * @@ -654,6 +862,25 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicPointer(void **a, void */ extern SDL_DECLSPEC void * SDLCALL SDL_SetAtomicPointer(void **a, void *v); +/** + * Set a pointer to a value atomically using relaxed memory ordering. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to a pointer. + * \param v the desired pointer value. + * \returns the previous value of the pointer. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CompareAndSwapRelaxedAtomicPointer + * \sa SDL_GetRelaxedAtomicPointer + */ +extern SDL_DECLSPEC void * SDLCALL SDL_SetRelaxedAtomicPointer(void **a, void *v); + /** * Get the value of a pointer atomically. * @@ -672,6 +899,24 @@ extern SDL_DECLSPEC void * SDLCALL SDL_SetAtomicPointer(void **a, void *v); */ extern SDL_DECLSPEC void * SDLCALL SDL_GetAtomicPointer(void **a); +/** + * Get the value of a pointer atomically using relaxed memory ordering. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to a pointer. + * \returns the current value of a pointer. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CompareAndSwapRelaxedAtomicPointer + * \sa SDL_SetRelaxedAtomicPointer + */ +extern SDL_DECLSPEC void * SDLCALL SDL_GetRelaxedAtomicPointer(void **a); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/atomic/SDL_atomic.c b/src/atomic/SDL_atomic.c index c51ba710a563a..94308ed9df7e9 100644 --- a/src/atomic/SDL_atomic.c +++ b/src/atomic/SDL_atomic.c @@ -23,6 +23,9 @@ #if defined(_MSC_VER) && (_MSC_VER >= 1900) #include #define HAVE_MSC_ATOMICS 1 +#if defined(_M_ARM) || defined(_M_ARM64) +#define HAVE_MSC_NF_ATOMICS 1 +#endif #endif #ifdef SDL_PLATFORM_MACOS // !!! FIXME: should we favor gcc atomics? @@ -42,9 +45,32 @@ #define HAVE_ATOMIC_LOAD_N 1 #endif #endif +#if __has_builtin(__atomic_compare_exchange_n) || defined(HAVE_GCC_ATOMICS) +/* !!! FIXME: see above */ +#ifndef SDL_PLATFORM_ANDROID +#define HAVE_ATOMIC_COMPARE_EXCHANGE_N 1 +#endif +#endif +#if __has_builtin(__atomic_exchange_n) || defined(HAVE_GCC_ATOMICS) +/* !!! FIXME: see above */ +#ifndef SDL_PLATFORM_ANDROID +#define HAVE_ATOMIC_EXCHANGE_N 1 +#endif +#endif +#if __has_builtin(__atomic_fetch_add) || defined(HAVE_GCC_ATOMICS) +/* !!! FIXME: see above */ +#ifndef SDL_PLATFORM_ANDROID +#define HAVE_ATOMIC_FETCH_ADD 1 +#endif +#endif #elif defined(__GNUC__) #if (__GNUC__ >= 5) #define HAVE_ATOMIC_LOAD_N 1 +#if !defined(SDL_PLATFORM_PS2) +#define HAVE_ATOMIC_COMPARE_EXCHANGE_N 1 +#define HAVE_ATOMIC_EXCHANGE_N 1 +#define HAVE_ATOMIC_FETCH_ADD 1 +#endif #endif #endif @@ -124,7 +150,9 @@ static SDL_INLINE void leaveLock(void *a) bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval) { -#ifdef HAVE_MSC_ATOMICS +#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N + return __atomic_compare_exchange_n(&a->value, &oldval, newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +#elif defined(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) @@ -152,9 +180,23 @@ bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval) #endif } +bool SDL_CompareAndSwapRelaxedAtomicInt(SDL_AtomicInt *a, int oldval, int newval) +{ +#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N + return __atomic_compare_exchange_n(&a->value, &oldval, newval, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value)); + return _InterlockedCompareExchange_nf((long *)&a->value, (long)newval, (long)oldval) == (long)oldval; +#else + return SDL_CompareAndSwapAtomicInt(a, oldval, newval); +#endif +} + bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval) { -#ifdef HAVE_MSC_ATOMICS +#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N + return __atomic_compare_exchange_n(&a->value, &oldval, newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +#elif defined(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) @@ -183,9 +225,23 @@ bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval) #endif } +bool SDL_CompareAndSwapRelaxedAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval) +{ +#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N + return __atomic_compare_exchange_n(&a->value, &oldval, newval, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value)); + return _InterlockedCompareExchange_nf((long *)&a->value, (long)newval, (long)oldval) == (long)oldval; +#else + return SDL_CompareAndSwapAtomicU32(a, oldval, newval); +#endif +} + bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval) { -#ifdef HAVE_MSC_ATOMICS +#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N + return __atomic_compare_exchange_n(a, &oldval, newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +#elif defined(HAVE_MSC_ATOMICS) return _InterlockedCompareExchangePointer(a, newval, oldval) == oldval; #elif defined(HAVE_WATCOM_ATOMICS) return _SDL_cmpxchg_watcom((int *)a, (long)newval, (long)oldval); @@ -213,9 +269,22 @@ bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval) #endif } +bool SDL_CompareAndSwapRelaxedAtomicPointer(void **a, void *oldval, void *newval) +{ +#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N + return __atomic_compare_exchange_n(a, &oldval, newval, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + return _InterlockedCompareExchangePointer_nf(a, newval, oldval) == oldval; +#else + return SDL_CompareAndSwapAtomicPointer(a, oldval, newval); +#endif +} + int SDL_SetAtomicInt(SDL_AtomicInt *a, int v) { -#ifdef HAVE_MSC_ATOMICS +#ifdef HAVE_ATOMIC_EXCHANGE_N + return __atomic_exchange_n(&a->value, v, __ATOMIC_SEQ_CST); +#elif defined(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) @@ -234,9 +303,23 @@ int SDL_SetAtomicInt(SDL_AtomicInt *a, int v) #endif } +int SDL_SetRelaxedAtomicInt(SDL_AtomicInt *a, int v) +{ +#ifdef HAVE_ATOMIC_EXCHANGE_N + return __atomic_exchange_n(&a->value, v, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); + return _InterlockedExchange_nf((long *)&a->value, v); +#else + return SDL_SetAtomicInt(a, v); +#endif +} + Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v) { -#ifdef HAVE_MSC_ATOMICS +#ifdef HAVE_ATOMIC_EXCHANGE_N + return __atomic_exchange_n(&a->value, v, __ATOMIC_SEQ_CST); +#elif defined(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) @@ -255,9 +338,23 @@ Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v) #endif } +Uint32 SDL_SetRelaxedAtomicU32(SDL_AtomicU32 *a, Uint32 v) +{ +#ifdef HAVE_ATOMIC_EXCHANGE_N + return __atomic_exchange_n(&a->value, v, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); + return _InterlockedExchange_nf((long *)&a->value, v); +#else + return SDL_SetAtomicU32(a, v); +#endif +} + void *SDL_SetAtomicPointer(void **a, void *v) { -#ifdef HAVE_MSC_ATOMICS +#ifdef HAVE_ATOMIC_EXCHANGE_N + return __atomic_exchange_n(a, v, __ATOMIC_SEQ_CST); +#elif defined(HAVE_MSC_ATOMICS) return _InterlockedExchangePointer(a, v); #elif defined(HAVE_WATCOM_ATOMICS) return (void *)_SDL_xchg_watcom((int *)a, (long)v); @@ -274,9 +371,22 @@ void *SDL_SetAtomicPointer(void **a, void *v) #endif } +void *SDL_SetRelaxedAtomicPointer(void **a, void *v) +{ +#ifdef HAVE_ATOMIC_EXCHANGE_N + return __atomic_exchange_n(a, v, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + return _InterlockedExchangePointer_nf(a, v); +#else + return SDL_SetAtomicPointer(a, v); +#endif +} + int SDL_AddAtomicInt(SDL_AtomicInt *a, int v) { -#ifdef HAVE_MSC_ATOMICS +#ifdef HAVE_ATOMIC_FETCH_ADD + return __atomic_fetch_add(&a->value, v, __ATOMIC_SEQ_CST); +#elif defined(HAVE_MSC_ATOMICS) SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); return _InterlockedExchangeAdd((long *)&a->value, v); #elif defined(HAVE_WATCOM_ATOMICS) @@ -298,9 +408,23 @@ int SDL_AddAtomicInt(SDL_AtomicInt *a, int v) #endif } +int SDL_AddRelaxedAtomicInt(SDL_AtomicInt *a, int v) +{ +#ifdef HAVE_ATOMIC_FETCH_ADD + return __atomic_fetch_add(&a->value, v, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); + return _InterlockedExchangeAdd_nf((long *)&a->value, v); +#else + return SDL_AddAtomicInt(a, v); +#endif +} + Uint32 SDL_AddAtomicU32(SDL_AtomicU32 *a, int v) { -#ifdef HAVE_MSC_ATOMICS +#ifdef HAVE_ATOMIC_FETCH_ADD + return __atomic_fetch_add(&a->value, v, __ATOMIC_SEQ_CST); +#elif defined(HAVE_MSC_ATOMICS) SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); return (Uint32)_InterlockedExchangeAdd((long *)&a->value, v); #elif defined(HAVE_WATCOM_ATOMICS) @@ -322,6 +446,18 @@ Uint32 SDL_AddAtomicU32(SDL_AtomicU32 *a, int v) #endif } +Uint32 SDL_AddRelaxedAtomicU32(SDL_AtomicU32 *a, int v) +{ +#ifdef HAVE_ATOMIC_FETCH_ADD + return __atomic_fetch_add(&a->value, v, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); + return _InterlockedExchangeAdd_nf((long *)&a->value, v); +#else + return SDL_AddAtomicU32(a, v); +#endif +} + int SDL_GetAtomicInt(SDL_AtomicInt *a) { #ifdef HAVE_ATOMIC_LOAD_N @@ -346,6 +482,18 @@ int SDL_GetAtomicInt(SDL_AtomicInt *a) #endif } +int SDL_GetRelaxedAtomicInt(SDL_AtomicInt *a) +{ +#ifdef HAVE_ATOMIC_LOAD_N + return __atomic_load_n(&a->value, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); + return _InterlockedOr_nf((long *)&a->value, 0); +#else + return SDL_GetAtomicInt(a); +#endif +} + Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a) { #ifdef HAVE_ATOMIC_LOAD_N @@ -372,6 +520,18 @@ Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a) #endif } +Uint32 SDL_GetRelaxedAtomicU32(SDL_AtomicU32 *a) +{ +#ifdef HAVE_ATOMIC_LOAD_N + return __atomic_load_n(&a->value, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); + return (Uint32)_InterlockedOr_nf((long *)&a->value, 0); +#else + return SDL_GetAtomicU32(a); +#endif +} + void *SDL_GetAtomicPointer(void **a) { #ifdef HAVE_ATOMIC_LOAD_N @@ -391,6 +551,17 @@ void *SDL_GetAtomicPointer(void **a) #endif } +void *SDL_GetRelaxedAtomicPointer(void **a) +{ +#ifdef HAVE_ATOMIC_LOAD_N + return __atomic_load_n(a, __ATOMIC_RELAXED); +#elif defined(HAVE_MSC_NF_ATOMICS) + return _InterlockedCompareExchangePointer_nf(a, NULL, NULL); +#else + return SDL_GetAtomicPointer(a); +#endif +} + #ifdef SDL_MEMORY_BARRIER_USES_FUNCTION #error This file should be built in arm mode so the mcr instruction is available for memory barriers #endif diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 0e4b0aa6d89a3..df91e67c6a488 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1257,6 +1257,17 @@ SDL3_0.0.0 { SDL_hid_get_properties; SDL_GetPixelFormatFromGPUTextureFormat; SDL_GetGPUTextureFormatFromPixelFormat; + SDL_CompareAndSwapRelaxedAtomicInt; + SDL_SetRelaxedAtomicInt; + SDL_GetRelaxedAtomicInt; + SDL_AddRelaxedAtomicInt; + SDL_CompareAndSwapRelaxedAtomicU32; + SDL_SetRelaxedAtomicU32; + SDL_GetRelaxedAtomicU32; + SDL_AddRelaxedAtomicU32; + SDL_CompareAndSwapRelaxedAtomicPointer; + SDL_SetRelaxedAtomicPointer; + SDL_GetRelaxedAtomicPointer; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 686e5472ed930..1dead5e55a0d4 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1282,3 +1282,14 @@ #define SDL_hid_get_properties SDL_hid_get_properties_REAL #define SDL_GetPixelFormatFromGPUTextureFormat SDL_GetPixelFormatFromGPUTextureFormat_REAL #define SDL_GetGPUTextureFormatFromPixelFormat SDL_GetGPUTextureFormatFromPixelFormat_REAL +#define SDL_CompareAndSwapRelaxedAtomicInt SDL_CompareAndSwapRelaxedAtomicInt_REAL +#define SDL_SetRelaxedAtomicInt SDL_SetRelaxedAtomicInt_REAL +#define SDL_GetRelaxedAtomicInt SDL_GetRelaxedAtomicInt_REAL +#define SDL_AddRelaxedAtomicInt SDL_AddRelaxedAtomicInt_REAL +#define SDL_CompareAndSwapRelaxedAtomicU32 SDL_CompareAndSwapRelaxedAtomicU32_REAL +#define SDL_SetRelaxedAtomicU32 SDL_SetRelaxedAtomicU32_REAL +#define SDL_GetRelaxedAtomicU32 SDL_GetRelaxedAtomicU32_REAL +#define SDL_AddRelaxedAtomicU32 SDL_AddRelaxedAtomicU32_REAL +#define SDL_CompareAndSwapRelaxedAtomicPointer SDL_CompareAndSwapRelaxedAtomicPointer_REAL +#define SDL_SetRelaxedAtomicPointer SDL_SetRelaxedAtomicPointer_REAL +#define SDL_GetRelaxedAtomicPointer SDL_GetRelaxedAtomicPointer_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 8516bfe26a0b7..88511a11b71ba 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1290,3 +1290,14 @@ SDL_DYNAPI_PROC(Uint32,SDL_AddAtomicU32,(SDL_AtomicU32 *a,int b),(a,b),return) SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_hid_get_properties,(SDL_hid_device *a),(a),return) SDL_DYNAPI_PROC(SDL_PixelFormat,SDL_GetPixelFormatFromGPUTextureFormat,(SDL_GPUTextureFormat a),(a),return) SDL_DYNAPI_PROC(SDL_GPUTextureFormat,SDL_GetGPUTextureFormatFromPixelFormat,(SDL_PixelFormat a),(a),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapRelaxedAtomicInt,(SDL_AtomicInt *a,int b,int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_SetRelaxedAtomicInt,(SDL_AtomicInt *a,int b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GetRelaxedAtomicInt,(SDL_AtomicInt *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_AddRelaxedAtomicInt,(SDL_AtomicInt *a,int b),(a,b),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapRelaxedAtomicU32,(SDL_AtomicU32 *a,Uint32 b,Uint32 c),(a,b,c),return) +SDL_DYNAPI_PROC(Uint32,SDL_SetRelaxedAtomicU32,(SDL_AtomicU32 *a,Uint32 b),(a,b),return) +SDL_DYNAPI_PROC(Uint32,SDL_GetRelaxedAtomicU32,(SDL_AtomicU32 *a),(a),return) +SDL_DYNAPI_PROC(Uint32,SDL_AddRelaxedAtomicU32,(SDL_AtomicU32 *a,int b),(a,b),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapRelaxedAtomicPointer,(void **a,void *b,void *c),(a,b,c),return) +SDL_DYNAPI_PROC(void*,SDL_SetRelaxedAtomicPointer,(void **a,void *b),(a,b),return) +SDL_DYNAPI_PROC(void*,SDL_GetRelaxedAtomicPointer,(void **a),(a),return) From d56a50d43583bb63436494b9c58e59608d9e892e Mon Sep 17 00:00:00 2001 From: Maia Date: Mon, 15 Sep 2025 14:18:33 +0200 Subject: [PATCH 2/2] Ordered atomics --- include/SDL3/SDL_atomic.h | 220 ++++++++++++++++++----- src/atomic/SDL_atomic.c | 280 ++++++++++++++++++++++++------ src/dynapi/SDL_dynapi.sym | 25 +-- src/dynapi/SDL_dynapi_overrides.h | 25 +-- src/dynapi/SDL_dynapi_procs.h | 25 +-- 5 files changed, 441 insertions(+), 134 deletions(-) diff --git a/include/SDL3/SDL_atomic.h b/include/SDL3/SDL_atomic.h index d02728ec25a4a..09c92b7f66f86 100644 --- a/include/SDL3/SDL_atomic.h +++ b/include/SDL3/SDL_atomic.h @@ -365,6 +365,17 @@ typedef void (*SDL_KernelMemoryBarrierFunc)(); #endif +/** + * Memory ordering. TODO: document this + */ +typedef enum SDL_MemoryOrder { + SDL_MEMORY_ORDER_RELAXED, + SDL_MEMORY_ORDER_ACQUIRE, + SDL_MEMORY_ORDER_RELEASE, + SDL_MEMORY_ORDER_ACQ_REL, + SDL_MEMORY_ORDER_SEQ_CST, +} SDL_MemoryOrder; + /** * A type representing an atomic integer value. * @@ -395,7 +406,8 @@ typedef void (*SDL_KernelMemoryBarrierFunc)(); typedef struct SDL_AtomicInt { int value; } SDL_AtomicInt; /** - * Set an atomic variable to a new value if it is currently an old value. + * Set an atomic variable to a new value if it is currently an old value, + * using SEQ_CST memory ordering. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** @@ -409,6 +421,8 @@ typedef struct SDL_AtomicInt { int value; } SDL_AtomicInt; * * \since This function is available since SDL 3.2.0. * + * \sa SDL_CompareAndSwapAtomicIntStrongWithOrder + * \sa SDL_CompareAndSwapAtomicIntWeakWithOrder * \sa SDL_GetAtomicInt * \sa SDL_SetAtomicInt */ @@ -416,7 +430,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, i /** * Set an atomic variable to a new value if it is currently an old value, - * using relaxed memory ordering. + * using the provided memory ordering. If you use this is a loop, consider + * using SDL_CompareAndSwapAtomicIntWeakWithOrder instead. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** @@ -424,16 +439,48 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, i * \param a a pointer to an SDL_AtomicInt variable to be modified. * \param oldval the old value. * \param newval the new value. + * \param successord memory order to use on a successful compare and swap. + * \param failord memory order to use on a failed compare. May not be RELEASE, ACQ_REL, or stronger than successord. * \returns true if the atomic variable was set, false otherwise. * * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.4.0. * - * \sa SDL_GetRelaxedAtomicInt - * \sa SDL_SetRelaxedAtomicInt + * \sa SDL_CompareAndSwapAtomicInt + * \sa SDL_CompareAndSwapAtomicIntWeakWithOrder + * \sa SDL_GetAtomicIntWithOrder + * \sa SDL_SetAtomicIntWithOrder */ -extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicInt(SDL_AtomicInt *a, int oldval, int newval); +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicIntStrongWithOrder(SDL_AtomicInt *a, int oldval, int newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord); + +/** + * Set an atomic variable to a new value if it is currently an old value, + * using the provided memory ordering. This version can spuriously fail, + * but may be faster than SDL_CompareAndSwapAtomicIntStrongWithOrder in a loop. + * If SDL_CompareAndSwapAtomicIntStrongWithOrder wouldn't need a loop you should + * use that instead. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to an SDL_AtomicInt variable to be modified. + * \param oldval the old value. + * \param newval the new value. + * \param successord memory order to use on a successful compare and swap. + * \param failord memory order to use on a failed compare. May not be RELEASE, ACQ_REL, or stronger than successord. + * \returns true if the atomic variable was set, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CompareAndSwapAtomicInt + * \sa SDL_CompareAndSwapAtomicIntStrongWithOrder + * \sa SDL_GetAtomicIntWithOrder + * \sa SDL_SetAtomicIntWithOrder + */ +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicIntWeakWithOrder(SDL_AtomicInt *a, int oldval, int newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord); /** * Set an atomic variable to a value. @@ -456,22 +503,23 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicInt(SDL_AtomicIn extern SDL_DECLSPEC int SDLCALL SDL_SetAtomicInt(SDL_AtomicInt *a, int v); /** - * Set an atomic variable to a value using relaxed memory ordering. + * Set an atomic variable to a value using the provided memory ordering. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** * * \param a a pointer to an SDL_AtomicInt variable to be modified. * \param v the desired value. + * \param order memory order to use for the atomic exchange. * \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.4.0. * - * \sa SDL_GetRelaxedAtomicInt + * \sa SDL_GetAtomicIntWithOrder */ -extern SDL_DECLSPEC int SDLCALL SDL_SetRelaxedAtomicInt(SDL_AtomicInt *a, int v); +extern SDL_DECLSPEC int SDLCALL SDL_SetAtomicIntWithOrder(SDL_AtomicInt *a, int v, SDL_MemoryOrder order); /** * Get the value of an atomic variable. @@ -491,21 +539,22 @@ extern SDL_DECLSPEC int SDLCALL SDL_SetRelaxedAtomicInt(SDL_AtomicInt *a, int v) extern SDL_DECLSPEC int SDLCALL SDL_GetAtomicInt(SDL_AtomicInt *a); /** - * Get the value of an atomic variable using relaxed memory ordering. + * Get the value of an atomic variable using the provided memory ordering. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** * * \param a a pointer to an SDL_AtomicInt variable. + * \param order memory order to use for the atomic load. Must be one of RELAXED, ACQUIRE or SEQ_CST. * \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.4.0. * - * \sa SDL_SetRelaxedAtomicInt + * \sa SDL_SetAtomicIntWithOrder */ -extern SDL_DECLSPEC int SDLCALL SDL_GetRelaxedAtomicInt(SDL_AtomicInt *a); +extern SDL_DECLSPEC int SDLCALL SDL_GetAtomicIntWithOrder(SDL_AtomicInt *a, SDL_MemoryOrder order); /** * Add to an atomic variable. @@ -529,23 +578,24 @@ extern SDL_DECLSPEC int SDLCALL SDL_GetRelaxedAtomicInt(SDL_AtomicInt *a); extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicInt(SDL_AtomicInt *a, int v); /** - * Add to an atomic variable using relaxed memory ordering. + * Add to an atomic variable using the provided memory ordering. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** * * \param a a pointer to an SDL_AtomicInt variable to be modified. * \param v the desired value to add. + * \param order memory order to use for the atomic add. * \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.4.0. * - * \sa SDL_RelaxedAtomicDecRef - * \sa SDL_RelaxedAtomicIncRef + * \sa SDL_AtomicDecRefWithOrder + * \sa SDL_AtomicIncRefWithOrder */ -extern SDL_DECLSPEC int SDLCALL SDL_AddRelaxedAtomicInt(SDL_AtomicInt *a, int v); +extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicIntWithOrder(SDL_AtomicInt *a, int v, SDL_MemoryOrder order); #ifndef SDL_AtomicIncRef @@ -566,23 +616,24 @@ extern SDL_DECLSPEC int SDLCALL SDL_AddRelaxedAtomicInt(SDL_AtomicInt *a, int v) #define SDL_AtomicIncRef(a) SDL_AddAtomicInt(a, 1) #endif -#ifndef SDL_RelaxedAtomicIncRef +#ifndef SDL_AtomicIncRefWithOrder /** - * Increment an atomic variable used as a reference count, using relaxed memory ordering. + * Increment an atomic variable used as a reference count, using the provided memory ordering. * * ***Note: If you don't know what this macro is for, you shouldn't use it!*** * * \param a a pointer to an SDL_AtomicInt to increment. + * \param o memory order to use for the atomic increment. * \returns the previous value of the atomic variable. * * \threadsafety It is safe to call this macro from any thread. * * \since This macro is available since SDL 3.4.0. * - * \sa SDL_RelaxedAtomicDecRef + * \sa SDL_AtomicDecRefWithOrder */ -#define SDL_RelaxedAtomicIncRef(a) SDL_AddRelaxedAtomicInt(a, 1) +#define SDL_AtomicIncRefWithOrder(a, o) SDL_AddAtomicIntWithOrder(a, 1, o) #endif #ifndef SDL_AtomicDecRef @@ -605,14 +656,15 @@ extern SDL_DECLSPEC int SDLCALL SDL_AddRelaxedAtomicInt(SDL_AtomicInt *a, int v) #define SDL_AtomicDecRef(a) (SDL_AddAtomicInt(a, -1) == 1) #endif -#ifndef SDL_RelaxedAtomicDecRef +#ifndef SDL_AtomicDecRefWithOrder /** - * Decrement an atomic variable used as a reference count, using relaxed memory ordering. + * Decrement an atomic variable used as a reference count, using the provided memory ordering. * * ***Note: If you don't know what this macro is for, you shouldn't use it!*** * * \param a a pointer to an SDL_AtomicInt to decrement. + * \param o memory order to use for the atomic decrement. * \returns true if the variable reached zero after decrementing, false * otherwise. * @@ -622,7 +674,7 @@ extern SDL_DECLSPEC int SDLCALL SDL_AddRelaxedAtomicInt(SDL_AtomicInt *a, int v) * * \sa SDL_AtomicIncRef */ -#define SDL_RelaxedAtomicDecRef(a) (SDL_AddRelaxedAtomicInt(a, -1) == 1) +#define SDL_AtomicDecRefWithOrder(a, o) (SDL_AddAtomicIntWithOrder(a, -1, o) == 1) #endif /** @@ -668,6 +720,8 @@ typedef struct SDL_AtomicU32 { Uint32 value; } SDL_AtomicU32; * * \since This function is available since SDL 3.2.0. * + * \sa SDL_CompareAndSwapAtomicU32StrongWithOrder + * \sa SDL_CompareAndSwapAtomicU32WeakWithOrder * \sa SDL_GetAtomicU32 * \sa SDL_SetAtomicU32 */ @@ -675,7 +729,36 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, U /** * Set an atomic variable to a new value if it is currently an old value, - * using relaxed memory ordering. + * using the provided memory ordering. If you use this is a loop, consider + * using SDL_CompareAndSwapAtomicU32WeakWithOrder instead. + * + * ***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. + * \param successord memory order to use on a successful compare and swap. + * \param failord memory order to use on a failed compare. May not be RELEASE, ACQ_REL, or stronger than successord. + * \returns true if the atomic variable was set, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CompareAndSwapAtomicU32 + * \sa SDL_CompareAndSwapAtomicU32WeakWithOrder + * \sa SDL_GetAtomicU32WithOrder + * \sa SDL_SetAtomicU32WithOrder + */ +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicU32StrongWithOrder(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord); + +/** + * Set an atomic variable to a new value if it is currently an old value, + * using the provided memory ordering. This version can spuriously fail, + * but may be faster than SDL_CompareAndSwapAtomicU32StrongWithOrder in a loop. + * If SDL_CompareAndSwapAtomicU32StrongWithOrder wouldn't need a loop you should + * use that instead. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** @@ -683,16 +766,20 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, U * \param a a pointer to an SDL_AtomicU32 variable to be modified. * \param oldval the old value. * \param newval the new value. + * \param successord memory order to use on a successful compare and swap. + * \param failord memory order to use on a failed compare. May not be RELEASE, ACQ_REL, or stronger than successord. * \returns true if the atomic variable was set, false otherwise. * * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.4.0. * - * \sa SDL_GetRelaxedAtomicU32 - * \sa SDL_SetRelaxedAtomicU32 + * \sa SDL_CompareAndSwapAtomicU32 + * \sa SDL_CompareAndSwapAtomicU32StrongWithOrder + * \sa SDL_GetAtomicU32WithOrder + * \sa SDL_SetAtomicU32WithOrder */ -extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval); +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicU32WeakWithOrder(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord); /** * Set an atomic variable to a value. @@ -715,22 +802,23 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicU32(SDL_AtomicU3 extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v); /** - * Set an atomic variable to a value using relaxed memory ordering. + * Set an atomic variable to a value using the provided memory ordering. * * ***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. + * \param order memory order to use for the atomic exchange. * \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.4.0. * - * \sa SDL_GetRelaxedAtomicU32 + * \sa SDL_GetAtomicU32WithOrder */ -extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetRelaxedAtomicU32(SDL_AtomicU32 *a, Uint32 v); +extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetAtomicU32WithOrder(SDL_AtomicU32 *a, Uint32 v, SDL_MemoryOrder order); /** * Get the value of an atomic variable. @@ -750,21 +838,22 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetRelaxedAtomicU32(SDL_AtomicU32 *a, Uin extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetAtomicU32(SDL_AtomicU32 *a); /** - * Get the value of an atomic variable using relaxed memory ordering. + * Get the value of an atomic variable using the provided memory ordering. * * ***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. + * \param order memory order to use for the atomic load. Must be one of RELAXED, ACQUIRE or SEQ_CST. * \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.4.0. * - * \sa SDL_SetRelaxedAtomicU32 + * \sa SDL_SetAtomicU32WithOrder */ -extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetRelaxedAtomicU32(SDL_AtomicU32 *a); +extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetAtomicU32WithOrder(SDL_AtomicU32 *a, SDL_MemoryOrder order); /** * Add to an atomic variable. @@ -785,20 +874,21 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetRelaxedAtomicU32(SDL_AtomicU32 *a); extern SDL_DECLSPEC Uint32 SDLCALL SDL_AddAtomicU32(SDL_AtomicU32 *a, int v); /** - * Add to an atomic variable using relaxed memory ordering. + * Add to an atomic variable using the provided memory ordering. * * ***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 to add or subtract. + * \param order memory order to use for the atomic add. * \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.4.0. */ -extern SDL_DECLSPEC Uint32 SDLCALL SDL_AddRelaxedAtomicU32(SDL_AtomicU32 *a, int v); +extern SDL_DECLSPEC Uint32 SDLCALL SDL_AddAtomicU32WithOrder(SDL_AtomicU32 *a, int v, SDL_MemoryOrder order); /** * Set a pointer to a new value if it is currently an old value. @@ -815,6 +905,8 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_AddRelaxedAtomicU32(SDL_AtomicU32 *a, int * * \since This function is available since SDL 3.2.0. * + * \sa SDL_CompareAndSwapAtomicPointerStrongWithOrder + * \sa SDL_CompareAndSwapAtomicPointerWeakWithOrder * \sa SDL_CompareAndSwapAtomicInt * \sa SDL_GetAtomicPointer * \sa SDL_SetAtomicPointer @@ -823,7 +915,8 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicPointer(void **a, void /** * Set a pointer to a new value if it is currently an old value, - * using relaxed memory ordering. + * using the provided memory ordering. If you use this is a loop, consider + * using SDL_CompareAndSwapAtomicIntWeakWithOrder instead. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** @@ -831,17 +924,48 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicPointer(void **a, void * \param a a pointer to a pointer. * \param oldval the old pointer value. * \param newval the new pointer value. + * \param successord memory order to use on a successful compare and swap. + * \param failord memory order to use on a failed compare. May not be RELEASE, ACQ_REL, or stronger than successord. * \returns true if the pointer was set, false otherwise. * * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.4.0. * - * \sa SDL_CompareAndSwapRelaxedAtomicInt - * \sa SDL_GetRelaxedAtomicPointer - * \sa SDL_SetRelaxedAtomicPointer + * \sa SDL_CompareAndSwapAtomicPointer + * \sa SDL_CompareAndSwapAtomicPointerWeakWithOrder + * \sa SDL_GetAtomicPointerWithOrder + * \sa SDL_SetAtomicPointerWithOrder + */ +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicPointerStrongWithOrder(void **a, void *oldval, void *newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord); + +/** + * Set a pointer to a new value if it is currently an old value, + * using the provided memory ordering. This version can spuriously fail, + * but may be faster than SDL_CompareAndSwapAtomicPointerStrongWithOrder in a loop. + * If SDL_CompareAndSwapAtomicPointerStrongWithOrder wouldn't need a loop you should + * use that instead. + * + * ***Note: If you don't know what this function is for, you shouldn't use + * it!*** + * + * \param a a pointer to a pointer. + * \param oldval the old pointer value. + * \param newval the new pointer value. + * \param successord memory order to use on a successful compare and swap. + * \param failord memory order to use on a failed compare. May not be RELEASE, ACQ_REL, or stronger than successord. + * \returns true if the pointer was set, false otherwise. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CompareAndSwapAtomicPointer + * \sa SDL_CompareAndSwapAtomicPointerStrongWithOrder + * \sa SDL_GetAtomicPointerWithOrder + * \sa SDL_SetAtomicPointerWithOrder */ -extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicPointer(void **a, void *oldval, void *newval); +extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicPointerWeakWithOrder(void **a, void *oldval, void *newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord); /** * Set a pointer to a value atomically. @@ -863,23 +987,24 @@ extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapRelaxedAtomicPointer(void **a extern SDL_DECLSPEC void * SDLCALL SDL_SetAtomicPointer(void **a, void *v); /** - * Set a pointer to a value atomically using relaxed memory ordering. + * Set a pointer to a value atomically using the provided memory ordering. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** * * \param a a pointer to a pointer. * \param v the desired pointer value. + * \param order memory order to use for the atomic exchange. * \returns the previous value of the pointer. * * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.4.0. * - * \sa SDL_CompareAndSwapRelaxedAtomicPointer - * \sa SDL_GetRelaxedAtomicPointer + * \sa SDL_CompareAndSwapAtomicPointerWithOrder + * \sa SDL_GetAtomicPointerWithOrder */ -extern SDL_DECLSPEC void * SDLCALL SDL_SetRelaxedAtomicPointer(void **a, void *v); +extern SDL_DECLSPEC void * SDLCALL SDL_SetAtomicPointerWithOrder(void **a, void *v, SDL_MemoryOrder order); /** * Get the value of a pointer atomically. @@ -900,22 +1025,23 @@ extern SDL_DECLSPEC void * SDLCALL SDL_SetRelaxedAtomicPointer(void **a, void *v extern SDL_DECLSPEC void * SDLCALL SDL_GetAtomicPointer(void **a); /** - * Get the value of a pointer atomically using relaxed memory ordering. + * Get the value of a pointer atomically using the provided memory ordering. * * ***Note: If you don't know what this function is for, you shouldn't use * it!*** * * \param a a pointer to a pointer. + * \param order memory order to use for the atomic load. Must be one of RELAXED, ACQUIRE or SEQ_CST. * \returns the current value of a pointer. * * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.4.0. * - * \sa SDL_CompareAndSwapRelaxedAtomicPointer - * \sa SDL_SetRelaxedAtomicPointer + * \sa SDL_CompareAndSwapAtomicPointerWithOrder + * \sa SDL_SetAtomicPointerWithOrder */ -extern SDL_DECLSPEC void * SDLCALL SDL_GetRelaxedAtomicPointer(void **a); +extern SDL_DECLSPEC void * SDLCALL SDL_GetAtomicPointerWithOrder(void **a, SDL_MemoryOrder order); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/src/atomic/SDL_atomic.c b/src/atomic/SDL_atomic.c index 94308ed9df7e9..b2708842a1274 100644 --- a/src/atomic/SDL_atomic.c +++ b/src/atomic/SDL_atomic.c @@ -24,7 +24,7 @@ #include #define HAVE_MSC_ATOMICS 1 #if defined(_M_ARM) || defined(_M_ARM64) -#define HAVE_MSC_NF_ATOMICS 1 +#define HAVE_MSC_ORDERED_ATOMICS 1 #endif #endif @@ -148,6 +148,80 @@ static SDL_INLINE void leaveLock(void *a) } #endif +#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N +// __atomic ordering arguments should be compile time constants +#define COMPARE_AND_SWAP_INT_WITH_ORDER(value, oldval, newval, weak, successord, failord) \ + switch (successord) { \ + case SDL_MEMORY_ORDER_RELAXED: \ + switch (failord) { \ + case SDL_MEMORY_ORDER_RELAXED: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_RELAXED, __ATOMIC_RELAXED); \ + default: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ + } \ + case SDL_MEMORY_ORDER_ACQUIRE: \ + switch (failord) { \ + case SDL_MEMORY_ORDER_RELAXED: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED); \ + case SDL_MEMORY_ORDER_ACQUIRE: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE); \ + default: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ + } \ + case SDL_MEMORY_ORDER_RELEASE: \ + switch (failord) { \ + case SDL_MEMORY_ORDER_RELAXED: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_RELEASE, __ATOMIC_RELAXED); \ + default: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ + } \ + case SDL_MEMORY_ORDER_ACQ_REL: \ + switch (failord) { \ + case SDL_MEMORY_ORDER_RELAXED: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED); \ + case SDL_MEMORY_ORDER_ACQUIRE: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE); \ + default: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ + } \ + default: \ + switch (failord) { \ + case SDL_MEMORY_ORDER_RELAXED: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); \ + case SDL_MEMORY_ORDER_ACQUIRE: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE); \ + default: \ + return __atomic_compare_exchange_n(value, &oldval, newval, weak, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ + } \ + } +#define COMPARE_AND_SWAP_POINTER_WITH_ORDER(value, oldval, newval, weak, successord, failord) \ + COMPARE_AND_SWAP_INT_WITH_ORDER(value, oldval, newval, weak, successord, failord) +#elif defined(HAVE_MSC_ORDERED_ATOMICS) +#define COMPARE_AND_SWAP_INT_WITH_ORDER(value, oldval, newval, weak, successord, failord) \ + SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(*value)); \ + switch (successord) { \ + case SDL_MEMORY_ORDER_RELAXED: \ + return _InterlockedCompareExchange_nf((long *)(value), (long)(newval), (long)(oldval)) == (long)(oldval); \ + case SDL_MEMORY_ORDER_ACQUIRE: \ + return _InterlockedCompareExchange_acq((long *)(value), (long)(newval), (long)(oldval)) == (long)(oldval); \ + case SDL_MEMORY_ORDER_RELEASE: \ + return _InterlockedCompareExchange_rel((long *)(value), (long)(newval), (long)(oldval)) == (long)(oldval); \ + default: \ + return _InterlockedCompareExchange((long *)(value), (long)(newval), (long)(oldval)) == (long)(oldval); \ + } +#define COMPARE_AND_SWAP_POINTER_WITH_ORDER(value, oldval, newval, weak, successord, failord) \ + switch (successord) { \ + case SDL_MEMORY_ORDER_RELAXED: \ + return _InterlockedCompareExchangePointer_nf(value, newval, oldval) == oldval; \ + case SDL_MEMORY_ORDER_ACQUIRE: \ + return _InterlockedCompareExchangePointer_acq(value, newval, oldval) == oldval; \ + case SDL_MEMORY_ORDER_RELEASE: \ + return _InterlockedCompareExchangePointer_rel(value, newval, oldval) == oldval; \ + default: \ + return _InterlockedCompareExchangePointer(value, newval, oldval) == oldval; \ + } +#endif + bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval) { #ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N @@ -180,13 +254,19 @@ bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval) #endif } -bool SDL_CompareAndSwapRelaxedAtomicInt(SDL_AtomicInt *a, int oldval, int newval) +bool SDL_CompareAndSwapAtomicIntStrongWithOrder(SDL_AtomicInt *a, int oldval, int newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord) { -#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N - return __atomic_compare_exchange_n(&a->value, &oldval, newval, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value)); - return _InterlockedCompareExchange_nf((long *)&a->value, (long)newval, (long)oldval) == (long)oldval; +#ifdef COMPARE_AND_SWAP_INT_WITH_ORDER + COMPARE_AND_SWAP_INT_WITH_ORDER(&a->value, oldval, newval, false, successord, failord) +#else + return SDL_CompareAndSwapAtomicInt(a, oldval, newval); +#endif +} + +bool SDL_CompareAndSwapAtomicIntWeakWithOrder(SDL_AtomicInt *a, int oldval, int newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord) +{ +#ifdef COMPARE_AND_SWAP_INT_WITH_ORDER + COMPARE_AND_SWAP_INT_WITH_ORDER(&a->value, oldval, newval, true, successord, failord) #else return SDL_CompareAndSwapAtomicInt(a, oldval, newval); #endif @@ -225,13 +305,19 @@ bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval) #endif } -bool SDL_CompareAndSwapRelaxedAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval) +bool SDL_CompareAndSwapAtomicU32StrongWithOrder(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord) { -#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N - return __atomic_compare_exchange_n(&a->value, &oldval, newval, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value)); - return _InterlockedCompareExchange_nf((long *)&a->value, (long)newval, (long)oldval) == (long)oldval; +#ifdef COMPARE_AND_SWAP_INT_WITH_ORDER + COMPARE_AND_SWAP_INT_WITH_ORDER(&a->value, oldval, newval, false, successord, failord) +#else + return SDL_CompareAndSwapAtomicU32(a, oldval, newval); +#endif +} + +bool SDL_CompareAndSwapAtomicU32WeakWithOrder(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord) +{ +#ifdef COMPARE_AND_SWAP_INT_WITH_ORDER + COMPARE_AND_SWAP_INT_WITH_ORDER(&a->value, oldval, newval, true, successord, failord) #else return SDL_CompareAndSwapAtomicU32(a, oldval, newval); #endif @@ -269,12 +355,19 @@ bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval) #endif } -bool SDL_CompareAndSwapRelaxedAtomicPointer(void **a, void *oldval, void *newval) +bool SDL_CompareAndSwapAtomicPointerStrongWithOrder(void **a, void *oldval, void *newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord) { -#ifdef HAVE_ATOMIC_COMPARE_EXCHANGE_N - return __atomic_compare_exchange_n(a, &oldval, newval, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - return _InterlockedCompareExchangePointer_nf(a, newval, oldval) == oldval; +#ifdef COMPARE_AND_SWAP_POINTER_WITH_ORDER + COMPARE_AND_SWAP_POINTER_WITH_ORDER(a, oldval, newval, false, successord, failord) +#else + return SDL_CompareAndSwapAtomicPointer(a, oldval, newval); +#endif +} + +bool SDL_CompareAndSwapAtomicPointerWeakWithOrder(void **a, void *oldval, void *newval, SDL_MemoryOrder successord, SDL_MemoryOrder failord) +{ +#ifdef COMPARE_AND_SWAP_POINTER_WITH_ORDER + COMPARE_AND_SWAP_POINTER_WITH_ORDER(a, oldval, newval, true, successord, failord) #else return SDL_CompareAndSwapAtomicPointer(a, oldval, newval); #endif @@ -303,13 +396,24 @@ int SDL_SetAtomicInt(SDL_AtomicInt *a, int v) #endif } -int SDL_SetRelaxedAtomicInt(SDL_AtomicInt *a, int v) +int SDL_SetAtomicIntWithOrder(SDL_AtomicInt *a, int v, SDL_MemoryOrder order) { #ifdef HAVE_ATOMIC_EXCHANGE_N - return __atomic_exchange_n(&a->value, v, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return __atomic_exchange_n(&a->value, v, __ATOMIC_RELAXED); + case SDL_MEMORY_ORDER_ACQUIRE: return __atomic_exchange_n(&a->value, v, __ATOMIC_ACQUIRE); + case SDL_MEMORY_ORDER_RELEASE: return __atomic_exchange_n(&a->value, v, __ATOMIC_RELEASE); + case SDL_MEMORY_ORDER_ACQ_REL: return __atomic_exchange_n(&a->value, v, __ATOMIC_ACQ_REL); + default: return __atomic_exchange_n(&a->value, v, __ATOMIC_SEQ_CST); + } +#elif defined(HAVE_MSC_ORDERED_ATOMICS) SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); - return _InterlockedExchange_nf((long *)&a->value, v); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return _InterlockedExchange_nf((long *)&a->value, v); + case SDL_MEMORY_ORDER_ACQUIRE: return _InterlockedExchange_acq((long *)&a->value, v); + case SDL_MEMORY_ORDER_RELEASE: return _InterlockedExchange_rel((long *)&a->value, v); + default: return _InterlockedExchange((long *)&a->value, v); + } #else return SDL_SetAtomicInt(a, v); #endif @@ -338,13 +442,24 @@ Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v) #endif } -Uint32 SDL_SetRelaxedAtomicU32(SDL_AtomicU32 *a, Uint32 v) +Uint32 SDL_SetAtomicU32WithOrder(SDL_AtomicU32 *a, Uint32 v, SDL_MemoryOrder order) { #ifdef HAVE_ATOMIC_EXCHANGE_N - return __atomic_exchange_n(&a->value, v, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return __atomic_exchange_n(&a->value, v, __ATOMIC_RELAXED); + case SDL_MEMORY_ORDER_ACQUIRE: return __atomic_exchange_n(&a->value, v, __ATOMIC_ACQUIRE); + case SDL_MEMORY_ORDER_RELEASE: return __atomic_exchange_n(&a->value, v, __ATOMIC_RELEASE); + case SDL_MEMORY_ORDER_ACQ_REL: return __atomic_exchange_n(&a->value, v, __ATOMIC_ACQ_REL); + default: return __atomic_exchange_n(&a->value, v, __ATOMIC_SEQ_CST); + } +#elif defined(HAVE_MSC_ORDERED_ATOMICS) SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); - return _InterlockedExchange_nf((long *)&a->value, v); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return _InterlockedExchange_nf((long *)&a->value, v); + case SDL_MEMORY_ORDER_ACQUIRE: return _InterlockedExchange_acq((long *)&a->value, v); + case SDL_MEMORY_ORDER_RELEASE: return _InterlockedExchange_rel((long *)&a->value, v); + default: return _InterlockedExchange((long *)&a->value, v); + } #else return SDL_SetAtomicU32(a, v); #endif @@ -371,12 +486,23 @@ void *SDL_SetAtomicPointer(void **a, void *v) #endif } -void *SDL_SetRelaxedAtomicPointer(void **a, void *v) +void *SDL_SetAtomicPointerWithOrder(void **a, void *v, SDL_MemoryOrder order) { #ifdef HAVE_ATOMIC_EXCHANGE_N - return __atomic_exchange_n(a, v, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - return _InterlockedExchangePointer_nf(a, v); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return __atomic_exchange_n(a, v, __ATOMIC_RELAXED); + case SDL_MEMORY_ORDER_ACQUIRE: return __atomic_exchange_n(a, v, __ATOMIC_ACQUIRE); + case SDL_MEMORY_ORDER_RELEASE: return __atomic_exchange_n(a, v, __ATOMIC_RELEASE); + case SDL_MEMORY_ORDER_ACQ_REL: return __atomic_exchange_n(a, v, __ATOMIC_ACQ_REL); + default: return __atomic_exchange_n(a, v, __ATOMIC_SEQ_CST); + } +#elif defined(HAVE_MSC_ORDERED_ATOMICS) + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return _InterlockedExchangePointer_nf(a, v); + case SDL_MEMORY_ORDER_ACQUIRE: return _InterlockedExchangePointer_acq(a, v); + case SDL_MEMORY_ORDER_RELEASE: return _InterlockedExchangePointer_rel(a, v); + default: return _InterlockedExchangePointer(a, v); + } #else return SDL_SetAtomicPointer(a, v); #endif @@ -408,13 +534,24 @@ int SDL_AddAtomicInt(SDL_AtomicInt *a, int v) #endif } -int SDL_AddRelaxedAtomicInt(SDL_AtomicInt *a, int v) +int SDL_AddAtomicIntWithOrder(SDL_AtomicInt *a, int v, SDL_MemoryOrder order) { #ifdef HAVE_ATOMIC_FETCH_ADD - return __atomic_fetch_add(&a->value, v, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); - return _InterlockedExchangeAdd_nf((long *)&a->value, v); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return __atomic_fetch_add(&a->value, v, __ATOMIC_RELAXED); + case SDL_MEMORY_ORDER_ACQUIRE: return __atomic_fetch_add(&a->value, v, __ATOMIC_ACQUIRE); + case SDL_MEMORY_ORDER_RELEASE: return __atomic_fetch_add(&a->value, v, __ATOMIC_RELEASE); + case SDL_MEMORY_ORDER_ACQ_REL: return __atomic_fetch_add(&a->value, v, __ATOMIC_ACQ_REL); + default: return __atomic_fetch_add(&a->value, v, __ATOMIC_SEQ_CST); + } +#elif defined(HAVE_MSC_ORDERED_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return _InterlockedExchangeAdd_nf((long *)&a->value, v); + case SDL_MEMORY_ORDER_ACQUIRE: return _InterlockedExchangeAdd_acq((long *)&a->value, v); + case SDL_MEMORY_ORDER_RELEASE: return _InterlockedExchangeAdd_rel((long *)&a->value, v); + default: return _InterlockedExchangeAdd((long *)&a->value, v); + } #else return SDL_AddAtomicInt(a, v); #endif @@ -446,13 +583,24 @@ Uint32 SDL_AddAtomicU32(SDL_AtomicU32 *a, int v) #endif } -Uint32 SDL_AddRelaxedAtomicU32(SDL_AtomicU32 *a, int v) +Uint32 SDL_AddAtomicU32WithOrder(SDL_AtomicU32 *a, int v, SDL_MemoryOrder order) { #ifdef HAVE_ATOMIC_FETCH_ADD - return __atomic_fetch_add(&a->value, v, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value)); - return _InterlockedExchangeAdd_nf((long *)&a->value, v); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return __atomic_fetch_add(&a->value, v, __ATOMIC_RELAXED); + case SDL_MEMORY_ORDER_ACQUIRE: return __atomic_fetch_add(&a->value, v, __ATOMIC_ACQUIRE); + case SDL_MEMORY_ORDER_RELEASE: return __atomic_fetch_add(&a->value, v, __ATOMIC_RELEASE); + case SDL_MEMORY_ORDER_ACQ_REL: return __atomic_fetch_add(&a->value, v, __ATOMIC_ACQ_REL); + default: return __atomic_fetch_add(&a->value, v, __ATOMIC_SEQ_CST); + } +#elif defined(HAVE_MSC_ORDERED_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return _InterlockedExchangeAdd_nf((long *)&a->value, v); + case SDL_MEMORY_ORDER_ACQUIRE: return _InterlockedExchangeAdd_acq((long *)&a->value, v); + case SDL_MEMORY_ORDER_RELEASE: return _InterlockedExchangeAdd_rel((long *)&a->value, v); + default: return _InterlockedExchangeAdd((long *)&a->value, v); + } #else return SDL_AddAtomicU32(a, v); #endif @@ -482,13 +630,21 @@ int SDL_GetAtomicInt(SDL_AtomicInt *a) #endif } -int SDL_GetRelaxedAtomicInt(SDL_AtomicInt *a) +int SDL_GetAtomicIntWithOrder(SDL_AtomicInt *a, SDL_MemoryOrder order) { #ifdef HAVE_ATOMIC_LOAD_N - return __atomic_load_n(&a->value, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); - return _InterlockedOr_nf((long *)&a->value, 0); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return __atomic_load_n(&a->value, __ATOMIC_RELAXED); + case SDL_MEMORY_ORDER_ACQUIRE: return __atomic_load_n(&a->value, __ATOMIC_ACQUIRE); + default: return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); + } +#elif defined(HAVE_MSC_ORDERED_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return _InterlockedOr_nf((long *)&a->value, 0); + case SDL_MEMORY_ORDER_ACQUIRE: return _InterlockedOr_acq((long *)&a->value, 0); + default: return _InterlockedOr((long *)&a->value, 0); + } #else return SDL_GetAtomicInt(a); #endif @@ -520,13 +676,21 @@ Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a) #endif } -Uint32 SDL_GetRelaxedAtomicU32(SDL_AtomicU32 *a) +Uint32 SDL_GetAtomicU32WithOrder(SDL_AtomicU32 *a, SDL_MemoryOrder order) { #ifdef HAVE_ATOMIC_LOAD_N - return __atomic_load_n(&a->value, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value)); - return (Uint32)_InterlockedOr_nf((long *)&a->value, 0); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return __atomic_load_n(&a->value, __ATOMIC_RELAXED); + case SDL_MEMORY_ORDER_ACQUIRE: return __atomic_load_n(&a->value, __ATOMIC_ACQUIRE); + default: return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST); + } +#elif defined(HAVE_MSC_ORDERED_ATOMICS) + SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value)); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return _InterlockedOr_nf((long *)&a->value, 0); + case SDL_MEMORY_ORDER_ACQUIRE: return _InterlockedOr_acq((long *)&a->value, 0); + default: return _InterlockedOr((long *)&a->value, 0); + } #else return SDL_GetAtomicU32(a); #endif @@ -551,12 +715,20 @@ void *SDL_GetAtomicPointer(void **a) #endif } -void *SDL_GetRelaxedAtomicPointer(void **a) +void *SDL_GetAtomicPointerWithOrder(void **a, SDL_MemoryOrder order) { #ifdef HAVE_ATOMIC_LOAD_N - return __atomic_load_n(a, __ATOMIC_RELAXED); -#elif defined(HAVE_MSC_NF_ATOMICS) - return _InterlockedCompareExchangePointer_nf(a, NULL, NULL); + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return __atomic_load_n(a, __ATOMIC_RELAXED); + case SDL_MEMORY_ORDER_ACQUIRE: return __atomic_load_n(a, __ATOMIC_ACQUIRE); + default: return __atomic_load_n(a, __ATOMIC_SEQ_CST); + } +#elif defined(HAVE_MSC_ORDERED_ATOMICS) + switch (order) { + case SDL_MEMORY_ORDER_RELAXED: return _InterlockedCompareExchangePointer_nf(a, NULL, NULL); + case SDL_MEMORY_ORDER_ACQUIRE: return _InterlockedCompareExchangePointer_acq(a, NULL, NULL); + default: return _InterlockedCompareExchangePointer(a, NULL, NULL); + } #else return SDL_GetAtomicPointer(a); #endif diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index df91e67c6a488..5ef677ec4e43b 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1257,17 +1257,20 @@ SDL3_0.0.0 { SDL_hid_get_properties; SDL_GetPixelFormatFromGPUTextureFormat; SDL_GetGPUTextureFormatFromPixelFormat; - SDL_CompareAndSwapRelaxedAtomicInt; - SDL_SetRelaxedAtomicInt; - SDL_GetRelaxedAtomicInt; - SDL_AddRelaxedAtomicInt; - SDL_CompareAndSwapRelaxedAtomicU32; - SDL_SetRelaxedAtomicU32; - SDL_GetRelaxedAtomicU32; - SDL_AddRelaxedAtomicU32; - SDL_CompareAndSwapRelaxedAtomicPointer; - SDL_SetRelaxedAtomicPointer; - SDL_GetRelaxedAtomicPointer; + SDL_CompareAndSwapAtomicIntStrongWithOrder; + SDL_CompareAndSwapAtomicIntWeakWithOrder; + SDL_SetAtomicIntWithOrder; + SDL_GetAtomicIntWithOrder; + SDL_AddAtomicIntWithOrder; + SDL_CompareAndSwapAtomicU32StrongWithOrder; + SDL_CompareAndSwapAtomicU32WeakWithOrder; + SDL_SetAtomicU32WithOrder; + SDL_GetAtomicU32WithOrder; + SDL_AddAtomicU32WithOrder; + SDL_CompareAndSwapAtomicPointerStrongWithOrder; + SDL_CompareAndSwapAtomicPointerWeakWithOrder; + SDL_SetAtomicPointerWithOrder; + SDL_GetAtomicPointerWithOrder; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 1dead5e55a0d4..4b493c87d8c73 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1282,14 +1282,17 @@ #define SDL_hid_get_properties SDL_hid_get_properties_REAL #define SDL_GetPixelFormatFromGPUTextureFormat SDL_GetPixelFormatFromGPUTextureFormat_REAL #define SDL_GetGPUTextureFormatFromPixelFormat SDL_GetGPUTextureFormatFromPixelFormat_REAL -#define SDL_CompareAndSwapRelaxedAtomicInt SDL_CompareAndSwapRelaxedAtomicInt_REAL -#define SDL_SetRelaxedAtomicInt SDL_SetRelaxedAtomicInt_REAL -#define SDL_GetRelaxedAtomicInt SDL_GetRelaxedAtomicInt_REAL -#define SDL_AddRelaxedAtomicInt SDL_AddRelaxedAtomicInt_REAL -#define SDL_CompareAndSwapRelaxedAtomicU32 SDL_CompareAndSwapRelaxedAtomicU32_REAL -#define SDL_SetRelaxedAtomicU32 SDL_SetRelaxedAtomicU32_REAL -#define SDL_GetRelaxedAtomicU32 SDL_GetRelaxedAtomicU32_REAL -#define SDL_AddRelaxedAtomicU32 SDL_AddRelaxedAtomicU32_REAL -#define SDL_CompareAndSwapRelaxedAtomicPointer SDL_CompareAndSwapRelaxedAtomicPointer_REAL -#define SDL_SetRelaxedAtomicPointer SDL_SetRelaxedAtomicPointer_REAL -#define SDL_GetRelaxedAtomicPointer SDL_GetRelaxedAtomicPointer_REAL +#define SDL_CompareAndSwapAtomicIntStrongWithOrder SDL_CompareAndSwapAtomicIntStrongWithOrder_REAL +#define SDL_CompareAndSwapAtomicIntWeakWithOrder SDL_CompareAndSwapAtomicIntWeakWithOrder_REAL +#define SDL_SetAtomicIntWithOrder SDL_SetAtomicIntWithOrder_REAL +#define SDL_GetAtomicIntWithOrder SDL_GetAtomicIntWithOrder_REAL +#define SDL_AddAtomicIntWithOrder SDL_AddAtomicIntWithOrder_REAL +#define SDL_CompareAndSwapAtomicU32StrongWithOrder SDL_CompareAndSwapAtomicU32StrongWithOrder_REAL +#define SDL_CompareAndSwapAtomicU32WeakWithOrder SDL_CompareAndSwapAtomicU32WeakWithOrder_REAL +#define SDL_SetAtomicU32WithOrder SDL_SetAtomicU32WithOrder_REAL +#define SDL_GetAtomicU32WithOrder SDL_GetAtomicU32WithOrder_REAL +#define SDL_AddAtomicU32WithOrder SDL_AddAtomicU32WithOrder_REAL +#define SDL_CompareAndSwapAtomicPointerStrongWithOrder SDL_CompareAndSwapAtomicPointerStrongWithOrder_REAL +#define SDL_CompareAndSwapAtomicPointerWeakWithOrder SDL_CompareAndSwapAtomicPointerWeakWithOrder_REAL +#define SDL_SetAtomicPointerWithOrder SDL_SetAtomicPointerWithOrder_REAL +#define SDL_GetAtomicPointerWithOrder SDL_GetAtomicPointerWithOrder_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 88511a11b71ba..3d158e29b80f5 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1290,14 +1290,17 @@ SDL_DYNAPI_PROC(Uint32,SDL_AddAtomicU32,(SDL_AtomicU32 *a,int b),(a,b),return) SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_hid_get_properties,(SDL_hid_device *a),(a),return) SDL_DYNAPI_PROC(SDL_PixelFormat,SDL_GetPixelFormatFromGPUTextureFormat,(SDL_GPUTextureFormat a),(a),return) SDL_DYNAPI_PROC(SDL_GPUTextureFormat,SDL_GetGPUTextureFormatFromPixelFormat,(SDL_PixelFormat a),(a),return) -SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapRelaxedAtomicInt,(SDL_AtomicInt *a,int b,int c),(a,b,c),return) -SDL_DYNAPI_PROC(int,SDL_SetRelaxedAtomicInt,(SDL_AtomicInt *a,int b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_GetRelaxedAtomicInt,(SDL_AtomicInt *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_AddRelaxedAtomicInt,(SDL_AtomicInt *a,int b),(a,b),return) -SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapRelaxedAtomicU32,(SDL_AtomicU32 *a,Uint32 b,Uint32 c),(a,b,c),return) -SDL_DYNAPI_PROC(Uint32,SDL_SetRelaxedAtomicU32,(SDL_AtomicU32 *a,Uint32 b),(a,b),return) -SDL_DYNAPI_PROC(Uint32,SDL_GetRelaxedAtomicU32,(SDL_AtomicU32 *a),(a),return) -SDL_DYNAPI_PROC(Uint32,SDL_AddRelaxedAtomicU32,(SDL_AtomicU32 *a,int b),(a,b),return) -SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapRelaxedAtomicPointer,(void **a,void *b,void *c),(a,b,c),return) -SDL_DYNAPI_PROC(void*,SDL_SetRelaxedAtomicPointer,(void **a,void *b),(a,b),return) -SDL_DYNAPI_PROC(void*,SDL_GetRelaxedAtomicPointer,(void **a),(a),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapAtomicIntStrongWithOrder,(SDL_AtomicInt *a,int b,int c,SDL_MemoryOrder d,SDL_MemoryOrder e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapAtomicIntWeakWithOrder,(SDL_AtomicInt *a,int b,int c,SDL_MemoryOrder d,SDL_MemoryOrder e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(int,SDL_SetAtomicIntWithOrder,(SDL_AtomicInt *a,int b,SDL_MemoryOrder c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetAtomicIntWithOrder,(SDL_AtomicInt *a,SDL_MemoryOrder b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_AddAtomicIntWithOrder,(SDL_AtomicInt *a,int b,SDL_MemoryOrder c),(a,b,c),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapAtomicU32StrongWithOrder,(SDL_AtomicU32 *a,Uint32 b,Uint32 c,SDL_MemoryOrder d,SDL_MemoryOrder e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapAtomicU32WeakWithOrder,(SDL_AtomicU32 *a,Uint32 b,Uint32 c,SDL_MemoryOrder d,SDL_MemoryOrder e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(Uint32,SDL_SetAtomicU32WithOrder,(SDL_AtomicU32 *a,Uint32 b,SDL_MemoryOrder c),(a,b,c),return) +SDL_DYNAPI_PROC(Uint32,SDL_GetAtomicU32WithOrder,(SDL_AtomicU32 *a,SDL_MemoryOrder b),(a,b),return) +SDL_DYNAPI_PROC(Uint32,SDL_AddAtomicU32WithOrder,(SDL_AtomicU32 *a,int b,SDL_MemoryOrder c),(a,b,c),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapAtomicPointerStrongWithOrder,(void **a,void *b,void *c,SDL_MemoryOrder d,SDL_MemoryOrder e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(bool,SDL_CompareAndSwapAtomicPointerWeakWithOrder,(void **a,void *b,void *c,SDL_MemoryOrder d,SDL_MemoryOrder e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(void*,SDL_SetAtomicPointerWithOrder,(void **a,void *b,SDL_MemoryOrder c),(a,b,c),return) +SDL_DYNAPI_PROC(void*,SDL_GetAtomicPointerWithOrder,(void **a,SDL_MemoryOrder b),(a,b),return)