From 634f82f1f1fda7983abf70b58e43c580b1f01df0 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 6 Aug 2012 19:57:59 +0200 Subject: [PATCH] Add a testAndSet overload to the atomics that returns the current value This is extremely useful, since the most common action after a failed compare-and-swap is to loop around, trying again with the current value as found in memory. Code currently written as: do { Type value = atomic.load(); ... } while (!atomic.testAndSetRelaxed(value, desired)); Becomes: Type value = atomic.load(); do { ... } while (!atomic.testAndSetRelaxed(value, desired, value)); In most CPU architectures, the value that was found in memory is known to the compare-and-swap code, so this is more efficient than the previous code. In architectures where the value is not known, the new code is no worse than before. The implementation sometimes modified an existing function, sometimes it added a new one, depending on whether more registers were needed in the assembly (like ARMv6-7), the code became more complex (ARMv5), the optimizer failed (C++11), or it was just plain equivalent (MIPS). Change-Id: I7d6d200ea9746ec8978a0c1e1969dbc3580b9285 Reviewed-by: Oswald Buddenhagen Reviewed-by: Lars Knoll Reviewed-by: Thiago Macieira --- src/corelib/arch/qatomic_armv5.h | 7 +- src/corelib/arch/qatomic_armv6.h | 96 +++++ src/corelib/arch/qatomic_cxx11.h | 30 +- src/corelib/arch/qatomic_gcc.h | 11 + src/corelib/arch/qatomic_ia64.h | 40 +- src/corelib/arch/qatomic_mips.h | 19 +- src/corelib/arch/qatomic_msvc.h | 31 ++ src/corelib/arch/qatomic_x86.h | 32 ++ src/corelib/thread/qbasicatomic.h | 18 + src/corelib/thread/qgenericatomic.h | 24 ++ .../thread/qatomicint/tst_qatomicint.cpp | 91 +++++ .../qatomicinteger/tst_qatomicinteger.cpp | 382 +++++++++++++++++- .../qatomicpointer/tst_qatomicpointer.cpp | 53 +++ 13 files changed, 803 insertions(+), 31 deletions(-) diff --git a/src/corelib/arch/qatomic_armv5.h b/src/corelib/arch/qatomic_armv5.h index 237ff5f2549..6939650c54b 100644 --- a/src/corelib/arch/qatomic_armv5.h +++ b/src/corelib/arch/qatomic_armv5.h @@ -133,13 +133,16 @@ bool QBasicAtomicOps<4>::deref(T &_q_value) Q_DECL_NOTHROW } template<> template inline -bool QBasicAtomicOps<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T originalValue; do { originalValue = _q_value; - if (originalValue != expectedValue) + if (originalValue != expectedValue) { + if (currentValue) + *currentValue = originalValue; return false; + } } while (_q_cmpxchg(expectedValue, newValue, &_q_value) != 0); return true; } diff --git a/src/corelib/arch/qatomic_armv6.h b/src/corelib/arch/qatomic_armv6.h index 37df64141c7..4f1c758ded2 100644 --- a/src/corelib/arch/qatomic_armv6.h +++ b/src/corelib/arch/qatomic_armv6.h @@ -81,6 +81,8 @@ template struct QBasicAtomicOps: QGenericAtomicOps static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW; + template static bool testAndSetRelaxed(T &_q_value, T expectedValue, + T newValue, T *currentValue) Q_DECL_NOTHROW; static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return true; } template static T fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW; @@ -162,6 +164,29 @@ bool QBasicAtomicOps<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newVa return result == 0; } +template<> template inline +bool QBasicAtomicOps<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW +{ + register T tempValue; + register int result; + asm volatile("0:\n" + "ldrex %[tempValue], [%[_q_value]]\n" + "eors %[result], %[tempValue], %[expectedValue]\n" + "itt eq\n" + "strexeq %[result], %[newValue], [%[_q_value]]\n" + "teqeq %[result], #1\n" + "beq 0b\n" + : [result] "=&r" (result), + [tempValue] "=&r" (tempValue), + "+m" (_q_value) + : [expectedValue] "r" (expectedValue), + [newValue] "r" (newValue), + [_q_value] "r" (&_q_value) + : "cc"); + *currentValue = tempValue; + return result == 0; +} + template<> template inline T QBasicAtomicOps<4>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { @@ -290,6 +315,29 @@ bool QBasicAtomicOps<1>::testAndSetRelaxed(T &_q_value, T expectedValue, T newVa return result == 0; } +template<> template inline +bool QBasicAtomicOps<1>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW +{ + register T tempValue; + register T result; + asm volatile("0:\n" + "ldrexb %[tempValue], [%[_q_value]]\n" + "eors %[result], %[tempValue], %[expectedValue]\n" + "itt eq\n" + "strexbeq %[result], %[newValue], [%[_q_value]]\n" + "teqeq %[result], #1\n" + "beq 0b\n" + : [result] "=&r" (result), + [tempValue] "=&r" (tempValue), + "+m" (_q_value) + : [expectedValue] "r" (expectedValue), + [newValue] "r" (newValue), + [_q_value] "r" (&_q_value) + : "cc"); + *currentValue = tempValue; + return result == 0; +} + template<> template inline T QBasicAtomicOps<1>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { @@ -389,6 +437,29 @@ bool QBasicAtomicOps<2>::testAndSetRelaxed(T &_q_value, T expectedValue, T newVa return result == 0; } +template<> template inline +bool QBasicAtomicOps<2>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW +{ + register T tempValue; + register T result; + asm volatile("0:\n" + "ldrexh %[tempValue], [%[_q_value]]\n" + "eors %[result], %[tempValue], %[expectedValue]\n" + "itt eq\n" + "strexheq %[result], %[newValue], [%[_q_value]]\n" + "teqeq %[result], #1\n" + "beq 0b\n" + : [result] "=&r" (result), + [tempValue] "=&r" (tempValue), + "+m" (_q_value) + : [expectedValue] "r" (expectedValue), + [newValue] "r" (newValue), + [_q_value] "r" (&_q_value) + : "cc"); + *currentValue = tempValue; + return result == 0; +} + template<> template inline T QBasicAtomicOps<2>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { @@ -500,6 +571,31 @@ bool QBasicAtomicOps<8>::testAndSetRelaxed(T &_q_value, T expectedValue, T newVa return quint32(result) == 0; } +template<> template inline +bool QBasicAtomicOps<8>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW +{ + register T tempValue; + register T result; + asm volatile("0:\n" + "ldrexd %[tempValue], %H[tempValue], [%[_q_value]]\n" + "eor %[result], %[tempValue], %[expectedValue]\n" + "eor %H[result], %H[tempValue], %H[expectedValue]\n" + "orrs %[result], %[result], %H[result]\n" + "itt eq\n" + "strexdeq %[result], %[newValue], %H[newValue], [%[_q_value]]\n" + "teqeq %[result], #1\n" + "beq 0b\n" + : [result] "=&r" (result), + [tempValue] "=&r" (tempValue), + "+m" (_q_value) + : [expectedValue] "r" (expectedValue), + [newValue] "r" (newValue), + [_q_value] "r" (&_q_value) + : "cc"); + *currentValue = tempValue; + return quint32(result) == 0; +} + template<> template inline T QBasicAtomicOps<8>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { diff --git a/src/corelib/arch/qatomic_cxx11.h b/src/corelib/arch/qatomic_cxx11.h index 85575da20e3..b07e4704841 100644 --- a/src/corelib/arch/qatomic_cxx11.h +++ b/src/corelib/arch/qatomic_cxx11.h @@ -149,28 +149,40 @@ template struct QAtomicOps static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() Q_DECL_NOTHROW { return false; } static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW { return false; } - template static - bool testAndSetRelaxed(Type &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW + template + static bool testAndSetRelaxed(std::atomic &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW { - return _q_value.compare_exchange_strong(expectedValue, newValue, std::memory_order_relaxed); + bool tmp = _q_value.compare_exchange_strong(expectedValue, newValue, std::memory_order_relaxed); + if (currentValue) + *currentValue = expectedValue; + return tmp; } template - static bool testAndSetAcquire(Type &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW + static bool testAndSetAcquire(std::atomic &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW { - return _q_value.compare_exchange_strong(expectedValue, newValue, std::memory_order_acquire); + bool tmp = _q_value.compare_exchange_strong(expectedValue, newValue, std::memory_order_acquire); + if (currentValue) + *currentValue = expectedValue; + return tmp; } template - static bool testAndSetRelease(Type &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW + static bool testAndSetRelease(std::atomic &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW { - return _q_value.compare_exchange_strong(expectedValue, newValue, std::memory_order_release); + bool tmp = _q_value.compare_exchange_strong(expectedValue, newValue, std::memory_order_release); + if (currentValue) + *currentValue = expectedValue; + return tmp; } template - static bool testAndSetOrdered(Type &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW + static bool testAndSetOrdered(std::atomic &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW { - return _q_value.compare_exchange_strong(expectedValue, newValue, std::memory_order_acq_rel); + bool tmp = _q_value.compare_exchange_strong(expectedValue, newValue, std::memory_order_acq_rel); + if (currentValue) + *currentValue = expectedValue; + return tmp; } static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return false; } diff --git a/src/corelib/arch/qatomic_gcc.h b/src/corelib/arch/qatomic_gcc.h index 052453c082b..51842934659 100644 --- a/src/corelib/arch/qatomic_gcc.h +++ b/src/corelib/arch/qatomic_gcc.h @@ -112,6 +112,17 @@ template struct QAtomicOps: QGenericAtomicOps > return __sync_bool_compare_and_swap(&_q_value, expectedValue, newValue); } + template + static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW + { + bool tmp = __sync_bool_compare_and_swap(&_q_value, expectedValue, newValue); + if (tmp) + *currentValue = expectedValue; + else + *currentValue = _q_value; + return tmp; + } + template static T fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { diff --git a/src/corelib/arch/qatomic_ia64.h b/src/corelib/arch/qatomic_ia64.h index 0075f32a422..e5e93ec2b79 100644 --- a/src/corelib/arch/qatomic_ia64.h +++ b/src/corelib/arch/qatomic_ia64.h @@ -150,10 +150,10 @@ template struct QBasicAtomicOps: QGenericAtomicOps static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW; - template static bool testAndSetAcquire(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW; - template static bool testAndSetRelease(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW; - template static bool testAndSetOrdered(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW; + template static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW; + template static bool testAndSetAcquire(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW; + template static bool testAndSetRelease(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW; + template static bool testAndSetOrdered(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW; static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return true; } static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return true; } @@ -373,7 +373,7 @@ bool QBasicAtomicOps<8>::deref(T &_q_value) Q_DECL_NOTHROW } template<> template inline -bool QBasicAtomicOps<1>::testAndSetAcquire(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<1>::testAndSetAcquire(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T ret; asm volatile("mov ar.ccv=%2\n" @@ -382,11 +382,13 @@ bool QBasicAtomicOps<1>::testAndSetAcquire(T &_q_value, T expectedValue, T newVa : "=r" (ret), "+m" (_q_value) : "r" (expectedValue), "r" (newValue) : "memory"); + if (currentValue) + *currentValue = ret; return ret == expectedValue; } template<> template inline -bool QBasicAtomicOps<1>::testAndSetRelease(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<1>::testAndSetRelease(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T ret; asm volatile("mov ar.ccv=%2\n" @@ -395,11 +397,13 @@ bool QBasicAtomicOps<1>::testAndSetRelease(T &_q_value, T expectedValue, T newVa : "=r" (ret), "+m" (_q_value) : "r" (expectedValue), "r" (newValue) : "memory"); + if (currentValue) + *currentValue = ret; return ret == expectedValue; } template<> template inline -bool QBasicAtomicOps<2>::testAndSetAcquire(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<2>::testAndSetAcquire(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T ret; asm volatile("mov ar.ccv=%2\n" @@ -408,11 +412,13 @@ bool QBasicAtomicOps<2>::testAndSetAcquire(T &_q_value, T expectedValue, T newVa : "=r" (ret), "+m" (_q_value) : "r" (expectedValue), "r" (newValue) : "memory"); + if (currentValue) + *currentValue = ret; return ret == expectedValue; } template<> template inline -bool QBasicAtomicOps<2>::testAndSetRelease(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<2>::testAndSetRelease(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T ret; asm volatile("mov ar.ccv=%2\n" @@ -421,11 +427,13 @@ bool QBasicAtomicOps<2>::testAndSetRelease(T &_q_value, T expectedValue, T newVa : "=r" (ret), "+m" (_q_value) : "r" (expectedValue), "r" (newValue) : "memory"); + if (currentValue) + *currentValue = ret; return ret == expectedValue; } template<> template inline -bool QBasicAtomicOps<4>::testAndSetAcquire(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<4>::testAndSetAcquire(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T ret; asm volatile("mov ar.ccv=%2\n" @@ -434,11 +442,13 @@ bool QBasicAtomicOps<4>::testAndSetAcquire(T &_q_value, T expectedValue, T newVa : "=r" (ret), "+m" (_q_value) : "r" (expectedValue), "r" (newValue) : "memory"); + if (currentValue) + *currentValue = ret; return ret == expectedValue; } template<> template inline -bool QBasicAtomicOps<4>::testAndSetRelease(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<4>::testAndSetRelease(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T ret; asm volatile("mov ar.ccv=%2\n" @@ -447,11 +457,13 @@ bool QBasicAtomicOps<4>::testAndSetRelease(T &_q_value, T expectedValue, T newVa : "=r" (ret), "+m" (_q_value) : "r" (expectedValue), "r" (newValue) : "memory"); + if (currentValue) + *currentValue = ret; return ret == expectedValue; } template<> template inline -bool QBasicAtomicOps<8>::testAndSetAcquire(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<8>::testAndSetAcquire(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T ret; asm volatile("mov ar.ccv=%2\n" @@ -460,11 +472,13 @@ bool QBasicAtomicOps<8>::testAndSetAcquire(T &_q_value, T expectedValue, T newVa : "=r" (ret), "+m" (_q_value) : "r" (expectedValue), "r" (newValue) : "memory"); + if (currentValue) + *currentValue = ret; return ret == expectedValue; } template<> template inline -bool QBasicAtomicOps<8>::testAndSetRelease(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<8>::testAndSetRelease(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T ret; asm volatile("mov ar.ccv=%2\n" @@ -473,6 +487,8 @@ bool QBasicAtomicOps<8>::testAndSetRelease(T &_q_value, T expectedValue, T newVa : "=r" (ret), "+m" (_q_value) : "r" (expectedValue), "r" (newValue) : "memory"); + if (currentValue) + *currentValue = ret; return ret == expectedValue; } diff --git a/src/corelib/arch/qatomic_mips.h b/src/corelib/arch/qatomic_mips.h index 9c744b885d1..463612212ba 100644 --- a/src/corelib/arch/qatomic_mips.h +++ b/src/corelib/arch/qatomic_mips.h @@ -84,7 +84,8 @@ template struct QBasicAtomicOps: QGenericAtomicOps static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW; + template static bool + testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW; static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return true; } template static T fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW; @@ -163,13 +164,13 @@ bool QBasicAtomicOps<4>::deref(T &_q_value) Q_DECL_NOTHROW } template<> template inline -bool QBasicAtomicOps<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T result; T tempValue; asm volatile("0:\n" - "ll %[result], %[_q_value]\n" - "xor %[result], %[result], %[expectedValue]\n" + "ll %[tempValue], %[_q_value]\n" + "xor %[result], %[tempValue], %[expectedValue]\n" "bnez %[result], 0f\n" "nop\n" "move %[tempValue], %[newValue]\n" @@ -183,6 +184,8 @@ bool QBasicAtomicOps<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newVa : [expectedValue] "r" (expectedValue), [newValue] "r" (newValue) : "cc", "memory"); + if (currentValue) + *currentValue = tempValue; return result == 0; } @@ -273,13 +276,13 @@ bool QBasicAtomicOps<8>::deref(T &_q_value) Q_DECL_NOTHROW } template<> template inline -bool QBasicAtomicOps<8>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW +bool QBasicAtomicOps<8>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW { T result; T tempValue; asm volatile("0:\n" - "lld %[result], %[_q_value]\n" - "xor %[result], %[result], %[expectedValue]\n" + "lld %[tempValue], %[_q_value]\n" + "xor %[result], %[tempValue], %[expectedValue]\n" "bnez %[result], 0f\n" "nop\n" "move %[tempValue], %[newValue]\n" @@ -293,6 +296,8 @@ bool QBasicAtomicOps<8>::testAndSetRelaxed(T &_q_value, T expectedValue, T newVa : [expectedValue] "r" (expectedValue), [newValue] "r" (newValue) : "cc", "memory"); + if (currentValue) + *currentValue = tempValue; return result == 0; } diff --git a/src/corelib/arch/qatomic_msvc.h b/src/corelib/arch/qatomic_msvc.h index 1cbb1442edb..c660f788881 100644 --- a/src/corelib/arch/qatomic_msvc.h +++ b/src/corelib/arch/qatomic_msvc.h @@ -310,6 +310,8 @@ template struct QAtomicOpsBySize : QGenericAtomicOps static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() Q_DECL_NOTHROW { return true; } static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW { return true; } template static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW; + template + static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW; static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return true; } static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return true; } @@ -351,6 +353,13 @@ inline bool QAtomicOpsBySize<4>::testAndSetRelaxed(T &_q_value, T expectedValue, return QT_INTERLOCKED_FUNCTION(CompareExchange)(atomic(&_q_value), value(newValue), value(expectedValue)) == value(expectedValue); } +template<> template +inline bool QAtomicOpsBySize<4>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW +{ + *currentValue = T(QT_INTERLOCKED_FUNCTION(CompareExchange)(atomic(&_q_value), newValue, expectedValue)); + return *currentValue == expectedValue; +} + template<> template inline T QAtomicOpsBySize<4>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { @@ -382,6 +391,13 @@ inline bool QAtomicOpsBySize<2>::testAndSetRelaxed(T &_q_value, T expectedValue, return QT_INTERLOCKED_FUNCTION(CompareExchange16)(atomic(&_q_value), value(newValue), value(expectedValue)) == value(expectedValue); } +template<> template +inline bool QAtomicOpsBySize<2>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW +{ + *currentValue = T(QT_INTERLOCKED_FUNCTION(CompareExchange16)(atomic(&_q_value), newValue, expectedValue)); + return *currentValue == expectedValue; +} + template<> template inline T QAtomicOpsBySize<2>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { @@ -414,6 +430,13 @@ inline bool QAtomicOpsBySize<8>::testAndSetRelaxed(T &_q_value, T expectedValue, return QT_INTERLOCKED_FUNCTION(CompareExchange64)(atomic(&_q_value), value(newValue), value(expectedValue)) == value(expectedValue); } +template<> template +inline bool QAtomicOpsBySize<8>::testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW +{ + *currentValue = T(QT_INTERLOCKED_FUNCTION(CompareExchange64)(atomic(&_q_value), newValue, expectedValue)); + return *currentValue == expectedValue; +} + template<> template inline T QAtomicOpsBySize<8>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { @@ -436,6 +459,7 @@ struct QAtomicOps : QGenericAtomicOps > static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() Q_DECL_NOTHROW { return true; } static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW { return true; } static bool testAndSetRelaxed(T *&_q_value, T *expectedValue, T *newValue) Q_DECL_NOTHROW; + static bool testAndSetRelaxed(T *&_q_value, T *expectedValue, T *newValue, T **currentValue) Q_DECL_NOTHROW; static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return true; } static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return true; } @@ -452,6 +476,13 @@ inline bool QAtomicOps::testAndSetRelaxed(T *&_q_value, T *expectedValue, T return QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&_q_value, newValue, expectedValue) == expectedValue; } +template +inline bool QAtomicOps::testAndSetRelaxed(T *&_q_value, T *expectedValue, T *newValue, T **currentValue) Q_DECL_NOTHROW +{ + *currentValue = reinterpret_cast(QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&_q_value, newValue, expectedValue)); + return *currentValue == expectedValue; +} + template inline T *QAtomicOps::fetchAndStoreRelaxed(T *&_q_value, T *newValue) Q_DECL_NOTHROW { diff --git a/src/corelib/arch/qatomic_x86.h b/src/corelib/arch/qatomic_x86.h index 2013d8f991a..e3cb296527c 100644 --- a/src/corelib/arch/qatomic_x86.h +++ b/src/corelib/arch/qatomic_x86.h @@ -99,6 +99,8 @@ template struct QBasicAtomicOps: QGenericAtomicOps static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW; + template static bool + testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue) Q_DECL_NOTHROW; static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return true; } static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return true; } @@ -252,6 +254,36 @@ bool QBasicAtomicOps<1>::testAndSetRelaxed(T &_q_value, T expectedValue, T newVa return ret != 0; } +template template inline +bool QBasicAtomicOps::testAndSetRelaxed(T &_q_value, T expectedValue, + T newValue, T *currentValue) Q_DECL_NOTHROW +{ + unsigned char ret; + asm volatile("lock\n" + "cmpxchg %3,%2\n" + "sete %1\n" + : "=a" (newValue), "=qm" (ret), "+m" (_q_value) + : "r" (newValue), "0" (expectedValue) + : "memory"); + *currentValue = newValue; + return ret != 0; +} + +template<> template inline +bool QBasicAtomicOps<1>::testAndSetRelaxed(T &_q_value, T expectedValue, + T newValue, T *currentValue) Q_DECL_NOTHROW +{ + unsigned char ret; + asm volatile("lock\n" + "cmpxchg %3,%2\n" + "sete %1\n" + : "=a" (newValue), "=qm" (ret), "+m" (_q_value) + : "q" (newValue), "0" (expectedValue) + : "memory"); + *currentValue = newValue; + return ret != 0; +} + template template inline T QBasicAtomicOps::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW { diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h index f75a24ae10b..b2234bdb805 100644 --- a/src/corelib/thread/qbasicatomic.h +++ b/src/corelib/thread/qbasicatomic.h @@ -145,6 +145,15 @@ public: bool testAndSetOrdered(T expectedValue, T newValue) Q_DECL_NOTHROW { return Ops::testAndSetOrdered(_q_value, expectedValue, newValue); } + bool testAndSetRelaxed(T expectedValue, T newValue, T ¤tValue) Q_DECL_NOTHROW + { return Ops::testAndSetRelaxed(_q_value, expectedValue, newValue, ¤tValue); } + bool testAndSetAcquire(T expectedValue, T newValue, T ¤tValue) Q_DECL_NOTHROW + { return Ops::testAndSetAcquire(_q_value, expectedValue, newValue, ¤tValue); } + bool testAndSetRelease(T expectedValue, T newValue, T ¤tValue) Q_DECL_NOTHROW + { return Ops::testAndSetRelease(_q_value, expectedValue, newValue, ¤tValue); } + bool testAndSetOrdered(T expectedValue, T newValue, T ¤tValue) Q_DECL_NOTHROW + { return Ops::testAndSetOrdered(_q_value, expectedValue, newValue, ¤tValue); } + static Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return Ops::isFetchAndStoreNative(); } static Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return Ops::isFetchAndStoreWaitFree(); } @@ -209,6 +218,15 @@ public: bool testAndSetOrdered(Type expectedValue, Type newValue) Q_DECL_NOTHROW { return Ops::testAndSetOrdered(_q_value, expectedValue, newValue); } + bool testAndSetRelaxed(Type expectedValue, Type newValue, Type ¤tValue) Q_DECL_NOTHROW + { return Ops::testAndSetRelaxed(_q_value, expectedValue, newValue, ¤tValue); } + bool testAndSetAcquire(Type expectedValue, Type newValue, Type ¤tValue) Q_DECL_NOTHROW + { return Ops::testAndSetAcquire(_q_value, expectedValue, newValue, ¤tValue); } + bool testAndSetRelease(Type expectedValue, Type newValue, Type ¤tValue) Q_DECL_NOTHROW + { return Ops::testAndSetRelease(_q_value, expectedValue, newValue, ¤tValue); } + bool testAndSetOrdered(Type expectedValue, Type newValue, Type ¤tValue) Q_DECL_NOTHROW + { return Ops::testAndSetOrdered(_q_value, expectedValue, newValue, ¤tValue); } + static Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return Ops::isFetchAndStoreNative(); } static Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return Ops::isFetchAndStoreWaitFree(); } diff --git a/src/corelib/thread/qgenericatomic.h b/src/corelib/thread/qgenericatomic.h index eacde411b99..aab0cfbb45a 100644 --- a/src/corelib/thread/qgenericatomic.h +++ b/src/corelib/thread/qgenericatomic.h @@ -141,6 +141,8 @@ template struct QGenericAtomicOps static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW; template static inline bool testAndSetRelaxed(T &_q_value, X expectedValue, X newValue) Q_DECL_NOTHROW; + template static inline + bool testAndSetRelaxed(T &_q_value, X expectedValue, X newValue, X *currentValue) Q_DECL_NOTHROW; #endif template static inline always_inline @@ -165,6 +167,28 @@ template struct QGenericAtomicOps return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue); } + template static inline always_inline + bool testAndSetAcquire(T &_q_value, X expectedValue, X newValue, X *currentValue) Q_DECL_NOTHROW + { + bool tmp = BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue, currentValue); + BaseClass::acquireMemoryFence(_q_value); + return tmp; + } + + template static inline always_inline + bool testAndSetRelease(T &_q_value, X expectedValue, X newValue, X *currentValue) Q_DECL_NOTHROW + { + BaseClass::releaseMemoryFence(_q_value); + return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue, currentValue); + } + + template static inline always_inline + bool testAndSetOrdered(T &_q_value, X expectedValue, X newValue, X *currentValue) Q_DECL_NOTHROW + { + BaseClass::orderedMemoryFence(_q_value); + return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue, currentValue); + } + static inline Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return false; } static inline Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return false; } diff --git a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp index 42b3a525310..f0d817d37d1 100644 --- a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp +++ b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp @@ -84,6 +84,8 @@ private slots: void fetchAndAdd_data(); void fetchAndAdd(); + void operators(); + // stress tests void testAndSet_loop(); void fetchAndAdd_loop(); @@ -540,6 +542,43 @@ void tst_QAtomicInt::testAndSet() QAtomicInt atomic = value; QTEST(atomic.testAndSetOrdered(expected, newval) ? 1 : 0, "result"); } + +#ifdef Q_ATOMIC_INT32_IS_SUPPORTED + QFETCH(int, result); + // the new implementation has the version that loads the current value + + { + QAtomicInt atomic = value; + int currentval = 0xdeadbeef; + QCOMPARE(atomic.testAndSetRelaxed(expected, newval, currentval), result); + if (!result) + QCOMPARE(currentval, value); + } + + { + QAtomicInt atomic = value; + int currentval = 0xdeadbeef; + QCOMPARE(atomic.testAndSetAcquire(expected, newval, currentval), result); + if (!result) + QCOMPARE(currentval, value); + } + + { + QAtomicInt atomic = value; + int currentval = 0xdeadbeef; + QCOMPARE(atomic.testAndSetRelease(expected, newval, currentval), result); + if (!result) + QCOMPARE(currentval, value); + } + + { + QAtomicInt atomic = value; + int currentval = 0xdeadbeef; + QCOMPARE(atomic.testAndSetOrdered(expected, newval, currentval), result); + if (!result) + QCOMPARE(currentval, value); + } +#endif } void tst_QAtomicInt::isFetchAndStoreNative() @@ -770,6 +809,58 @@ void tst_QAtomicInt::fetchAndAdd() } } +void tst_QAtomicInt::operators() +{ + { + // Test that QBasicAtomicInt also has operator= and cast operators + // We've been using them for QAtomicInt elsewhere + QBasicAtomicInt atomic = Q_BASIC_ATOMIC_INITIALIZER(0); + atomic = 1; + QCOMPARE(int(atomic), 1); + } + + QAtomicInt atomic = 0; + int x = ++atomic; + QCOMPARE(int(atomic), x); + QCOMPARE(int(atomic), 1); + + x = atomic++; + QCOMPARE(int(atomic), x + 1); + QCOMPARE(int(atomic), 2); + + x = atomic--; + QCOMPARE(int(atomic), x - 1); + QCOMPARE(int(atomic), 1); + + x = --atomic; + QCOMPARE(int(atomic), x); + QCOMPARE(int(atomic), 0); + + x = (atomic += 1); + QCOMPARE(int(atomic), x); + QCOMPARE(int(atomic), 1); + + x = (atomic -= 1); + QCOMPARE(int(atomic), x); + QCOMPARE(int(atomic), 0); + + x = (atomic |= 0xf); + QCOMPARE(int(atomic), x); + QCOMPARE(int(atomic), 0xf); + + x = (atomic &= 0x17); + QCOMPARE(int(atomic), x); + QCOMPARE(int(atomic), 7); + + x = (atomic ^= 0x14); + QCOMPARE(int(atomic), x); + QCOMPARE(int(atomic), 0x13); + + x = (atomic ^= atomic); + QCOMPARE(int(atomic), x); + QCOMPARE(int(atomic), 0); +} + void tst_QAtomicInt::testAndSet_loop() { QTime stopWatch; diff --git a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp index a7139b6a9ae..6ddd2ff2335 100644 --- a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp +++ b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp @@ -143,6 +143,9 @@ private Q_SLOTS: void assign_data() { addData(); } void assign(); + void operatorInteger_data() { addData(); } + void operatorInteger(); + void loadAcquireStoreRelease_data() { addData(); } void loadAcquireStoreRelease(); @@ -152,11 +155,29 @@ private Q_SLOTS: void testAndSet_data() { addData(); } void testAndSet(); + void testAndSet3_data() { addData(); } + void testAndSet3(); + void fetchAndStore_data() { addData(); } void fetchAndStore(); void fetchAndAdd_data() { addData(); } void fetchAndAdd(); + + void fetchAndSub_data() { addData(); } + void fetchAndSub(); + + void addSub_data() { addData(); } + void addSub(); + + void fetchAndOr_data() { addData(); } + void fetchAndOr(); + + void fetchAndAnd_data() { addData(); } + void fetchAndAnd(); + + void fetchAndXor_data() { addData(); } + void fetchAndXor(); }; template inline void booleanHelper() { } @@ -285,9 +306,13 @@ void tst_QAtomicIntegerXX::assign() QCOMPARE(copy.load(), atomic.load()); QAtomicInteger copy2; - copy2 = atomic; + copy2 = atomic; // operator=(const QAtomicInteger &) QCOMPARE(copy2.load(), atomic.load()); + QAtomicInteger copy2bis; + copy2bis = atomic.load(); // operator=(T) + QCOMPARE(copy2bis.load(), atomic.load()); + // move QAtomicInteger copy3; copy3 = qMove(copy); @@ -298,6 +323,16 @@ void tst_QAtomicIntegerXX::assign() QCOMPARE(copy4.load(), atomic.load()); } +void tst_QAtomicIntegerXX::operatorInteger() +{ + QFETCH(LargeInt, value); + + QAtomicInteger atomic(value); + T val2 = atomic; + QCOMPARE(val2, atomic.load()); + QCOMPARE(val2, T(value)); +} + void tst_QAtomicIntegerXX::loadAcquireStoreRelease() { QFETCH(LargeInt, value); @@ -327,6 +362,17 @@ void tst_QAtomicIntegerXX::refDeref() QCOMPARE(atomic.load(), prevValue); QCOMPARE(atomic.ref(), (value != 0)); QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(++atomic, nextValue); + QCOMPARE(--atomic, T(value)); + QCOMPARE(--atomic, prevValue); + QCOMPARE(++atomic, T(value)); + + QCOMPARE(atomic++, T(value)); + QCOMPARE(atomic--, nextValue); + QCOMPARE(atomic--, T(value)); + QCOMPARE(atomic++, prevValue); + QCOMPARE(atomic.load(), T(value)); } void tst_QAtomicIntegerXX::testAndSet() @@ -360,6 +406,42 @@ void tst_QAtomicIntegerXX::testAndSet() QCOMPARE(atomic.loadAcquire(), T(value)); } +void tst_QAtomicIntegerXX::testAndSet3() +{ + QFETCH(LargeInt, value); + T newValue = ~T(value); + T oldValue; + QAtomicInteger atomic(value); + + QVERIFY(atomic.testAndSetRelaxed(value, newValue, oldValue)); + QCOMPARE(atomic.load(), newValue); + QVERIFY(!atomic.testAndSetRelaxed(value, newValue, oldValue)); + QCOMPARE(oldValue, newValue); + QVERIFY(atomic.testAndSetRelaxed(newValue, value, oldValue)); + QCOMPARE(atomic.load(), T(value)); + + QVERIFY(atomic.testAndSetAcquire(value, newValue, oldValue)); + QCOMPARE(atomic.load(), newValue); + QVERIFY(!atomic.testAndSetAcquire(value, newValue, oldValue)); + QCOMPARE(oldValue, newValue); + QVERIFY(atomic.testAndSetAcquire(newValue, value, oldValue)); + QCOMPARE(atomic.load(), T(value)); + + QVERIFY(atomic.testAndSetRelease(value, newValue, oldValue)); + QCOMPARE(atomic.loadAcquire(), newValue); + QVERIFY(!atomic.testAndSetRelease(value, newValue, oldValue)); + QCOMPARE(oldValue, newValue); + QVERIFY(atomic.testAndSetRelease(newValue, value, oldValue)); + QCOMPARE(atomic.loadAcquire(), T(value)); + + QVERIFY(atomic.testAndSetOrdered(value, newValue, oldValue)); + QCOMPARE(atomic.loadAcquire(), newValue); + QVERIFY(!atomic.testAndSetOrdered(value, newValue, oldValue)); + QCOMPARE(oldValue, newValue); + QVERIFY(atomic.testAndSetOrdered(newValue, value, oldValue)); + QCOMPARE(atomic.loadAcquire(), T(value)); +} + void tst_QAtomicIntegerXX::fetchAndStore() { QFETCH(LargeInt, value); @@ -433,6 +515,304 @@ void tst_QAtomicIntegerXX::fetchAndAdd() QCOMPARE(atomic.loadAcquire(), newValue2); QCOMPARE(atomic.fetchAndAddOrdered(parcel1), newValue2); QCOMPARE(atomic.loadAcquire(), T(value)); + + // operator+= + QCOMPARE(atomic += parcel1, newValue1); + QCOMPARE(atomic += parcel2, T(value)); + QCOMPARE(atomic += parcel2, newValue2); + QCOMPARE(atomic += parcel1, T(value)); +} + +void tst_QAtomicIntegerXX::fetchAndSub() +{ + QFETCH(LargeInt, value); + QAtomicInteger atomic(value); + + // note: this test has undefined behavior for signed max and min + T parcel1 = 42; + T parcel2 = T(0-parcel1); + T newValue1 = T(value) - parcel1; + T newValue2 = T(value) - parcel2; + + QCOMPARE(atomic.fetchAndSubRelaxed(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndSubRelaxed(parcel2), newValue1); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubRelaxed(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndSubRelaxed(parcel1), newValue2); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic.fetchAndSubAcquire(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndSubAcquire(parcel2), newValue1); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubAcquire(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndSubAcquire(parcel1), newValue2); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic.fetchAndSubRelease(parcel1), T(value)); + QCOMPARE(atomic.loadAcquire(), newValue1); + QCOMPARE(atomic.fetchAndSubRelease(parcel2), newValue1); + QCOMPARE(atomic.loadAcquire(), T(value)); + QCOMPARE(atomic.fetchAndSubRelease(parcel2), T(value)); + QCOMPARE(atomic.loadAcquire(), newValue2); + QCOMPARE(atomic.fetchAndSubRelease(parcel1), newValue2); + QCOMPARE(atomic.loadAcquire(), T(value)); + + QCOMPARE(atomic.fetchAndSubOrdered(parcel1), T(value)); + QCOMPARE(atomic.loadAcquire(), newValue1); + QCOMPARE(atomic.fetchAndSubOrdered(parcel2), newValue1); + QCOMPARE(atomic.loadAcquire(), T(value)); + QCOMPARE(atomic.fetchAndSubOrdered(parcel2), T(value)); + QCOMPARE(atomic.loadAcquire(), newValue2); + QCOMPARE(atomic.fetchAndSubOrdered(parcel1), newValue2); + QCOMPARE(atomic.loadAcquire(), T(value)); + + // operator-= + QCOMPARE(atomic -= parcel1, newValue1); + QCOMPARE(atomic -= parcel2, T(value)); + QCOMPARE(atomic -= parcel2, newValue2); + QCOMPARE(atomic -= parcel1, T(value)); +} + +void tst_QAtomicIntegerXX::addSub() +{ + QFETCH(LargeInt, value); + QAtomicInteger atomic(value); + + // note: this test has undefined behavior for signed max and min + T parcel1 = 42; + T parcel2 = T(0-parcel1); + T newValue1 = T(value) + parcel1; + T newValue2 = T(value) - parcel1; + + QCOMPARE(atomic.fetchAndAddRelaxed(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndSubRelaxed(parcel1), newValue1); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubRelaxed(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndAddRelaxed(parcel1), newValue2); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndAddRelaxed(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndSubRelaxed(parcel2), newValue2); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubRelaxed(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndAddRelaxed(parcel2), newValue1); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic.fetchAndAddAcquire(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndSubAcquire(parcel1), newValue1); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubAcquire(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndAddAcquire(parcel1), newValue2); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndAddAcquire(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndSubAcquire(parcel2), newValue2); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubAcquire(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndAddAcquire(parcel2), newValue1); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic.fetchAndAddRelease(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndSubRelease(parcel1), newValue1); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubRelease(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndAddRelease(parcel1), newValue2); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndAddRelease(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndSubRelease(parcel2), newValue2); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubRelease(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndAddRelease(parcel2), newValue1); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic.fetchAndAddOrdered(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndSubOrdered(parcel1), newValue1); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubOrdered(parcel1), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndAddOrdered(parcel1), newValue2); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndAddOrdered(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue2); + QCOMPARE(atomic.fetchAndSubOrdered(parcel2), newValue2); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndSubOrdered(parcel2), T(value)); + QCOMPARE(atomic.load(), newValue1); + QCOMPARE(atomic.fetchAndAddOrdered(parcel2), newValue1); + QCOMPARE(atomic.load(), T(value)); + + // operator+= and operator-= + QCOMPARE(atomic += parcel1, newValue1); + QCOMPARE(atomic -= parcel1, T(value)); + QCOMPARE(atomic -= parcel1, newValue2); + QCOMPARE(atomic += parcel1, T(value)); + QCOMPARE(atomic += parcel2, newValue2); + QCOMPARE(atomic -= parcel2, T(value)); + QCOMPARE(atomic -= parcel2, newValue1); + QCOMPARE(atomic += parcel2, T(value)); +} + +void tst_QAtomicIntegerXX::fetchAndOr() +{ + QFETCH(LargeInt, value); + QAtomicInteger atomic(value); + + T zero = 0; + T one = 1; + T minusOne = T(~0); + + QCOMPARE(atomic.fetchAndOrRelaxed(zero), T(value)); + QCOMPARE(atomic.fetchAndOrRelaxed(one), T(value)); + QCOMPARE(atomic.load(), T(value | 1)); + QCOMPARE(atomic.fetchAndOrRelaxed(minusOne), T(value | 1)); + QCOMPARE(atomic.load(), minusOne); + + atomic.store(value); + QCOMPARE(atomic.fetchAndOrAcquire(zero), T(value)); + QCOMPARE(atomic.fetchAndOrAcquire(one), T(value)); + QCOMPARE(atomic.load(), T(value | 1)); + QCOMPARE(atomic.fetchAndOrAcquire(minusOne), T(value | 1)); + QCOMPARE(atomic.load(), minusOne); + + atomic.store(value); + QCOMPARE(atomic.fetchAndOrRelease(zero), T(value)); + QCOMPARE(atomic.fetchAndOrRelease(one), T(value)); + QCOMPARE(atomic.load(), T(value | 1)); + QCOMPARE(atomic.fetchAndOrRelease(minusOne), T(value | 1)); + QCOMPARE(atomic.load(), minusOne); + + atomic.store(value); + QCOMPARE(atomic.fetchAndOrOrdered(zero), T(value)); + QCOMPARE(atomic.fetchAndOrOrdered(one), T(value)); + QCOMPARE(atomic.load(), T(value | 1)); + QCOMPARE(atomic.fetchAndOrOrdered(minusOne), T(value | 1)); + QCOMPARE(atomic.load(), minusOne); + + atomic.store(value); + QCOMPARE(atomic |= zero, T(value)); + QCOMPARE(atomic |= one, T(value | 1)); + QCOMPARE(atomic |= minusOne, minusOne); +} + +void tst_QAtomicIntegerXX::fetchAndAnd() +{ + QFETCH(LargeInt, value); + QAtomicInteger atomic(value); + + T zero = 0; + T f = 0xf; + T minusOne = T(~0); + + QCOMPARE(atomic.fetchAndAndRelaxed(minusOne), T(value)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndAndRelaxed(f), T(value)); + QCOMPARE(atomic.load(), T(value & 0xf)); + QCOMPARE(atomic.fetchAndAndRelaxed(zero), T(value & 0xf)); + QCOMPARE(atomic.load(), zero); + + atomic.store(value); + QCOMPARE(atomic.fetchAndAndAcquire(minusOne), T(value)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndAndAcquire(f), T(value)); + QCOMPARE(atomic.load(), T(value & 0xf)); + QCOMPARE(atomic.fetchAndAndAcquire(zero), T(value & 0xf)); + QCOMPARE(atomic.load(), zero); + + atomic.store(value); + QCOMPARE(atomic.fetchAndAndRelease(minusOne), T(value)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndAndRelease(f), T(value)); + QCOMPARE(atomic.load(), T(value & 0xf)); + QCOMPARE(atomic.fetchAndAndRelease(zero), T(value & 0xf)); + QCOMPARE(atomic.load(), zero); + + atomic.store(value); + QCOMPARE(atomic.fetchAndAndOrdered(minusOne), T(value)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndAndOrdered(f), T(value)); + QCOMPARE(atomic.load(), T(value & 0xf)); + QCOMPARE(atomic.fetchAndAndOrdered(zero), T(value & 0xf)); + QCOMPARE(atomic.load(), zero); + + atomic.store(value); + QCOMPARE(atomic &= minusOne, T(value)); + QCOMPARE(atomic &= f, T(value & 0xf)); + QCOMPARE(atomic &= zero, zero); +} + +void tst_QAtomicIntegerXX::fetchAndXor() +{ + QFETCH(LargeInt, value); + QAtomicInteger atomic(value); + + T zero = 0; + T pattern = T(Q_UINT64_C(0xcccccccccccccccc)); + T minusOne = T(~0); + + QCOMPARE(atomic.fetchAndXorRelaxed(zero), T(value)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndXorRelaxed(pattern), T(value)); + QCOMPARE(atomic.load(), T(value ^ pattern)); + QCOMPARE(atomic.fetchAndXorRelaxed(pattern), T(value ^ pattern)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndXorRelaxed(minusOne), T(value)); + QCOMPARE(atomic.load(), T(~value)); + QCOMPARE(atomic.fetchAndXorRelaxed(minusOne), T(~value)); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic.fetchAndXorAcquire(zero), T(value)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndXorAcquire(pattern), T(value)); + QCOMPARE(atomic.load(), T(value ^ pattern)); + QCOMPARE(atomic.fetchAndXorAcquire(pattern), T(value ^ pattern)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndXorAcquire(minusOne), T(value)); + QCOMPARE(atomic.load(), T(~value)); + QCOMPARE(atomic.fetchAndXorAcquire(minusOne), T(~value)); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic.fetchAndXorRelease(zero), T(value)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndXorRelease(pattern), T(value)); + QCOMPARE(atomic.load(), T(value ^ pattern)); + QCOMPARE(atomic.fetchAndXorRelease(pattern), T(value ^ pattern)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndXorRelease(minusOne), T(value)); + QCOMPARE(atomic.load(), T(~value)); + QCOMPARE(atomic.fetchAndXorRelease(minusOne), T(~value)); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic.fetchAndXorOrdered(zero), T(value)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndXorOrdered(pattern), T(value)); + QCOMPARE(atomic.load(), T(value ^ pattern)); + QCOMPARE(atomic.fetchAndXorOrdered(pattern), T(value ^ pattern)); + QCOMPARE(atomic.load(), T(value)); + QCOMPARE(atomic.fetchAndXorOrdered(minusOne), T(value)); + QCOMPARE(atomic.load(), T(~value)); + QCOMPARE(atomic.fetchAndXorOrdered(minusOne), T(~value)); + QCOMPARE(atomic.load(), T(value)); + + QCOMPARE(atomic ^= zero, T(value)); + QCOMPARE(atomic ^= pattern, T(value ^ pattern)); + QCOMPARE(atomic ^= pattern, T(value)); + QCOMPARE(atomic ^= minusOne, T(~value)); + QCOMPARE(atomic ^= minusOne, T(value)); } #include "tst_qatomicinteger.moc" diff --git a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp index d4e777e35b2..595808e2704 100644 --- a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp +++ b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp @@ -70,6 +70,8 @@ private slots: void constAndVolatile(); void forwardDeclared(); + + void operators(); private: static void warningFreeHelper(); }; @@ -664,5 +666,56 @@ void tst_QAtomicPointer::forwardDeclared() QVERIFY(true); } +template static void operators_helper() +{ + typedef T *Ptr; + T array[3] = {}; + Ptr zero = array; + Ptr one = array + 1; + Ptr two = array + 2; + + { + // Test that QBasicAtomicPointer also has operator= and cast operators + // We've been using them for QAtomicPointer elsewhere + QBasicAtomicPointer atomic = Q_BASIC_ATOMIC_INITIALIZER(0); + atomic = one; + QCOMPARE(Ptr(atomic), one); + } + + QAtomicPointer atomic = zero; + Ptr x = ++atomic; + QCOMPARE(Ptr(atomic), x); + QCOMPARE(Ptr(atomic), one); + + x = atomic++; + QCOMPARE(Ptr(atomic), x + 1); + QCOMPARE(Ptr(atomic), two); + + x = atomic--; + QCOMPARE(Ptr(atomic), x - 1); + QCOMPARE(Ptr(atomic), one); + + x = --atomic; + QCOMPARE(Ptr(atomic), x); + QCOMPARE(Ptr(atomic), zero); + + x = (atomic += 1); + QCOMPARE(Ptr(atomic), x); + QCOMPARE(Ptr(atomic), one); + + x = (atomic -= 1); + QCOMPARE(Ptr(atomic), x); + QCOMPARE(Ptr(atomic), zero); +} + +struct Big { double d[10]; }; +void tst_QAtomicPointer::operators() +{ + operators_helper(); + operators_helper(); + operators_helper(); + operators_helper(); +} + QTEST_APPLESS_MAIN(tst_QAtomicPointer) #include "tst_qatomicpointer.moc"