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 <oswald.buddenhagen@digia.com>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Thiago Macieira 2012-08-06 19:57:59 +02:00 committed by The Qt Project
parent fb30a3dd7c
commit 634f82f1f1
13 changed files with 803 additions and 31 deletions

View File

@ -133,13 +133,16 @@ bool QBasicAtomicOps<4>::deref(T &_q_value) Q_DECL_NOTHROW
}
template<> template <typename T> 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;
}

View File

@ -81,6 +81,8 @@ template <int size> struct QBasicAtomicOps: QGenericAtomicOps<QBasicAtomicOps<si
static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() Q_DECL_NOTHROW { return true; }
static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW { return false; }
template <typename T> static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
template <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> inline
T QBasicAtomicOps<8>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW
{

View File

@ -149,28 +149,40 @@ template <typename X> 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 <typename T> static
bool testAndSetRelaxed(Type &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW
template <typename T>
static bool testAndSetRelaxed(std::atomic<T> &_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 <typename T>
static bool testAndSetAcquire(Type &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW
static bool testAndSetAcquire(std::atomic<T> &_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 <typename T>
static bool testAndSetRelease(Type &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW
static bool testAndSetRelease(std::atomic<T> &_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 <typename T>
static bool testAndSetOrdered(Type &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW
static bool testAndSetOrdered(std::atomic<T> &_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; }

View File

@ -112,6 +112,17 @@ template <typename X> struct QAtomicOps: QGenericAtomicOps<QAtomicOps<X> >
return __sync_bool_compare_and_swap(&_q_value, expectedValue, newValue);
}
template <typename T>
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 <typename T>
static T fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW
{

View File

@ -150,10 +150,10 @@ template <int size> struct QBasicAtomicOps: QGenericAtomicOps<QBasicAtomicOps<si
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 <typename T> static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
template <typename T> static bool testAndSetAcquire(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
template <typename T> static bool testAndSetRelease(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
template <typename T> static bool testAndSetOrdered(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
template <typename T> static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW;
template <typename T> static bool testAndSetAcquire(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW;
template <typename T> static bool testAndSetRelease(T &_q_value, T expectedValue, T newValue, T *currentValue = 0) Q_DECL_NOTHROW;
template <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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;
}

View File

@ -84,7 +84,8 @@ template <int size> struct QBasicAtomicOps: QGenericAtomicOps<QBasicAtomicOps<si
static inline Q_DECL_CONSTEXPR bool isTestAndSetNative() Q_DECL_NOTHROW { return true; }
static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW { return false; }
template <typename T> static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
template <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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;
}

View File

@ -310,6 +310,8 @@ template <int N> struct QAtomicOpsBySize : QGenericAtomicOps<QAtomicOpsBySize<N>
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 <typename T> static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
template <typename T>
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 <typename T>
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<typename T>
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 <typename T>
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<typename T>
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 <typename T>
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<typename T>
inline T QAtomicOpsBySize<8>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW
{
@ -436,6 +459,7 @@ struct QAtomicOps<T *> : QGenericAtomicOps<QAtomicOps<T *> >
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<T *>::testAndSetRelaxed(T *&_q_value, T *expectedValue, T
return QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&_q_value, newValue, expectedValue) == expectedValue;
}
template <typename T>
inline bool QAtomicOps<T *>::testAndSetRelaxed(T *&_q_value, T *expectedValue, T *newValue, T **currentValue) Q_DECL_NOTHROW
{
*currentValue = reinterpret_cast<T *>(QT_INTERLOCKED_COMPARE_EXCHANGE_POINTER(&_q_value, newValue, expectedValue));
return *currentValue == expectedValue;
}
template <typename T>
inline T *QAtomicOps<T *>::fetchAndStoreRelaxed(T *&_q_value, T *newValue) Q_DECL_NOTHROW
{

View File

@ -99,6 +99,8 @@ template <int size> struct QBasicAtomicOps: QGenericAtomicOps<QBasicAtomicOps<si
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 <typename T> static bool testAndSetRelaxed(T &_q_value, T expectedValue, T newValue) Q_DECL_NOTHROW;
template <typename T> 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<int size> template <typename T> inline
bool QBasicAtomicOps<size>::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 <typename T> 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<int size> template <typename T> inline
T QBasicAtomicOps<size>::fetchAndStoreRelaxed(T &_q_value, T newValue) Q_DECL_NOTHROW
{

View File

@ -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 &currentValue) Q_DECL_NOTHROW
{ return Ops::testAndSetRelaxed(_q_value, expectedValue, newValue, &currentValue); }
bool testAndSetAcquire(T expectedValue, T newValue, T &currentValue) Q_DECL_NOTHROW
{ return Ops::testAndSetAcquire(_q_value, expectedValue, newValue, &currentValue); }
bool testAndSetRelease(T expectedValue, T newValue, T &currentValue) Q_DECL_NOTHROW
{ return Ops::testAndSetRelease(_q_value, expectedValue, newValue, &currentValue); }
bool testAndSetOrdered(T expectedValue, T newValue, T &currentValue) Q_DECL_NOTHROW
{ return Ops::testAndSetOrdered(_q_value, expectedValue, newValue, &currentValue); }
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 &currentValue) Q_DECL_NOTHROW
{ return Ops::testAndSetRelaxed(_q_value, expectedValue, newValue, &currentValue); }
bool testAndSetAcquire(Type expectedValue, Type newValue, Type &currentValue) Q_DECL_NOTHROW
{ return Ops::testAndSetAcquire(_q_value, expectedValue, newValue, &currentValue); }
bool testAndSetRelease(Type expectedValue, Type newValue, Type &currentValue) Q_DECL_NOTHROW
{ return Ops::testAndSetRelease(_q_value, expectedValue, newValue, &currentValue); }
bool testAndSetOrdered(Type expectedValue, Type newValue, Type &currentValue) Q_DECL_NOTHROW
{ return Ops::testAndSetOrdered(_q_value, expectedValue, newValue, &currentValue); }
static Q_DECL_CONSTEXPR bool isFetchAndStoreNative() Q_DECL_NOTHROW { return Ops::isFetchAndStoreNative(); }
static Q_DECL_CONSTEXPR bool isFetchAndStoreWaitFree() Q_DECL_NOTHROW { return Ops::isFetchAndStoreWaitFree(); }

View File

@ -141,6 +141,8 @@ template <typename BaseClass> struct QGenericAtomicOps
static inline Q_DECL_CONSTEXPR bool isTestAndSetWaitFree() Q_DECL_NOTHROW;
template <typename T, typename X> static inline
bool testAndSetRelaxed(T &_q_value, X expectedValue, X newValue) Q_DECL_NOTHROW;
template <typename T, typename X> static inline
bool testAndSetRelaxed(T &_q_value, X expectedValue, X newValue, X *currentValue) Q_DECL_NOTHROW;
#endif
template <typename T, typename X> static inline always_inline
@ -165,6 +167,28 @@ template <typename BaseClass> struct QGenericAtomicOps
return BaseClass::testAndSetRelaxed(_q_value, expectedValue, newValue);
}
template <typename T, typename X> 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 <typename T, typename X> 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 <typename T, typename X> 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; }

View File

@ -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;

View File

@ -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 <bool> inline void booleanHelper() { }
@ -285,9 +306,13 @@ void tst_QAtomicIntegerXX::assign()
QCOMPARE(copy.load(), atomic.load());
QAtomicInteger<T> copy2;
copy2 = atomic;
copy2 = atomic; // operator=(const QAtomicInteger &)
QCOMPARE(copy2.load(), atomic.load());
QAtomicInteger<T> copy2bis;
copy2bis = atomic.load(); // operator=(T)
QCOMPARE(copy2bis.load(), atomic.load());
// move
QAtomicInteger<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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"

View File

@ -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 <typename T> 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<T> elsewhere
QBasicAtomicPointer<T> atomic = Q_BASIC_ATOMIC_INITIALIZER(0);
atomic = one;
QCOMPARE(Ptr(atomic), one);
}
QAtomicPointer<T> 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<char>();
operators_helper<int>();
operators_helper<double>();
operators_helper<Big>();
}
QTEST_APPLESS_MAIN(tst_QAtomicPointer)
#include "tst_qatomicpointer.moc"