Skip to content

Commit

Permalink
Atomic operations. (#985)
Browse files Browse the repository at this point in the history
* New atomic functions.

* Use MSVC intrinsics, instead of Win32 calls.

* Use the new _hx_atomic* functions instead of the old HxAtomic*.

* Fix some casts.

* Use volatile int.

* More volatile.

* Fix more C++ casting stuff.

* More C++ casting stuff.

* Try fixing things some more.

* Fix typo.

* Add non-atomic fallback instead of erroring.
  • Loading branch information
Apprentice-Alchemist authored Nov 21, 2022
1 parent ae8cbc5 commit aebf08f
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 145 deletions.
148 changes: 148 additions & 0 deletions include/hx/StdLibs.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,154 @@ bool _hx_atomic_exchange_if(::cpp::Pointer<cpp::AtomicInt> inPtr, int test, int
int _hx_atomic_inc(::cpp::Pointer<cpp::AtomicInt> inPtr );
int _hx_atomic_dec(::cpp::Pointer<cpp::AtomicInt> inPtr );

// Assumptions made:
// People are not using 8 year old versions of GCC.

#if defined(__GNUC__) || defined(__clang__)
#define HX_GCC_ATOMICS
#define HX_HAS_ATOMIC 1
#elif defined(_MSC_VER)
#define HX_MSVC_ATOMICS
#define HX_HAS_ATOMIC 1
#include <intrin.h>
#else
#define HX_HAS_ATOMIC 0
#endif

inline int _hx_atomic_add(volatile int *a, int b) {
#if defined(HX_GCC_ATOMICS)
return __atomic_fetch_add(a, b, __ATOMIC_SEQ_CST);
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedExchangeAdd((long volatile *)a, b);
#else
int old = *a;
*a += b;
return old;
#endif
}

inline int _hx_atomic_sub(volatile int *a, int b) {
#if defined(HX_GCC_ATOMICS)
return __atomic_fetch_sub(a, b, __ATOMIC_SEQ_CST);
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedExchangeAdd((long volatile *)a, -b);
#else
int old = *a;
*a -= b;
return old;
#endif
}

inline int _hx_atomic_and(volatile int *a, int b) {
#if defined(HX_GCC_ATOMICS)
return __atomic_fetch_and(a, b, __ATOMIC_SEQ_CST);
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedAnd((long volatile *)a, b);
#else
int old = *a;
*a &= b;
return old;
#endif
}

inline int _hx_atomic_or(volatile int *a, int b) {
#if defined(HX_GCC_ATOMICS)
return __atomic_fetch_or(a, b, __ATOMIC_SEQ_CST);
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedOr((long volatile *)a, b);
#else
int old = *a;
*a |= b;
return old;
#endif
}

inline int _hx_atomic_xor(int *a, int b) {
#if defined(HX_GCC_ATOMICS)
return __atomic_fetch_xor(a, b, __ATOMIC_SEQ_CST);
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedXor((long volatile *)a, b);
#else
int old = *a;
*a ^= b;
return old;
#endif
}

inline int _hx_atomic_compare_exchange(volatile int *a, int expected,
int replacement) {
#if defined(HX_GCC_ATOMICS)
int _expected = expected;
__atomic_compare_exchange(a, &_expected, &replacement, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
return _expected;
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedCompareExchange((long volatile *)a, replacement, expected);
#else
int old = *a;
if(old == expected) {
*a = replacement;
}
return old;
#endif
}

inline int _hx_atomic_exchange(volatile int *a, int replacement) {
#if defined(HX_GCC_ATOMICS)
int ret = 0;
__atomic_exchange(a, &replacement, &ret, __ATOMIC_SEQ_CST);
return ret;
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedExchange((long volatile *)a, replacement);
#else
int old = *a;
*a = replacement;
return old;
#endif
}

inline int _hx_atomic_load(volatile int *a) {
#if defined(HX_GCC_ATOMICS)
int ret = 0;
__atomic_load(a, &ret, __ATOMIC_SEQ_CST);
return ret;
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedXor((long volatile *)a, 0);
#else
return *a;
#endif
}

inline int _hx_atomic_store(volatile int *a, int value) {
#if defined(HX_GCC_ATOMICS)
__atomic_store(a, &value, __ATOMIC_SEQ_CST);
return value;
#elif defined(HX_MSVC_ATOMICS)
_InterlockedExchange((long volatile *)a, value);
return value;
#else
*a = value;
return value;
#endif
}

inline void* _hx_atomic_compare_exchange_ptr(volatile void **a, void *expected, void* replacement) {
#if defined(HX_GCC_ATOMICS)
void* _expected = expected;
__atomic_compare_exchange(a, (volatile void **)&_expected, (volatile void**)&replacement, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
return _expected;
#elif defined(HX_MSVC_ATOMICS)
return _InterlockedCompareExchangePointer((void *volatile *)a, replacement, expected);
#else
void *old = *a;
*a = replacement;
return old;
#endif
}

inline void* _hx_atomic_compare_exchange_cast_ptr(void *a, void *expected, void *replacement) {
return _hx_atomic_compare_exchange_ptr((volatile void **)a, expected, replacement);
}

Array<String> __hxcpp_get_call_stack(bool inSkipLast);
Array<String> __hxcpp_get_exception_stack();
#define HXCPP_HAS_CLASSLIST
Expand Down
112 changes: 0 additions & 112 deletions include/hx/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,118 +41,6 @@
#undef RegisterClass
#endif

#if defined(ANDROID)

#define HX_HAS_ATOMIC 1

#if (HXCPP_ANDROID_PLATFORM>=16)
// Nice one, google, no one was using that.
#define __ATOMIC_INLINE__ static __inline__ __attribute__((always_inline))
// returns 0=exchange took place, 1=not
__ATOMIC_INLINE__ int __atomic_cmpxchg(int old, int _new, volatile int *ptr)
{ return __sync_val_compare_and_swap(ptr, old, _new) != old; }
__ATOMIC_INLINE__ int __atomic_dec(volatile int *ptr) { return __sync_fetch_and_sub (ptr, 1); }
__ATOMIC_INLINE__ int __atomic_inc(volatile int *ptr) { return __sync_fetch_and_add (ptr, 1); }
#else
#include <sys/atomics.h>
#endif

// returns 1 if exchange took place
inline bool HxAtomicExchangeIf(int inTest, int inNewVal,volatile int *ioWhere)
{ return !__atomic_cmpxchg(inTest, inNewVal, ioWhere); }
inline bool HxAtomicExchangeIfPtr(void *inTest, void *inNewVal,void * volatile *ioWhere)
{ return __sync_val_compare_and_swap(ioWhere, inTest, inNewVal)==inTest; }

// Returns old value naturally
inline int HxAtomicInc(volatile int *ioWhere)
{ return __atomic_inc(ioWhere); }
inline int HxAtomicDec(volatile int *ioWhere)
{ return __atomic_dec(ioWhere); }


#elif defined(HX_WINDOWS)

inline bool HxAtomicExchangeIf(int inTest, int inNewVal,volatile int *ioWhere)
{ return InterlockedCompareExchange((volatile LONG *)ioWhere, inNewVal, inTest)==inTest; }

inline bool HxAtomicExchangeIfPtr(void *inTest, void *inNewVal,void *volatile *ioWhere)
{ return InterlockedCompareExchangePointer(ioWhere, inNewVal, inTest)==inTest; }

// Make it return old value
inline int HxAtomicInc(volatile int *ioWhere)
{ return InterlockedIncrement((volatile LONG *)ioWhere)-1; }
inline int HxAtomicDec(volatile int *ioWhere)
{ return InterlockedDecrement((volatile LONG *)ioWhere)+1; }

#define HX_HAS_ATOMIC 1

#elif defined(HX_MACOS) || defined(IPHONE) || defined(APPLETV)
#include <libkern/OSAtomic.h>

#define HX_HAS_ATOMIC 1

inline bool HxAtomicExchangeIf(int inTest, int inNewVal,volatile int *ioWhere)
{ return OSAtomicCompareAndSwap32Barrier(inTest, inNewVal, ioWhere); }
inline bool HxAtomicExchangeIfPtr(void *inTest, void *inNewVal,void * volatile *ioWhere)
{ return OSAtomicCompareAndSwapPtrBarrier(inTest, inNewVal, ioWhere); }
inline int HxAtomicInc(volatile int *ioWhere)
{ return OSAtomicIncrement32Barrier(ioWhere)-1; }
inline int HxAtomicDec(volatile int *ioWhere)
{ return OSAtomicDecrement32Barrier(ioWhere)+1; }


#elif defined(HX_LINUX)

#define HX_HAS_ATOMIC 1

inline bool HxAtomicExchangeIf(int inTest, int inNewVal,volatile int *ioWhere)
{ return __sync_bool_compare_and_swap(ioWhere, inTest, inNewVal); }
inline bool HxAtomicExchangeIfPtr(void *inTest, void *inNewVal,void *volatile *ioWhere)
{ return __sync_bool_compare_and_swap(ioWhere, inTest, inNewVal); }
// Returns old value naturally
inline int HxAtomicInc(volatile int *ioWhere)
{ return __sync_fetch_and_add(ioWhere,1); }
inline int HxAtomicDec(volatile int *ioWhere)
{ return __sync_fetch_and_sub(ioWhere,1); }

#else

#define HX_HAS_ATOMIC 0

inline bool HxAtomicExchangeIfPtr(void *inTest, void *inNewVal,void *volatile *ioWhere)
{
if (*ioWhere == inTest)
{
*ioWhere = inNewVal;
return true;
}
return false;
}


inline int HxAtomicExchangeIf(int inTest, int inNewVal,volatile int *ioWhere)
{
if (*ioWhere == inTest)
{
*ioWhere = inNewVal;
return true;
}
return false;
}
inline int HxAtomicInc(volatile int *ioWhere)
{ return (*ioWhere)++; }
inline int HxAtomicDec(volatile int *ioWhere)
{ return (*ioWhere)--; }


#endif

inline bool HxAtomicExchangeIfCastPtr(void *inTest, void *inNewVal,void *ioWhere)
{
return HxAtomicExchangeIfPtr(inTest, inNewVal, (void *volatile *)ioWhere);
}


#if defined(HX_WINDOWS)


Expand Down
4 changes: 2 additions & 2 deletions src/String.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1172,7 +1172,7 @@ const ::String &::String::makePermanent() const
{
unsigned int myHash = hash();
{
while(! HxAtomicExchangeIf(0,1,&sPermanentStringSetMutex) )
while(_hx_atomic_compare_exchange(&sPermanentStringSetMutex, 0, 1) != 0)
__hxcpp_gc_safe_point();
TNonGcStringSet *element = sPermanentStringSet->find(myHash , *this);
sPermanentStringSetMutex = 0;
Expand All @@ -1198,7 +1198,7 @@ const ::String &::String::makePermanent() const
const_cast<String *>(this)->__s = s;
}

while(! HxAtomicExchangeIf(0,1,&sPermanentStringSetMutex) )
while(_hx_atomic_compare_exchange(&sPermanentStringSetMutex, 0, 1) != 0)
__hxcpp_gc_safe_point();
sPermanentStringSet->set(*this,null());
sPermanentStringSetMutex = 0;
Expand Down
6 changes: 3 additions & 3 deletions src/hx/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -930,17 +930,17 @@ int __hxcpp_GetCurrentThreadNumber()

bool _hx_atomic_exchange_if(::cpp::Pointer<cpp::AtomicInt> inPtr, int test, int newVal )
{
return HxAtomicExchangeIf(test, newVal, inPtr);
return _hx_atomic_compare_exchange(inPtr, test, newVal) == test;
}

int _hx_atomic_inc(::cpp::Pointer<cpp::AtomicInt> inPtr )
{
return HxAtomicInc(inPtr);
return _hx_atomic_add(inPtr, 1);
}

int _hx_atomic_dec(::cpp::Pointer<cpp::AtomicInt> inPtr )
{
return HxAtomicDec(inPtr);
return _hx_atomic_sub(inPtr, 1);
}


Loading

0 comments on commit aebf08f

Please sign in to comment.