qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,37 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(QT_FEATURE_thread)
add_subdirectory(qatomicint)
add_subdirectory(qatomicinteger)
add_subdirectory(qatomicpointer)
add_subdirectory(qresultstore)
if(QT_FEATURE_concurrent AND NOT INTEGRITY)
add_subdirectory(qfuture)
endif()
add_subdirectory(qfuturesynchronizer)
add_subdirectory(qmutex)
add_subdirectory(qmutexlocker)
add_subdirectory(qreadlocker)
add_subdirectory(qreadwritelock)
add_subdirectory(qsemaphore)
# special case begin
# QTBUG-85364
if(NOT CMAKE_CROSSCOMPILING)
add_subdirectory(qthread)
endif()
# special case end
add_subdirectory(qthreadonce)
add_subdirectory(qthreadpool)
add_subdirectory(qthreadstorage)
add_subdirectory(qwaitcondition)
add_subdirectory(qwritelocker)
if(NOT INTEGRITY)
add_subdirectory(qpromise)
endif()
endif()
# QTBUG-87431
if(TARGET Qt::Concurrent AND NOT INTEGRITY)
add_subdirectory(qfuturewatcher)
endif()

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicint Test:
#####################################################################
qt_internal_add_test(tst_qatomicint
SOURCES
tst_qatomicint.cpp
)

View File

@ -0,0 +1,875 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QAtomicInt>
#include <QCoreApplication>
#include <QElapsedTimer>
#include <limits.h>
class tst_QAtomicInt : public QObject
{
Q_OBJECT
private slots:
void warningFree();
void alignment();
// QAtomicInt members
void constructor_data();
void constructor();
void copy_constructor_data();
void copy_constructor();
void assignment_operator_data();
void assignment_operator();
void isReferenceCountingNative();
void isReferenceCountingWaitFree();
void ref_data();
void ref();
void deref_data();
void deref();
void isTestAndSetNative();
void isTestAndSetWaitFree();
void testAndSet_data();
void testAndSet();
void isFetchAndStoreNative();
void isFetchAndStoreWaitFree();
void fetchAndStore_data();
void fetchAndStore();
void isFetchAndAddNative();
void isFetchAndAddWaitFree();
void fetchAndAdd_data();
void fetchAndAdd();
void operators();
// stress tests
void testAndSet_loop();
void fetchAndAdd_loop();
void fetchAndAdd_threadedLoop();
private:
static void warningFreeHelper();
};
template <int I>
static inline void assemblyMarker(void *ptr = nullptr)
{
puts((char *)ptr + I);
}
template <typename T, typename Atomic>
static void warningFreeHelperTemplate()
{
T expectedValue = 0;
T newValue = 0;
T valueToAdd = 0;
// the marker calls are here only to provide a divider for
// those reading the assembly output
assemblyMarker<0>();
Atomic i = Q_BASIC_ATOMIC_INITIALIZER(0);
printf("%d\n", int(i.loadAcquire()));
assemblyMarker<1>(&i);
// the loads sometimes generate no assembly output
i.loadRelaxed();
assemblyMarker<11>(&i);
i.loadAcquire();
assemblyMarker<12>(&i);
i.storeRelaxed(newValue);
assemblyMarker<21>(&i);
i.storeRelease(newValue);
assemblyMarker<22>(&i);
i.ref();
assemblyMarker<31>(&i);
i.deref();
assemblyMarker<32>(&i);
i.testAndSetRelaxed(expectedValue, newValue);
assemblyMarker<41>(&i);
i.testAndSetAcquire(expectedValue, newValue);
assemblyMarker<42>(&i);
i.testAndSetRelease(expectedValue, newValue);
assemblyMarker<43>(&i);
i.testAndSetOrdered(expectedValue, newValue);
assemblyMarker<44>(&i);
i.fetchAndStoreRelaxed(newValue);
assemblyMarker<51>(&i);
i.fetchAndStoreAcquire(newValue);
assemblyMarker<52>(&i);
i.fetchAndStoreRelease(newValue);
assemblyMarker<53>(&i);
i.fetchAndStoreOrdered(newValue);
assemblyMarker<54>(&i);
i.fetchAndAddRelaxed(valueToAdd);
assemblyMarker<61>(&i);
i.fetchAndAddAcquire(valueToAdd);
assemblyMarker<62>(&i);
i.fetchAndAddRelease(valueToAdd);
assemblyMarker<63>(&i);
i.fetchAndAddOrdered(valueToAdd);
assemblyMarker<64>(&i);
}
template <bool> inline void booleanHelper()
{ }
template <typename Atomic>
static void constexprFunctionsHelperTemplate()
{
// this is a compile-time test only
booleanHelper<Atomic::isReferenceCountingWaitFree()>();
booleanHelper<Atomic::isTestAndSetWaitFree()>();
booleanHelper<Atomic::isFetchAndStoreWaitFree()>();
booleanHelper<Atomic::isFetchAndAddWaitFree()>();
}
void tst_QAtomicInt::warningFreeHelper()
{
qFatal("This code is bogus, and shouldn't be run. We're looking for compiler warnings only.");
warningFreeHelperTemplate<int, QBasicAtomicInt>();
// 32-bit are always supported:
warningFreeHelperTemplate<int, QBasicAtomicInteger<int> >();
warningFreeHelperTemplate<unsigned int, QBasicAtomicInteger<unsigned int> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<int> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned int> >();
warningFreeHelperTemplate<qint16, QBasicAtomicInteger<char32_t> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<char32_t> >();
// pointer-sized integers are always supported:
warningFreeHelperTemplate<int, QBasicAtomicInteger<qptrdiff> >();
warningFreeHelperTemplate<unsigned int, QBasicAtomicInteger<quintptr> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<qptrdiff> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<quintptr> >();
// long is always supported because it's either 32-bit or pointer-sized:
warningFreeHelperTemplate<int, QBasicAtomicInteger<long int> >();
warningFreeHelperTemplate<unsigned int, QBasicAtomicInteger<unsigned long int> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<long int> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned long int> >();
warningFreeHelperTemplate<qint16, QBasicAtomicInteger<qint16> >();
warningFreeHelperTemplate<quint16, QBasicAtomicInteger<quint16> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<qint16> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<quint16> >();
warningFreeHelperTemplate<qint16, QBasicAtomicInteger<char16_t> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<char16_t> >();
warningFreeHelperTemplate<char, QBasicAtomicInteger<char> >();
warningFreeHelperTemplate<signed char, QBasicAtomicInteger<signed char> >();
warningFreeHelperTemplate<unsigned char, QBasicAtomicInteger<unsigned char> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<char> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<signed char> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned char> >();
#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
#if !defined(__i386__) || (defined(Q_CC_GNU) && defined(__OPTIMIZE__))
warningFreeHelperTemplate<qlonglong, QBasicAtomicInteger<qlonglong> >();
warningFreeHelperTemplate<qulonglong, QBasicAtomicInteger<qulonglong> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<qlonglong> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<qulonglong> >();
#endif
#endif
}
void tst_QAtomicInt::warningFree()
{
// This is a compile time check for warnings.
// No need for actual work here.
void (*foo)() = &warningFreeHelper;
(void)foo;
}
template <typename T> struct TypeInStruct { T type; };
void tst_QAtomicInt::alignment()
{
static_assert(alignof(QBasicAtomicInt) == alignof(TypeInStruct<int>));
static_assert(alignof(QBasicAtomicInt) == alignof(TypeInStruct<int>));
QCOMPARE(alignof(QBasicAtomicInteger<int>), alignof(TypeInStruct<int>));
QCOMPARE(alignof(QBasicAtomicInteger<short>), alignof(TypeInStruct<short>));
QCOMPARE(alignof(QBasicAtomicInteger<char>), alignof(TypeInStruct<char>));
#if !defined(Q_PROCESSOR_X86_32) && defined(Q_ATOMIC_INT64_IS_SUPPORTED)
// The alignment is different on x86_32
QCOMPARE(alignof(QBasicAtomicInteger<qlonglong>), alignof(TypeInStruct<qlonglong>));
#endif
}
void tst_QAtomicInt::constructor_data()
{
QTest::addColumn<int>("value");
QTest::newRow("0") << 31337;
QTest::newRow("1") << 0;
QTest::newRow("2") << 1;
QTest::newRow("3") << -1;
QTest::newRow("4") << 2;
QTest::newRow("5") << -2;
QTest::newRow("6") << 3;
QTest::newRow("7") << -3;
QTest::newRow("8") << INT_MAX;
QTest::newRow("9") << INT_MIN+1;
}
void tst_QAtomicInt::constructor()
{
QFETCH(int, value);
QAtomicInt atomic1(value);
QCOMPARE(atomic1.loadRelaxed(), value);
QAtomicInt atomic2 = value;
QCOMPARE(atomic2.loadRelaxed(), value);
}
void tst_QAtomicInt::copy_constructor_data()
{ constructor_data(); }
void tst_QAtomicInt::copy_constructor()
{
QFETCH(int, value);
QAtomicInt atomic1(value);
QCOMPARE(atomic1.loadRelaxed(), value);
QAtomicInt atomic2(atomic1);
QCOMPARE(atomic2.loadRelaxed(), value);
QAtomicInt atomic3 = atomic1;
QCOMPARE(atomic3.loadRelaxed(), value);
QAtomicInt atomic4(atomic2);
QCOMPARE(atomic4.loadRelaxed(), value);
QAtomicInt atomic5 = atomic2;
QCOMPARE(atomic5.loadRelaxed(), value);
}
void tst_QAtomicInt::assignment_operator_data()
{
QTest::addColumn<int>("value");
QTest::addColumn<int>("newval");
QTest::newRow("value0") << 0 << 1;
QTest::newRow("value1") << 1 << 0;
QTest::newRow("value2") << 0 << -1;
QTest::newRow("value3") << -1 << 0;
QTest::newRow("value4") << -1 << 1;
QTest::newRow("value5") << 1 << -1;
}
void tst_QAtomicInt::assignment_operator()
{
QFETCH(int, value);
QFETCH(int, newval);
{
QAtomicInt atomic1 = value;
atomic1 = newval;
QCOMPARE(atomic1.loadRelaxed(), newval);
atomic1 = value;
QCOMPARE(atomic1.loadRelaxed(), value);
QAtomicInt atomic2 = newval;
atomic1 = atomic2;
QCOMPARE(atomic1.loadRelaxed(), atomic2.loadRelaxed());
}
}
void tst_QAtomicInt::isReferenceCountingNative()
{
#if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE)
// the runtime test should say the same thing
QVERIFY(QAtomicInt::isReferenceCountingNative());
# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE) \
|| defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE)
// could be either, just want to make sure the function is implemented
QVERIFY(QAtomicInt::isReferenceCountingNative() || !QAtomicInt::isReferenceCountingNative());
# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE)
// the runtime test should say the same thing
QVERIFY(!QAtomicInt::isReferenceCountingNative());
# if (defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE))
# error "Define only one of Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#else
# error "Q_ATOMIC_INT_REFERENCE_COUNTING_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
#endif
}
void tst_QAtomicInt::isReferenceCountingWaitFree()
{
#if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE)
// the runtime test should say the same thing
QVERIFY(QAtomicInt::isReferenceCountingWaitFree());
// enforce some invariants
QVERIFY(QAtomicInt::isReferenceCountingNative());
# if defined(Q_ATOMIC_INT_REFERENCE_COUNTING_IS_NOT_NATIVE)
# error "Reference counting cannot be wait-free and unsupported at the same time!"
# endif
#else
// the runtime test should say the same thing
QVERIFY(!QAtomicInt::isReferenceCountingWaitFree());
#endif
}
void tst_QAtomicInt::ref_data()
{
QTest::addColumn<int>("value");
QTest::addColumn<int>("result");
QTest::addColumn<int>("expected");
QTest::newRow("data0") << 0 << 1 << 1;
QTest::newRow("data1") << -1 << 0 << 0;
QTest::newRow("data2") << 1 << 1 << 2;
}
void tst_QAtomicInt::ref()
{
QFETCH(int, value);
QAtomicInt x = value;
QTEST(x.ref() ? 1 : 0, "result");
QTEST(x.loadRelaxed(), "expected");
}
void tst_QAtomicInt::deref_data()
{
QTest::addColumn<int>("value");
QTest::addColumn<int>("result");
QTest::addColumn<int>("expected");
QTest::newRow("data0") << 0 << 1 << -1;
QTest::newRow("data1") << 1 << 0 << 0;
QTest::newRow("data2") << 2 << 1 << 1;
}
void tst_QAtomicInt::deref()
{
QFETCH(int, value);
QAtomicInt x = value;
QTEST(x.deref() ? 1 : 0, "result");
QTEST(x.loadRelaxed(), "expected");
}
void tst_QAtomicInt::isTestAndSetNative()
{
#if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE)
// the runtime test should say the same thing
QVERIFY(QAtomicInt::isTestAndSetNative());
# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE) \
|| defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE)
// could be either, just want to make sure the function is implemented
QVERIFY(QAtomicInt::isTestAndSetNative() || !QAtomicInt::isTestAndSetNative());
# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE)
// the runtime test should say the same thing
QVERIFY(!QAtomicInt::isTestAndSetNative());
# if (defined(Q_ATOMIC_INT_TEST_AND_SET_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_INT_TEST_AND_SET_IS_SOMETIMES_NATIVE))
# error "Define only one of Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#else
# error "Q_ATOMIC_INT_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
#endif
}
void tst_QAtomicInt::isTestAndSetWaitFree()
{
#if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_WAIT_FREE)
// the runtime test should say the same thing
QVERIFY(QAtomicInt::isTestAndSetWaitFree());
// enforce some invariants
QVERIFY(QAtomicInt::isTestAndSetNative());
# if defined(Q_ATOMIC_INT_TEST_AND_SET_IS_NOT_NATIVE)
# error "Reference counting cannot be wait-free and unsupported at the same time!"
# endif
#else
// the runtime test should say the same thing
QVERIFY(!QAtomicInt::isTestAndSetWaitFree());
#endif
}
void tst_QAtomicInt::testAndSet_data()
{
QTest::addColumn<int>("value");
QTest::addColumn<int>("expected");
QTest::addColumn<int>("newval");
QTest::addColumn<bool>("result");
// these should succeed
QTest::newRow("success0") << 0 << 0 << 0 << true;
QTest::newRow("success1") << 0 << 0 << 1 << true;
QTest::newRow("success2") << 0 << 0 << -1 << true;
QTest::newRow("success3") << 1 << 1 << 0 << true;
QTest::newRow("success4") << 1 << 1 << 1 << true;
QTest::newRow("success5") << 1 << 1 << -1 << true;
QTest::newRow("success6") << -1 << -1 << 0 << true;
QTest::newRow("success7") << -1 << -1 << 1 << true;
QTest::newRow("success8") << -1 << -1 << -1 << true;
QTest::newRow("success9") << INT_MIN+1 << INT_MIN+1 << INT_MIN+1 << true;
QTest::newRow("successA") << INT_MIN+1 << INT_MIN+1 << 1 << true;
QTest::newRow("successB") << INT_MIN+1 << INT_MIN+1 << -1 << true;
QTest::newRow("successC") << INT_MAX << INT_MAX << INT_MAX << true;
QTest::newRow("successD") << INT_MAX << INT_MAX << 1 << true;
QTest::newRow("successE") << INT_MAX << INT_MAX << -1 << true;
// these should fail
QTest::newRow("failure0") << 0 << 1 << ~0 << false;
QTest::newRow("failure1") << 0 << -1 << ~0 << false;
QTest::newRow("failure2") << 1 << 0 << ~0 << false;
QTest::newRow("failure3") << -1 << 0 << ~0 << false;
QTest::newRow("failure4") << 1 << -1 << ~0 << false;
QTest::newRow("failure5") << -1 << 1 << ~0 << false;
QTest::newRow("failure6") << INT_MIN+1 << INT_MAX << ~0 << false;
QTest::newRow("failure7") << INT_MAX << INT_MIN+1 << ~0 << false;
}
void tst_QAtomicInt::testAndSet()
{
QFETCH(int, value);
QFETCH(int, expected);
QFETCH(int, newval);
{
QAtomicInt atomic = value;
QTEST(atomic.testAndSetRelaxed(expected, newval), "result");
}
{
QAtomicInt atomic = value;
QTEST(atomic.testAndSetAcquire(expected, newval), "result");
}
{
QAtomicInt atomic = value;
QTEST(atomic.testAndSetRelease(expected, newval), "result");
}
{
QAtomicInt atomic = value;
QTEST(atomic.testAndSetOrdered(expected, newval), "result");
}
QFETCH(bool, 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);
}
}
void tst_QAtomicInt::isFetchAndStoreNative()
{
#if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE)
// the runtime test should say the same thing
QVERIFY(QAtomicInt::isFetchAndStoreNative());
# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE) \
|| defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE)
// could be either, just want to make sure the function is implemented
QVERIFY(QAtomicInt::isFetchAndStoreNative() || !QAtomicInt::isFetchAndStoreNative());
# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE)
// the runtime test should say the same thing
QVERIFY(!QAtomicInt::isFetchAndStoreNative());
# if (defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_SOMETIMES_NATIVE))
# error "Define only one of Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#else
# error "Q_ATOMIC_INT_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
#endif
}
void tst_QAtomicInt::isFetchAndStoreWaitFree()
{
#if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_WAIT_FREE)
// the runtime test should say the same thing
QVERIFY(QAtomicInt::isFetchAndStoreWaitFree());
// enforce some invariants
QVERIFY(QAtomicInt::isFetchAndStoreNative());
# if defined(Q_ATOMIC_INT_FETCH_AND_STORE_IS_NOT_NATIVE)
# error "Reference counting cannot be wait-free and unsupported at the same time!"
# endif
#else
// the runtime test should say the same thing
QVERIFY(!QAtomicInt::isFetchAndStoreWaitFree());
#endif
}
void tst_QAtomicInt::fetchAndStore_data()
{
QTest::addColumn<int>("value");
QTest::addColumn<int>("newval");
QTest::newRow("data0") << 0 << 1;
QTest::newRow("data1") << 1 << 2;
QTest::newRow("data2") << 3 << 8;
}
void tst_QAtomicInt::fetchAndStore()
{
QFETCH(int, value);
QFETCH(int, newval);
{
QAtomicInt atomic = value;
QCOMPARE(atomic.fetchAndStoreRelaxed(newval), value);
QCOMPARE(atomic.loadRelaxed(), newval);
}
{
QAtomicInt atomic = value;
QCOMPARE(atomic.fetchAndStoreAcquire(newval), value);
QCOMPARE(atomic.loadRelaxed(), newval);
}
{
QAtomicInt atomic = value;
QCOMPARE(atomic.fetchAndStoreRelease(newval), value);
QCOMPARE(atomic.loadRelaxed(), newval);
}
{
QAtomicInt atomic = value;
QCOMPARE(atomic.fetchAndStoreOrdered(newval), value);
QCOMPARE(atomic.loadRelaxed(), newval);
}
}
void tst_QAtomicInt::isFetchAndAddNative()
{
#if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE)
// the runtime test should say the same thing
QVERIFY(QAtomicInt::isFetchAndAddNative());
# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE) \
|| defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE)
// could be either, just want to make sure the function is implemented
QVERIFY(QAtomicInt::isFetchAndAddNative() || !QAtomicInt::isFetchAndAddNative());
# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE)
// the runtime test should say the same thing
QVERIFY(!QAtomicInt::isFetchAndAddNative());
# if (defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_SOMETIMES_NATIVE))
# error "Define only one of Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#else
# error "Q_ATOMIC_INT_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
#endif
}
void tst_QAtomicInt::isFetchAndAddWaitFree()
{
#if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_WAIT_FREE)
// the runtime test should say the same thing
QVERIFY(QAtomicInt::isFetchAndAddWaitFree());
// enforce some invariants
QVERIFY(QAtomicInt::isFetchAndAddNative());
# if defined(Q_ATOMIC_INT_FETCH_AND_ADD_IS_NOT_NATIVE)
# error "Reference counting cannot be wait-free and unsupported at the same time!"
# endif
#else
// the runtime test should say the same thing
QVERIFY(!QAtomicInt::isFetchAndAddWaitFree());
#endif
}
void tst_QAtomicInt::fetchAndAdd_data()
{
QTest::addColumn<int>("value1");
QTest::addColumn<int>("value2");
QTest::newRow("0+1") << 0 << 1;
QTest::newRow("1+0") << 1 << 0;
QTest::newRow("1+2") << 1 << 2;
QTest::newRow("2+1") << 2 << 1;
QTest::newRow("10+21") << 10 << 21;
QTest::newRow("31+40") << 31 << 40;
QTest::newRow("51+62") << 51 << 62;
QTest::newRow("72+81") << 72 << 81;
QTest::newRow("810+721") << 810 << 721;
QTest::newRow("631+540") << 631 << 540;
QTest::newRow("451+362") << 451 << 362;
QTest::newRow("272+181") << 272 << 181;
QTest::newRow("1810+8721") << 1810 << 8721;
QTest::newRow("3631+6540") << 3631 << 6540;
QTest::newRow("5451+4362") << 5451 << 4362;
QTest::newRow("7272+2181") << 7272 << 2181;
QTest::newRow("0+-1") << 0 << -1;
QTest::newRow("1+-2") << 1 << -2;
QTest::newRow("2+-1") << 2 << -1;
QTest::newRow("10+-21") << 10 << -21;
QTest::newRow("31+-40") << 31 << -40;
QTest::newRow("51+-62") << 51 << -62;
QTest::newRow("72+-81") << 72 << -81;
QTest::newRow("810+-721") << 810 << -721;
QTest::newRow("631+-540") << 631 << -540;
QTest::newRow("451+-362") << 451 << -362;
QTest::newRow("272+-181") << 272 << -181;
QTest::newRow("1810+-8721") << 1810 << -8721;
QTest::newRow("3631+-6540") << 3631 << -6540;
QTest::newRow("5451+-4362") << 5451 << -4362;
QTest::newRow("7272+-2181") << 7272 << -2181;
QTest::newRow("-1+0") << -1 << 0;
QTest::newRow("-1+2") << -1 << 2;
QTest::newRow("-2+1") << -2 << 1;
QTest::newRow("-10+21") << -10 << 21;
QTest::newRow("-31+40") << -31 << 40;
QTest::newRow("-51+62") << -51 << 62;
QTest::newRow("-72+81") << -72 << 81;
QTest::newRow("-810+721") << -810 << 721;
QTest::newRow("-631+540") << -631 << 540;
QTest::newRow("-451+362") << -451 << 362;
QTest::newRow("-272+181") << -272 << 181;
QTest::newRow("-1810+8721") << -1810 << 8721;
QTest::newRow("-3631+6540") << -3631 << 6540;
QTest::newRow("-5451+4362") << -5451 << 4362;
QTest::newRow("-7272+2181") << -7272 << 2181;
}
void tst_QAtomicInt::fetchAndAdd()
{
QFETCH(int, value1);
QFETCH(int, value2);
int result;
{
QAtomicInt atomic = value1;
result = atomic.fetchAndAddRelaxed(value2);
QCOMPARE(result, value1);
QCOMPARE(atomic.loadRelaxed(), value1 + value2);
}
{
QAtomicInt atomic = value1;
result = atomic.fetchAndAddAcquire(value2);
QCOMPARE(result, value1);
QCOMPARE(atomic.loadRelaxed(), value1 + value2);
}
{
QAtomicInt atomic = value1;
result = atomic.fetchAndAddRelease(value2);
QCOMPARE(result, value1);
QCOMPARE(atomic.loadRelaxed(), value1 + value2);
}
{
QAtomicInt atomic = value1;
result = atomic.fetchAndAddOrdered(value2);
QCOMPARE(result, value1);
QCOMPARE(atomic.loadRelaxed(), value1 + value2);
}
}
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);
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
x = (atomic ^= atomic);
QCOMPARE(int(atomic), x);
QCOMPARE(int(atomic), 0);
QT_WARNING_POP
}
void tst_QAtomicInt::testAndSet_loop()
{
QElapsedTimer stopWatch;
stopWatch.start();
int iterations = 10000000;
QAtomicInt val=0;
for (int i = 0; i < iterations; ++i) {
int v = val.loadRelaxed();
QVERIFY(val.testAndSetRelaxed(v, v+1));
if ((i % 1000) == 999) {
if (stopWatch.elapsed() > 60 * 1000) {
// This test shouldn't run for more than two minutes.
qDebug("Interrupted test after %d iterations (%.2f iterations/sec)",
i, (i * 1000.0) / double(stopWatch.elapsed()));
break;
}
}
}
}
void tst_QAtomicInt::fetchAndAdd_loop()
{
int iterations = 10000000;
#if defined (Q_OS_HPUX)
iterations = 1000000;
#endif
QAtomicInt val=0;
for (int i = 0; i < iterations; ++i) {
const int prev = val.fetchAndAddRelaxed(1);
QCOMPARE(prev, val.loadRelaxed() -1);
}
}
class FetchAndAddThread : public QThread
{
public:
void run() override
{
for (int i = 0; i < iterations; ++i)
val->fetchAndAddAcquire(1);
for (int i = 0; i < iterations; ++i)
val->fetchAndAddAcquire(-1);
}
QAtomicInt *val;
int iterations;
};
void tst_QAtomicInt::fetchAndAdd_threadedLoop()
{
QAtomicInt val;
FetchAndAddThread t1;
t1.val = &val;
t1.iterations = 1000000;
FetchAndAddThread t2;
t2.val = &val;
t2.iterations = 2000000;
t1.start();
t2.start();
t1.wait();
t2.wait();
QCOMPARE(val.loadRelaxed(), 0);
}
QTEST_MAIN(tst_QAtomicInt)
#include "tst_qatomicint.moc"

View File

@ -0,0 +1,19 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(char)
add_subdirectory(char16_t)
add_subdirectory(char32_t)
add_subdirectory(int)
add_subdirectory(long)
add_subdirectory(qlonglong)
add_subdirectory(qptrdiff)
add_subdirectory(quintptr)
add_subdirectory(qulonglong)
add_subdirectory(schar)
add_subdirectory(short)
add_subdirectory(uchar)
add_subdirectory(uint)
add_subdirectory(ulong)
add_subdirectory(ushort)
add_subdirectory(wchar_t)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_char
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=char
tst_QAtomicIntegerXX=tst_QAtomicInteger_char
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char16_t Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_char16_t
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=char16_t
tst_QAtomicIntegerXX=tst_QAtomicInteger_char16_t
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char32_t Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_char32_t
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=char32_t
tst_QAtomicIntegerXX=tst_QAtomicInteger_char32_t
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_int Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_int
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=int
tst_QAtomicIntegerXX=tst_QAtomicInteger_int
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_long Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_long
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=long
tst_QAtomicIntegerXX=tst_QAtomicInteger_long
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qlonglong Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_qlonglong
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=qlonglong
tst_QAtomicIntegerXX=tst_QAtomicInteger_qlonglong
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qptrdiff Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_qptrdiff
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=qptrdiff
tst_QAtomicIntegerXX=tst_QAtomicInteger_qptrdiff
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_quintptr Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_quintptr
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=quintptr
tst_QAtomicIntegerXX=tst_QAtomicInteger_quintptr
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qulonglong Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_qulonglong
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=qulonglong
tst_QAtomicIntegerXX=tst_QAtomicInteger_qulonglong
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_schar Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_schar
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=schar
tst_QAtomicIntegerXX=tst_QAtomicInteger_schar
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_short Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_short
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=short
tst_QAtomicIntegerXX=tst_QAtomicInteger_short
)

View File

@ -0,0 +1,766 @@
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifdef QT_ATOMIC_FORCE_CXX11
// We need to check if this compiler has C++11 atomics and constexpr support.
// We can't rely on qcompilerdetection.h because it forces all of qglobal.h to
// be included, which causes qbasicatomic.h to be included too.
// Incomplete, but ok
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1500 && (__cplusplus >= 201103L || defined(__INTEL_CXX11_MODE__))
# elif defined(__clang__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
# if !__has_feature(cxx_constexpr) || !__has_feature(cxx_atomic) || !__has_include(<atomic>)
# undef QT_ATOMIC_FORCE_CXX11
# endif
# elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
# elif defined(_MSC_VER)
// We need MSVC 2015 because of: atomics (2012), constexpr (2015), and unrestricted unions (2015).
// Support for constexpr is not working completely on MSVC 2015 but it's enough for the test.
# else
# undef QT_ATOMIC_FORCE_CXX11
# endif
# ifndef QT_ATOMIC_FORCE_CXX11
# undef QATOMIC_TEST_TYPE
# define QATOMIC_TEST_TYPE unsupported
# endif
#endif
#include <QTest>
#include <QAtomicInt>
#include <limits>
#include <limits.h>
#include <wchar.h>
#if !defined(Q_ATOMIC_INT8_IS_SUPPORTED)
# error "QAtomicInteger for 8-bit types must be supported!"
#endif
#if !defined(Q_ATOMIC_INT16_IS_SUPPORTED)
# error "QAtomicInteger for 16-bit types must be supported!"
#endif
#if !defined(Q_ATOMIC_INT32_IS_SUPPORTED)
# error "QAtomicInteger for 32-bit types must be supported!"
#endif
#if QT_POINTER_SIZE == 8 && !defined(Q_ATOMIC_INT64_IS_SUPPORTED)
# error "QAtomicInteger for 64-bit types must be supported on 64-bit builds!"
#endif
// always supported types:
#define TYPE_SUPPORTED_char 1
#define TYPE_SUPPORTED_uchar 1
#define TYPE_SUPPORTED_schar 1
#define TYPE_SUPPORTED_short 1
#define TYPE_SUPPORTED_ushort 1
#define TYPE_SUPPORTED_char16_t 1
#define TYPE_SUPPORTED_wchar_t 1
#define TYPE_SUPPORTED_int 1
#define TYPE_SUPPORTED_uint 1
#define TYPE_SUPPORTED_long 1
#define TYPE_SUPPORTED_ulong 1
#define TYPE_SUPPORTED_qptrdiff 1
#define TYPE_SUPPORTED_quintptr 1
#define TYPE_SUPPORTED_char32_t 1
#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
# define TYPE_SUPPORTED_qlonglong 1
# define TYPE_SUPPORTED_qulonglong 1
#endif
#ifdef Q_MOC_RUN
# define QATOMIC_TYPE_SUPPORTED(type) 1
#else
# define QATOMIC_TYPE_SUPPORTED2(type) TYPE_SUPPORTED_ ## type
# define QATOMIC_TYPE_SUPPORTED(type) QATOMIC_TYPE_SUPPORTED2(type)
#endif // Q_MOC_RUN
#if QATOMIC_TYPE_SUPPORTED(QATOMIC_TEST_TYPE)
# define TEST_TYPE QATOMIC_TEST_TYPE
#else
# define TEST_TYPE int
# define QATOMIC_TEST_NOT_SUPPORTED
#endif
QT_WARNING_DISABLE_GCC("-Wtype-limits")
QT_WARNING_DISABLE_GCC("-Wsign-compare")
QT_WARNING_DISABLE_CLANG("-Wtautological-constant-out-of-range-compare")
typedef signed char schar;
typedef TEST_TYPE Type;
typedef Type T; // shorthand
enum {
TypeIsUnsigned = Type(-1) > Type(0),
TypeIsSigned = !TypeIsUnsigned
};
template <bool> struct LargeIntTemplate;
template <> struct LargeIntTemplate<true> { typedef quint64 Type; };
template <> struct LargeIntTemplate<false> { typedef qint64 Type; };
typedef LargeIntTemplate<TypeIsUnsigned>::Type LargeInt;
class tst_QAtomicIntegerXX : public QObject
{
Q_OBJECT
void addData();
private Q_SLOTS:
void initTestCase();
void static_checks();
void constructor_data() { addData(); }
void constructor();
void copy_data() { addData(); }
void copy();
void assign_data() { addData(); }
void assign();
void operatorInteger_data() { addData(); }
void operatorInteger();
void loadAcquireStoreRelease_data() { addData(); }
void loadAcquireStoreRelease();
void refDeref_data() { addData(); }
void refDeref();
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 fetchAndOr_data() { addData(); }
void fetchAndOr();
void fetchAndAnd_data() { addData(); }
void fetchAndAnd();
void fetchAndXor_data() { addData(); }
void fetchAndXor();
};
template <bool> inline void booleanHelper() { }
void tst_QAtomicIntegerXX::static_checks()
{
static_assert(sizeof(QAtomicInteger<T>) == sizeof(T));
// statements with no effect
(void) QAtomicInteger<T>::isReferenceCountingNative();
(void) QAtomicInteger<T>::isReferenceCountingWaitFree();
(void) QAtomicInteger<T>::isTestAndSetNative();
(void) QAtomicInteger<T>::isTestAndSetWaitFree();
(void) QAtomicInteger<T>::isFetchAndStoreNative();
(void) QAtomicInteger<T>::isFetchAndStoreWaitFree();
(void) QAtomicInteger<T>::isFetchAndAddNative();
(void) QAtomicInteger<T>::isFetchAndAddWaitFree();
// this is a compile-time test only
booleanHelper<QAtomicInteger<T>::isReferenceCountingWaitFree()>();
booleanHelper<QAtomicInteger<T>::isTestAndSetWaitFree()>();
booleanHelper<QAtomicInteger<T>::isFetchAndStoreWaitFree()>();
booleanHelper<QAtomicInteger<T>::isFetchAndAddWaitFree()>();
}
void tst_QAtomicIntegerXX::addData()
{
typedef std::numeric_limits<T> Limits;
QTest::addColumn<LargeInt>("value");
QTest::newRow("0") << LargeInt(0);
QTest::newRow("+1") << LargeInt(1);
QTest::newRow("42") << LargeInt(42);
if (TypeIsSigned) {
QTest::newRow("-1") << qint64(-1);
QTest::newRow("-47") << qint64(-47);
}
// exercise bits
if (TypeIsSigned && Limits::min() < qint64(SCHAR_MIN))
QTest::newRow("int8_min") << qint64(SCHAR_MIN);
if (Limits::max() > LargeInt(SCHAR_MAX))
QTest::newRow("int8_max") << LargeInt(SCHAR_MAX);
if (Limits::max() > LargeInt(UCHAR_MAX))
QTest::newRow("uint8_max") << LargeInt(UCHAR_MAX);
if (TypeIsSigned && Limits::min() < -qint64(UCHAR_MAX))
QTest::newRow("-uint8_max") << -qint64(UCHAR_MAX);
if (Limits::max() > LargeInt(SHRT_MAX))
QTest::newRow("int16_max") << LargeInt(SHRT_MAX);
if (TypeIsSigned && Limits::min() < qint64(SHRT_MIN))
QTest::newRow("int16_min") << qint64(SHRT_MIN);
if (Limits::max() > LargeInt(USHRT_MAX))
QTest::newRow("uint16_max") << LargeInt(USHRT_MAX);
if (TypeIsSigned && Limits::min() < -qint64(USHRT_MAX))
QTest::newRow("-uint16_max") << -qint64(USHRT_MAX);
if (Limits::max() > LargeInt(INT_MAX))
QTest::newRow("int32_max") << LargeInt(INT_MAX);
if (TypeIsSigned && Limits::min() < qint64(INT_MIN))
QTest::newRow("int32_min") << qint64(INT_MIN);
if (Limits::max() > LargeInt(UINT_MAX))
QTest::newRow("uint32_max") << LargeInt(UINT_MAX);
if (Limits::max() > LargeInt(std::numeric_limits<qint64>::max()))
QTest::newRow("int64_max") << LargeInt(std::numeric_limits<qint64>::max());
if (TypeIsSigned && Limits::min() < -qint64(UINT_MAX))
QTest::newRow("-uint32_max") << -qint64(UINT_MAX);
if (TypeIsSigned)
QTest::newRow(QT_STRINGIFY(QATOMIC_TEST_TYPE) "_min") << qint64(Limits::min());
QTest::newRow(QT_STRINGIFY(QATOMIC_TEST_TYPE) "_max") << LargeInt(Limits::max());
}
void tst_QAtomicIntegerXX::initTestCase()
{
#ifdef QATOMIC_TEST_NOT_SUPPORTED
QSKIP("QAtomicInteger<" QT_STRINGIFY(QATOMIC_TEST_TYPE) "> is not supported on this platform");
#endif
}
void tst_QAtomicIntegerXX::constructor()
{
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
QCOMPARE(atomic.loadRelaxed(), T(value));
QAtomicInteger<T> atomic2 = value;
QCOMPARE(atomic2.loadRelaxed(), T(value));
QVERIFY(atomic.loadRelaxed() >= std::numeric_limits<T>::min());
QVERIFY(atomic.loadRelaxed() <= std::numeric_limits<T>::max());
}
void tst_QAtomicIntegerXX::copy()
{
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
QAtomicInteger<T> copy(atomic);
QCOMPARE(copy.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy2 = atomic;
QCOMPARE(copy2.loadRelaxed(), atomic.loadRelaxed());
// move
QAtomicInteger<T> copy3(std::move(copy));
QCOMPARE(copy3.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy4 = std::move(copy2);
QCOMPARE(copy4.loadRelaxed(), atomic.loadRelaxed());
}
void tst_QAtomicIntegerXX::assign()
{
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
QAtomicInteger<T> copy;
copy = atomic;
QCOMPARE(copy.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy2;
copy2 = atomic; // operator=(const QAtomicInteger &)
QCOMPARE(copy2.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy2bis;
copy2bis = atomic.loadRelaxed(); // operator=(T)
QCOMPARE(copy2bis.loadRelaxed(), atomic.loadRelaxed());
// move
QAtomicInteger<T> copy3;
copy3 = std::move(copy);
QCOMPARE(copy3.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy4;
copy4 = std::move(copy2);
QCOMPARE(copy4.loadRelaxed(), atomic.loadRelaxed());
}
void tst_QAtomicIntegerXX::operatorInteger()
{
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
T val2 = atomic;
QCOMPARE(val2, atomic.loadRelaxed());
QCOMPARE(val2, T(value));
}
void tst_QAtomicIntegerXX::loadAcquireStoreRelease()
{
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
QCOMPARE(atomic.loadAcquire(), T(value));
atomic.storeRelease(~value);
QCOMPARE(atomic.loadAcquire(), T(~value));
atomic.storeRelease(value);
QCOMPARE(atomic.loadRelaxed(), T(value));
}
void tst_QAtomicIntegerXX::refDeref()
{
QFETCH(LargeInt, value);
const bool needToPreventOverflow = TypeIsSigned && value == std::numeric_limits<T>::max();
const bool needToPreventUnderflow = TypeIsSigned && value == std::numeric_limits<T>::min();
T nextValue = T(value);
if (!needToPreventOverflow)
++nextValue;
T prevValue = T(value);
if (!needToPreventUnderflow)
--prevValue;
QAtomicInteger<T> atomic(value);
if (!needToPreventOverflow) {
QCOMPARE(atomic.ref(), (nextValue != 0));
QCOMPARE(atomic.loadRelaxed(), nextValue);
QCOMPARE(atomic.deref(), (value != 0));
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventUnderflow) {
QCOMPARE(atomic.deref(), (prevValue != 0));
QCOMPARE(atomic.loadRelaxed(), prevValue);
QCOMPARE(atomic.ref(), (value != 0));
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventOverflow) {
QCOMPARE(++atomic, nextValue);
QCOMPARE(--atomic, T(value));
}
if (!needToPreventUnderflow) {
QCOMPARE(--atomic, prevValue);
QCOMPARE(++atomic, T(value));
}
if (!needToPreventOverflow) {
QCOMPARE(atomic++, T(value));
QCOMPARE(atomic--, nextValue);
}
if (!needToPreventUnderflow) {
QCOMPARE(atomic--, T(value));
QCOMPARE(atomic++, prevValue);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
}
void tst_QAtomicIntegerXX::testAndSet()
{
QFETCH(LargeInt, value);
T newValue = ~T(value);
QAtomicInteger<T> atomic(value);
QVERIFY(atomic.testAndSetRelaxed(value, newValue));
QCOMPARE(atomic.loadRelaxed(), newValue);
QVERIFY(!atomic.testAndSetRelaxed(value, newValue));
QVERIFY(atomic.testAndSetRelaxed(newValue, value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QVERIFY(atomic.testAndSetAcquire(value, newValue));
QCOMPARE(atomic.loadRelaxed(), newValue);
QVERIFY(!atomic.testAndSetAcquire(value, newValue));
QVERIFY(atomic.testAndSetAcquire(newValue, value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QVERIFY(atomic.testAndSetRelease(value, newValue));
QCOMPARE(atomic.loadAcquire(), newValue);
QVERIFY(!atomic.testAndSetRelease(value, newValue));
QVERIFY(atomic.testAndSetRelease(newValue, value));
QCOMPARE(atomic.loadAcquire(), T(value));
QVERIFY(atomic.testAndSetOrdered(value, newValue));
QCOMPARE(atomic.loadAcquire(), newValue);
QVERIFY(!atomic.testAndSetOrdered(value, newValue));
QVERIFY(atomic.testAndSetOrdered(newValue, value));
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.loadRelaxed(), newValue);
QVERIFY(!atomic.testAndSetRelaxed(value, newValue, oldValue));
QCOMPARE(oldValue, newValue);
QVERIFY(atomic.testAndSetRelaxed(newValue, value, oldValue));
QCOMPARE(atomic.loadRelaxed(), T(value));
QVERIFY(atomic.testAndSetAcquire(value, newValue, oldValue));
QCOMPARE(atomic.loadRelaxed(), newValue);
QVERIFY(!atomic.testAndSetAcquire(value, newValue, oldValue));
QCOMPARE(oldValue, newValue);
QVERIFY(atomic.testAndSetAcquire(newValue, value, oldValue));
QCOMPARE(atomic.loadRelaxed(), 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);
T newValue = ~T(value);
QAtomicInteger<T> atomic(value);
QCOMPARE(atomic.fetchAndStoreRelaxed(newValue), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue);
QCOMPARE(atomic.fetchAndStoreRelaxed(value), newValue);
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndStoreAcquire(newValue), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue);
QCOMPARE(atomic.fetchAndStoreAcquire(value), newValue);
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndStoreRelease(newValue), T(value));
QCOMPARE(atomic.loadAcquire(), newValue);
QCOMPARE(atomic.fetchAndStoreRelease(value), newValue);
QCOMPARE(atomic.loadAcquire(), T(value));
QCOMPARE(atomic.fetchAndStoreOrdered(newValue), T(value));
QCOMPARE(atomic.loadAcquire(), newValue);
QCOMPARE(atomic.fetchAndStoreOrdered(value), newValue);
QCOMPARE(atomic.loadAcquire(), T(value));
}
void tst_QAtomicIntegerXX::fetchAndAdd()
{
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
T parcel1 = 42;
T parcel2 = T(0-parcel1);
const bool needToPreventOverflow = TypeIsSigned && value > std::numeric_limits<T>::max() + parcel2;
const bool needToPreventUnderflow = TypeIsSigned && value < std::numeric_limits<T>::min() + parcel1;
T newValue1 = T(value);
if (!needToPreventOverflow)
newValue1 += parcel1;
T newValue2 = T(value);
if (!needToPreventUnderflow)
newValue2 += parcel2;
if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndAddRelaxed(parcel1), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue1);
QCOMPARE(atomic.fetchAndAddRelaxed(parcel2), newValue1);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndAddRelaxed(parcel2), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue2);
QCOMPARE(atomic.fetchAndAddRelaxed(parcel1), newValue2);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndAddAcquire(parcel1), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue1);
QCOMPARE(atomic.fetchAndAddAcquire(parcel2), newValue1);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndAddAcquire(parcel2), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue2);
QCOMPARE(atomic.fetchAndAddAcquire(parcel1), newValue2);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndAddRelease(parcel1), T(value));
QCOMPARE(atomic.loadAcquire(), newValue1);
QCOMPARE(atomic.fetchAndAddRelease(parcel2), newValue1);
}
QCOMPARE(atomic.loadAcquire(), T(value));
if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndAddRelease(parcel2), T(value));
QCOMPARE(atomic.loadAcquire(), newValue2);
QCOMPARE(atomic.fetchAndAddRelease(parcel1), newValue2);
}
QCOMPARE(atomic.loadAcquire(), T(value));
if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndAddOrdered(parcel1), T(value));
QCOMPARE(atomic.loadAcquire(), newValue1);
QCOMPARE(atomic.fetchAndAddOrdered(parcel2), newValue1);
}
QCOMPARE(atomic.loadAcquire(), T(value));
if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndAddOrdered(parcel2), T(value));
QCOMPARE(atomic.loadAcquire(), newValue2);
QCOMPARE(atomic.fetchAndAddOrdered(parcel1), newValue2);
}
QCOMPARE(atomic.loadAcquire(), T(value));
// operator+=
if (!needToPreventOverflow) {
QCOMPARE(atomic += parcel1, newValue1);
QCOMPARE(atomic += parcel2, T(value));
}
if (!needToPreventUnderflow) {
QCOMPARE(atomic += parcel2, newValue2);
QCOMPARE(atomic += parcel1, T(value));
}
}
void tst_QAtomicIntegerXX::fetchAndSub()
{
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
T parcel1 = 42;
T parcel2 = T(0-parcel1);
const bool needToPreventOverflow = TypeIsSigned && value > std::numeric_limits<T>::max() - parcel1;
const bool needToPreventUnderflow = TypeIsSigned && value < std::numeric_limits<T>::min() - parcel2;
T newValue1 = T(value);
if (!needToPreventUnderflow)
newValue1 -= parcel1;
T newValue2 = T(value);
if (!needToPreventOverflow)
newValue2 -= parcel2;
if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndSubRelaxed(parcel1), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue1);
QCOMPARE(atomic.fetchAndSubRelaxed(parcel2), newValue1);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndSubRelaxed(parcel2), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue2);
QCOMPARE(atomic.fetchAndSubRelaxed(parcel1), newValue2);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndSubAcquire(parcel1), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue1);
QCOMPARE(atomic.fetchAndSubAcquire(parcel2), newValue1);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndSubAcquire(parcel2), T(value));
QCOMPARE(atomic.loadRelaxed(), newValue2);
QCOMPARE(atomic.fetchAndSubAcquire(parcel1), newValue2);
}
QCOMPARE(atomic.loadRelaxed(), T(value));
if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndSubRelease(parcel1), T(value));
QCOMPARE(atomic.loadAcquire(), newValue1);
QCOMPARE(atomic.fetchAndSubRelease(parcel2), newValue1);
}
QCOMPARE(atomic.loadAcquire(), T(value));
if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndSubRelease(parcel2), T(value));
QCOMPARE(atomic.loadAcquire(), newValue2);
QCOMPARE(atomic.fetchAndSubRelease(parcel1), newValue2);
}
QCOMPARE(atomic.loadAcquire(), T(value));
if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndSubOrdered(parcel1), T(value));
QCOMPARE(atomic.loadAcquire(), newValue1);
QCOMPARE(atomic.fetchAndSubOrdered(parcel2), newValue1);
}
QCOMPARE(atomic.loadAcquire(), T(value));
if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndSubOrdered(parcel2), T(value));
QCOMPARE(atomic.loadAcquire(), newValue2);
QCOMPARE(atomic.fetchAndSubOrdered(parcel1), newValue2);
}
QCOMPARE(atomic.loadAcquire(), T(value));
// operator-=
if (!needToPreventUnderflow) {
QCOMPARE(atomic -= parcel1, newValue1);
QCOMPARE(atomic -= parcel2, T(value));
}
if (!needToPreventOverflow) {
QCOMPARE(atomic -= parcel2, newValue2);
QCOMPARE(atomic -= parcel1, 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.loadRelaxed(), T(value | 1));
QCOMPARE(atomic.fetchAndOrRelaxed(minusOne), T(value | 1));
QCOMPARE(atomic.loadRelaxed(), minusOne);
atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndOrAcquire(zero), T(value));
QCOMPARE(atomic.fetchAndOrAcquire(one), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value | 1));
QCOMPARE(atomic.fetchAndOrAcquire(minusOne), T(value | 1));
QCOMPARE(atomic.loadRelaxed(), minusOne);
atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndOrRelease(zero), T(value));
QCOMPARE(atomic.fetchAndOrRelease(one), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value | 1));
QCOMPARE(atomic.fetchAndOrRelease(minusOne), T(value | 1));
QCOMPARE(atomic.loadRelaxed(), minusOne);
atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndOrOrdered(zero), T(value));
QCOMPARE(atomic.fetchAndOrOrdered(one), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value | 1));
QCOMPARE(atomic.fetchAndOrOrdered(minusOne), T(value | 1));
QCOMPARE(atomic.loadRelaxed(), minusOne);
atomic.storeRelaxed(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.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAndRelaxed(f), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value & 0xf));
QCOMPARE(atomic.fetchAndAndRelaxed(zero), T(value & 0xf));
QCOMPARE(atomic.loadRelaxed(), zero);
atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndAndAcquire(minusOne), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAndAcquire(f), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value & 0xf));
QCOMPARE(atomic.fetchAndAndAcquire(zero), T(value & 0xf));
QCOMPARE(atomic.loadRelaxed(), zero);
atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndAndRelease(minusOne), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAndRelease(f), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value & 0xf));
QCOMPARE(atomic.fetchAndAndRelease(zero), T(value & 0xf));
QCOMPARE(atomic.loadRelaxed(), zero);
atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndAndOrdered(minusOne), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAndOrdered(f), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value & 0xf));
QCOMPARE(atomic.fetchAndAndOrdered(zero), T(value & 0xf));
QCOMPARE(atomic.loadRelaxed(), zero);
atomic.storeRelaxed(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.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelaxed(pattern), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value ^ pattern));
QCOMPARE(atomic.fetchAndXorRelaxed(pattern), T(value ^ pattern));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelaxed(minusOne), T(value));
QCOMPARE(atomic.loadRelaxed(), T(~value));
QCOMPARE(atomic.fetchAndXorRelaxed(minusOne), T(~value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorAcquire(zero), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorAcquire(pattern), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value ^ pattern));
QCOMPARE(atomic.fetchAndXorAcquire(pattern), T(value ^ pattern));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorAcquire(minusOne), T(value));
QCOMPARE(atomic.loadRelaxed(), T(~value));
QCOMPARE(atomic.fetchAndXorAcquire(minusOne), T(~value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelease(zero), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelease(pattern), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value ^ pattern));
QCOMPARE(atomic.fetchAndXorRelease(pattern), T(value ^ pattern));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelease(minusOne), T(value));
QCOMPARE(atomic.loadRelaxed(), T(~value));
QCOMPARE(atomic.fetchAndXorRelease(minusOne), T(~value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorOrdered(zero), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorOrdered(pattern), T(value));
QCOMPARE(atomic.loadRelaxed(), T(value ^ pattern));
QCOMPARE(atomic.fetchAndXorOrdered(pattern), T(value ^ pattern));
QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorOrdered(minusOne), T(value));
QCOMPARE(atomic.loadRelaxed(), T(~value));
QCOMPARE(atomic.fetchAndXorOrdered(minusOne), T(~value));
QCOMPARE(atomic.loadRelaxed(), 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"
QTEST_APPLESS_MAIN(tst_QAtomicIntegerXX)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_uchar Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_uchar
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=uchar
tst_QAtomicIntegerXX=tst_QAtomicInteger_uchar
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_uint Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_uint
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=uint
tst_QAtomicIntegerXX=tst_QAtomicInteger_uint
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_ulong Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_ulong
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=ulong
tst_QAtomicIntegerXX=tst_QAtomicInteger_ulong
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_ushort Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_ushort
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=ushort
tst_QAtomicIntegerXX=tst_QAtomicInteger_ushort
)

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_wchar_t Test:
#####################################################################
qt_internal_add_test(tst_qatomicinteger_wchar_t
SOURCES
../tst_qatomicinteger.cpp
DEFINES
QATOMIC_TEST_TYPE=wchar_t
tst_QAtomicIntegerXX=tst_QAtomicInteger_wchar_t
)

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicpointer Test:
#####################################################################
qt_internal_add_test(tst_qatomicpointer
SOURCES
tst_qatomicpointer.cpp
)

View File

@ -0,0 +1,680 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <qatomic.h>
#include <limits.h>
class tst_QAtomicPointer : public QObject
{
Q_OBJECT
private slots:
void warningFree();
void alignment();
void constructor();
void copy_constructor();
void assignment_operator();
void isTestAndSetNative();
void isTestAndSetWaitFree();
void testAndSet();
void isFetchAndStoreNative();
void isFetchAndStoreWaitFree();
void fetchAndStore();
void isFetchAndAddNative();
void isFetchAndAddWaitFree();
void fetchAndAdd_data();
void fetchAndAdd();
void constAndVolatile();
void forwardDeclared();
void operators();
private:
static void warningFreeHelper();
};
struct WFHC
{
void bar() {}
};
void tst_QAtomicPointer::warningFreeHelper()
{
qFatal("This code is bogus, and shouldn't be run. We're looking for compiler warnings only.");
QBasicAtomicPointer<WFHC> p = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
p.loadRelaxed()->bar();
WFHC *expectedValue = nullptr;
WFHC *newValue = nullptr;
qptrdiff valueToAdd = 0;
p.testAndSetRelaxed(expectedValue, newValue);
p.testAndSetAcquire(expectedValue, newValue);
p.testAndSetRelease(expectedValue, newValue);
p.testAndSetOrdered(expectedValue, newValue);
p.fetchAndStoreRelaxed(newValue);
p.fetchAndStoreAcquire(newValue);
p.fetchAndStoreRelease(newValue);
p.fetchAndStoreOrdered(newValue);
p.fetchAndAddRelaxed(valueToAdd);
p.fetchAndAddAcquire(valueToAdd);
p.fetchAndAddRelease(valueToAdd);
p.fetchAndAddOrdered(valueToAdd);
}
void tst_QAtomicPointer::warningFree()
{
// This is a compile time check for warnings.
// No need for actual work here.
void (*foo)() = &warningFreeHelper;
(void)foo;
}
void tst_QAtomicPointer::alignment()
{
static_assert(alignof(QBasicAtomicPointer<void>) == alignof(void*));
}
void tst_QAtomicPointer::constructor()
{
void *one = this;
QAtomicPointer<void> atomic1 = one;
QCOMPARE(atomic1.loadRelaxed(), one);
void *two = &one;
QAtomicPointer<void> atomic2 = two;
QCOMPARE(atomic2.loadRelaxed(), two);
void *three = &two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic3.loadRelaxed(), three);
}
void tst_QAtomicPointer::copy_constructor()
{
void *one = this;
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic1_copy = atomic1;
QCOMPARE(atomic1_copy.loadRelaxed(), one);
QCOMPARE(atomic1_copy.loadRelaxed(), atomic1.loadRelaxed());
void *two = &one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic2_copy = atomic2;
QCOMPARE(atomic2_copy.loadRelaxed(), two);
QCOMPARE(atomic2_copy.loadRelaxed(), atomic2.loadRelaxed());
void *three = &two;
QAtomicPointer<void> atomic3 = three;
QAtomicPointer<void> atomic3_copy = atomic3;
QCOMPARE(atomic3_copy.loadRelaxed(), three);
QCOMPARE(atomic3_copy.loadRelaxed(), atomic3.loadRelaxed());
}
void tst_QAtomicPointer::assignment_operator()
{
void *one = this;
void *two = &one;
void *three = &two;
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
atomic1 = two;
atomic2 = three;
atomic3 = one;
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
void tst_QAtomicPointer::isTestAndSetNative()
{
#if defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE)
// the runtime test should say the same thing
QVERIFY(QAtomicPointer<void>::isTestAndSetNative());
# if (defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE) \
|| defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE)
// could be either, just want to make sure the function is implemented
QVERIFY(QAtomicPointer<void>::isTestAndSetNative()
|| !QAtomicPointer<void>::isTestAndSetNative());
# if (defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE)
// the runtime test should say the same thing
QVERIFY(!QAtomicPointer<void>::isTestAndSetNative());
# if (defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_SOMETIMES_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#else
# error "Q_ATOMIC_POINTER_TEST_AND_SET_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
#endif
}
void tst_QAtomicPointer::isTestAndSetWaitFree()
{
#if defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_WAIT_FREE)
// the runtime test should say the same thing
QVERIFY(QAtomicPointer<void>::isTestAndSetWaitFree());
// enforce some invariants
QVERIFY(QAtomicPointer<void>::isTestAndSetNative());
# if defined(Q_ATOMIC_POINTER_TEST_AND_SET_IS_NOT_NATIVE)
# error "Reference counting cannot be wait-free and unsupported at the same time!"
# endif
#else
// the runtime test should say the same thing
QVERIFY(!QAtomicPointer<void>::isTestAndSetWaitFree());
#endif
}
void tst_QAtomicPointer::testAndSet()
{
void *one = this;
void *two = &one;
void *three = &two;
{
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
QVERIFY(atomic1.testAndSetRelaxed(one, two));
QVERIFY(atomic2.testAndSetRelaxed(two, three));
QVERIFY(atomic3.testAndSetRelaxed(three, one));
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
{
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
QVERIFY(atomic1.testAndSetAcquire(one, two));
QVERIFY(atomic2.testAndSetAcquire(two, three));
QVERIFY(atomic3.testAndSetAcquire(three, one));
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
{
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
QVERIFY(atomic1.testAndSetRelease(one, two));
QVERIFY(atomic2.testAndSetRelease(two, three));
QVERIFY(atomic3.testAndSetRelease(three, one));
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
{
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
QVERIFY(atomic1.testAndSetOrdered(one, two));
QVERIFY(atomic2.testAndSetOrdered(two, three));
QVERIFY(atomic3.testAndSetOrdered(three, one));
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
}
void tst_QAtomicPointer::isFetchAndStoreNative()
{
#if defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE)
// the runtime test should say the same thing
QVERIFY(QAtomicPointer<void>::isFetchAndStoreNative());
# if (defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_SOMETIMES_NATIVE) \
|| defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_SOMETIMES_NATIVE)
// could be either, just want to make sure the function is implemented
QVERIFY(QAtomicPointer<void>::isFetchAndStoreNative()
|| !QAtomicPointer<void>::isFetchAndStoreNative());
# if (defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE)
// the runtime test should say the same thing
QVERIFY(!QAtomicPointer<void>::isFetchAndStoreNative());
# if (defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_SOMETIMES_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#else
# error "Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
#endif
}
void tst_QAtomicPointer::isFetchAndStoreWaitFree()
{
#if defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_WAIT_FREE)
// the runtime test should say the same thing
QVERIFY(QAtomicPointer<void>::isFetchAndStoreWaitFree());
// enforce some invariants
QVERIFY(QAtomicPointer<void>::isFetchAndStoreNative());
# if defined(Q_ATOMIC_POINTER_FETCH_AND_STORE_IS_NOT_NATIVE)
# error "Reference counting cannot be wait-free and unsupported at the same time!"
# endif
#else
// the runtime test should say the same thing
QVERIFY(!QAtomicPointer<void>::isFetchAndStoreWaitFree());
#endif
}
void tst_QAtomicPointer::fetchAndStore()
{
void *one = this;
void *two = &one;
void *three = &two;
{
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
QCOMPARE(atomic1.fetchAndStoreRelaxed(two), one);
QCOMPARE(atomic2.fetchAndStoreRelaxed(three), two);
QCOMPARE(atomic3.fetchAndStoreRelaxed(one), three);
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
{
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
QCOMPARE(atomic1.fetchAndStoreAcquire(two), one);
QCOMPARE(atomic2.fetchAndStoreAcquire(three), two);
QCOMPARE(atomic3.fetchAndStoreAcquire(one), three);
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
{
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
QCOMPARE(atomic1.fetchAndStoreRelease(two), one);
QCOMPARE(atomic2.fetchAndStoreRelease(three), two);
QCOMPARE(atomic3.fetchAndStoreRelease(one), three);
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
{
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
QCOMPARE(atomic1.loadRelaxed(), one);
QCOMPARE(atomic2.loadRelaxed(), two);
QCOMPARE(atomic3.loadRelaxed(), three);
QCOMPARE(atomic1.fetchAndStoreOrdered(two), one);
QCOMPARE(atomic2.fetchAndStoreOrdered(three), two);
QCOMPARE(atomic3.fetchAndStoreOrdered(one), three);
QCOMPARE(atomic1.loadRelaxed(), two);
QCOMPARE(atomic2.loadRelaxed(), three);
QCOMPARE(atomic3.loadRelaxed(), one);
}
}
void tst_QAtomicPointer::isFetchAndAddNative()
{
#if defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE)
// the runtime test should say the same thing
QVERIFY(QAtomicPointer<void>::isFetchAndAddNative());
# if (defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_SOMETIMES_NATIVE) \
|| defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_SOMETIMES_NATIVE)
// could be either, just want to make sure the function is implemented
QVERIFY(QAtomicPointer<void>::isFetchAndAddNative()
|| !QAtomicPointer<void>::isFetchAndAddNative());
# if (defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#elif defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE)
// the runtime test should say the same thing
QVERIFY(!QAtomicPointer<void>::isFetchAndAddNative());
# if (defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_ALWAYS_NATIVE) \
|| defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_SOMETIMES_NATIVE))
# error "Define only one of Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE"
# endif
#else
# error "Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_{ALWAYS,SOMTIMES,NOT}_NATIVE is not defined"
#endif
}
void tst_QAtomicPointer::isFetchAndAddWaitFree()
{
#if defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_WAIT_FREE)
// the runtime test should say the same thing
QVERIFY(QAtomicPointer<void>::isFetchAndAddWaitFree());
// enforce some invariants
QVERIFY(QAtomicPointer<void>::isFetchAndAddNative());
# if defined(Q_ATOMIC_POINTER_FETCH_AND_ADD_IS_NOT_NATIVE)
# error "Reference counting cannot be wait-free and unsupported at the same time!"
# endif
#else
// the runtime test should say the same thing
QVERIFY(!QAtomicPointer<void>::isFetchAndAddWaitFree());
#endif
}
void tst_QAtomicPointer::fetchAndAdd_data()
{
QTest::addColumn<int>("valueToAdd");
QTest::newRow("0") << 0;
QTest::newRow("1") << 1;
QTest::newRow("2") << 2;
QTest::newRow("10") << 10;
QTest::newRow("31") << 31;
QTest::newRow("51") << 51;
QTest::newRow("72") << 72;
QTest::newRow("810") << 810;
QTest::newRow("631") << 631;
QTest::newRow("451") << 451;
QTest::newRow("272") << 272;
QTest::newRow("1810") << 1810;
QTest::newRow("3631") << 3631;
QTest::newRow("5451") << 5451;
QTest::newRow("7272") << 7272;
QTest::newRow("-1") << -1;
QTest::newRow("-2") << -2;
QTest::newRow("-10") << -10;
QTest::newRow("-31") << -31;
QTest::newRow("-51") << -51;
QTest::newRow("-72") << -72;
QTest::newRow("-810") << -810;
QTest::newRow("-631") << -631;
QTest::newRow("-451") << -451;
QTest::newRow("-272") << -272;
QTest::newRow("-1810") << -1810;
QTest::newRow("-3631") << -3631;
QTest::newRow("-5451") << -5451;
QTest::newRow("-7272") << -7272;
}
void tst_QAtomicPointer::fetchAndAdd()
{
QFETCH(int, valueToAdd);
char c;
char *pc = &c;
short s;
short *ps = &s;
int i;
int *pi = &i;
{
QAtomicPointer<char> pointer1 = pc;
// cast to void* in order to avoid QCOMPARE to compare string content of the char*
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddRelaxed(valueToAdd)), static_cast<void*>(pc));
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddRelaxed(-valueToAdd)), static_cast<void*>(pc + valueToAdd));
QCOMPARE(static_cast<void*>(pointer1.loadRelaxed()), static_cast<void*>(pc));
QAtomicPointer<short> pointer2 = ps;
QCOMPARE(pointer2.fetchAndAddRelaxed(valueToAdd), ps);
QCOMPARE(pointer2.fetchAndAddRelaxed(-valueToAdd), ps + valueToAdd);
QCOMPARE(pointer2.loadRelaxed(), ps);
QAtomicPointer<int> pointer3 = pi;
QCOMPARE(pointer3.fetchAndAddRelaxed(valueToAdd), pi);
QCOMPARE(pointer3.fetchAndAddRelaxed(-valueToAdd), pi + valueToAdd);
QCOMPARE(pointer3.loadRelaxed(), pi);
}
{
QAtomicPointer<char> pointer1 = pc;
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddAcquire(valueToAdd)), static_cast<void*>(pc));
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddAcquire(-valueToAdd)), static_cast<void*>(pc + valueToAdd));
QCOMPARE(static_cast<void*>(pointer1.loadRelaxed()), static_cast<void*>(pc));
QAtomicPointer<short> pointer2 = ps;
QCOMPARE(pointer2.fetchAndAddAcquire(valueToAdd), ps);
QCOMPARE(pointer2.fetchAndAddAcquire(-valueToAdd), ps + valueToAdd);
QCOMPARE(pointer2.loadRelaxed(), ps);
QAtomicPointer<int> pointer3 = pi;
QCOMPARE(pointer3.fetchAndAddAcquire(valueToAdd), pi);
QCOMPARE(pointer3.fetchAndAddAcquire(-valueToAdd), pi + valueToAdd);
QCOMPARE(pointer3.loadRelaxed(), pi);
}
{
QAtomicPointer<char> pointer1 = pc;
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddRelease(valueToAdd)), static_cast<void*>(pc));
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddRelease(-valueToAdd)), static_cast<void*>(pc + valueToAdd));
QCOMPARE(static_cast<void*>(pointer1.loadRelaxed()), static_cast<void*>(pc));
QAtomicPointer<short> pointer2 = ps;
QCOMPARE(pointer2.fetchAndAddRelease(valueToAdd), ps);
QCOMPARE(pointer2.fetchAndAddRelease(-valueToAdd), ps + valueToAdd);
QCOMPARE(pointer2.loadRelaxed(), ps);
QAtomicPointer<int> pointer3 = pi;
QCOMPARE(pointer3.fetchAndAddRelease(valueToAdd), pi);
QCOMPARE(pointer3.fetchAndAddRelease(-valueToAdd), pi + valueToAdd);
QCOMPARE(pointer3.loadRelaxed(), pi);
}
{
QAtomicPointer<char> pointer1 = pc;
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddOrdered(valueToAdd)), static_cast<void*>(pc));
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddOrdered(-valueToAdd)), static_cast<void*>(pc + valueToAdd));
QCOMPARE(static_cast<void*>(pointer1.loadRelaxed()), static_cast<void*>(pc));
QAtomicPointer<short> pointer2 = ps;
QCOMPARE(pointer2.fetchAndAddOrdered(valueToAdd), ps);
QCOMPARE(pointer2.fetchAndAddOrdered(-valueToAdd), ps + valueToAdd);
QCOMPARE(pointer2.loadRelaxed(), ps);
QAtomicPointer<int> pointer3 = pi;
QCOMPARE(pointer3.fetchAndAddOrdered(valueToAdd), pi);
QCOMPARE(pointer3.fetchAndAddOrdered(-valueToAdd), pi + valueToAdd);
QCOMPARE(pointer3.loadRelaxed(), pi);
}
}
template <typename T> void constAndVolatile_helper()
{
T *one = nullptr;
T *two = &one;
T *three = &two;
{
QAtomicPointer<T> atomic1 = one;
QAtomicPointer<T> atomic2 = two;
QAtomicPointer<T> atomic3 = three;
QVERIFY(atomic1.loadRelaxed() == one);
QVERIFY(atomic2.loadRelaxed() == two);
QVERIFY(atomic3.loadRelaxed() == three);
QVERIFY(atomic1.fetchAndStoreRelaxed(two) == one);
QVERIFY(atomic2.fetchAndStoreRelaxed(three) == two);
QVERIFY(atomic3.fetchAndStoreRelaxed(one) == three);
QVERIFY(atomic1.loadRelaxed() == two);
QVERIFY(atomic2.loadRelaxed() == three);
QVERIFY(atomic3.loadRelaxed() == one);
}
{
QAtomicPointer<T> atomic1 = one;
QAtomicPointer<T> atomic2 = two;
QAtomicPointer<T> atomic3 = three;
QVERIFY(atomic1.loadRelaxed() == one);
QVERIFY(atomic2.loadRelaxed() == two);
QVERIFY(atomic3.loadRelaxed() == three);
QVERIFY(atomic1.testAndSetRelaxed(one, two));
QVERIFY(atomic2.testAndSetRelaxed(two, three));
QVERIFY(atomic3.testAndSetRelaxed(three, one));
QVERIFY(atomic1.loadRelaxed() == two);
QVERIFY(atomic2.loadRelaxed() == three);
QVERIFY(atomic3.loadRelaxed() == one);
}
}
void tst_QAtomicPointer::constAndVolatile()
{
constAndVolatile_helper<void>();
constAndVolatile_helper<const void>();
constAndVolatile_helper<volatile void>();
constAndVolatile_helper<const volatile void>();
}
struct ForwardDeclared;
struct ContainsForwardDeclared
{
QAtomicPointer<ForwardDeclared> ptr;
};
void tst_QAtomicPointer::forwardDeclared()
{
// this is just a compilation test
QAtomicPointer<ForwardDeclared> ptr;
ContainsForwardDeclared cfd;
Q_UNUSED(ptr)
Q_UNUSED(cfd)
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(nullptr);
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"

View File

@ -0,0 +1,20 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfuture Test:
#####################################################################
qt_internal_add_test(tst_qfuture
SOURCES
tst_qfuture.cpp
LIBRARIES
Qt::CorePrivate
)
qt_internal_extend_target(tst_qfuture CONDITION MSVC
COMPILE_OPTIONS
/bigobj
)
qt_internal_undefine_global_definition(tst_qfuture QT_NO_JAVA_STYLE_ITERATORS)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfuturesynchronizer Test:
#####################################################################
qt_internal_add_test(tst_qfuturesynchronizer
SOURCES
tst_qfuturesynchronizer.cpp
)

View File

@ -0,0 +1,119 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtCore/qfuturesynchronizer.h>
#include <QtCore/qfuture.h>
class tst_QFutureSynchronizer : public QObject
{
Q_OBJECT
private Q_SLOTS:
void construction();
void addFuture();
void cancelOnWait();
void clearFutures();
void futures();
void setFuture();
void waitForFinished();
};
void tst_QFutureSynchronizer::construction()
{
QFuture<void> future;
QFutureSynchronizer<void> synchronizer;
QFutureSynchronizer<void> synchronizerWithFuture(future);
QCOMPARE(synchronizer.futures().size(), 0);
QCOMPARE(synchronizerWithFuture.futures().size(), 1);
}
void tst_QFutureSynchronizer::addFuture()
{
QFutureSynchronizer<void> synchronizer;
synchronizer.addFuture(QFuture<void>());
QFuture<void> future;
synchronizer.addFuture(future);
synchronizer.addFuture(future);
QCOMPARE(synchronizer.futures().size(), 3);
}
void tst_QFutureSynchronizer::cancelOnWait()
{
QFutureSynchronizer<void> synchronizer;
QVERIFY(!synchronizer.cancelOnWait());
synchronizer.setCancelOnWait(true);
QVERIFY(synchronizer.cancelOnWait());
synchronizer.setCancelOnWait(false);
QVERIFY(!synchronizer.cancelOnWait());
synchronizer.setCancelOnWait(true);
QVERIFY(synchronizer.cancelOnWait());
}
void tst_QFutureSynchronizer::clearFutures()
{
QFutureSynchronizer<void> synchronizer;
synchronizer.clearFutures();
QVERIFY(synchronizer.futures().isEmpty());
synchronizer.addFuture(QFuture<void>());
QFuture<void> future;
synchronizer.addFuture(future);
synchronizer.addFuture(future);
synchronizer.clearFutures();
QVERIFY(synchronizer.futures().isEmpty());
}
void tst_QFutureSynchronizer::futures()
{
QFutureSynchronizer<void> synchronizer;
QList<QFuture<void> > futures;
for (int i=0; i<100; i++) {
QFuture<void> future;
futures.append(future);
synchronizer.addFuture(future);
}
QCOMPARE(futures.size(), synchronizer.futures().size());
}
void tst_QFutureSynchronizer::setFuture()
{
QFutureSynchronizer<void> synchronizer;
for (int i=0; i<100; i++) {
synchronizer.addFuture(QFuture<void>());
}
QCOMPARE(synchronizer.futures().size(), 100);
QFuture<void> future;
synchronizer.setFuture(future);
QCOMPARE(synchronizer.futures().size(), 1);
}
void tst_QFutureSynchronizer::waitForFinished()
{
QFutureSynchronizer<void> synchronizer;
for (int i=0; i<100; i++) {
synchronizer.addFuture(QFuture<void>());
}
synchronizer.waitForFinished();
const QList<QFuture<void> > futures = synchronizer.futures();
for (int i=0; i<100; i++) {
QVERIFY(futures.at(i).isFinished());
}
}
QTEST_MAIN(tst_QFutureSynchronizer)
#include "tst_qfuturesynchronizer.moc"

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfuturewatcher Test:
#####################################################################
qt_internal_add_test(tst_qfuturewatcher
SOURCES
tst_qfuturewatcher.cpp
LIBRARIES
Qt::Concurrent
Qt::CorePrivate
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmutex Test:
#####################################################################
qt_internal_add_test(tst_qmutex
SOURCES
tst_qmutex.cpp
LIBRARIES
Qt::CorePrivate
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmutexlocker Test:
#####################################################################
qt_internal_add_test(tst_qmutexlocker
SOURCES
tst_qmutexlocker.cpp
)

View File

@ -0,0 +1,257 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QCoreApplication>
#include <QMutexLocker>
#include <QSemaphore>
#include <QThread>
class tst_QMutexLockerThread : public QThread
{
public:
QRecursiveMutex mutex;
QSemaphore semaphore, testSemaphore;
void waitForTest()
{
semaphore.release();
testSemaphore.acquire();
}
};
class tst_QMutexLocker : public QObject
{
Q_OBJECT
public:
tst_QMutexLockerThread *thread;
void waitForThread()
{
thread->semaphore.acquire();
}
void releaseThread()
{
thread->testSemaphore.release();
}
private slots:
void scopeTest();
void unlockAndRelockTest();
void lockerStateTest();
void moveSemantics();
};
void tst_QMutexLocker::scopeTest()
{
class ScopeTestThread : public tst_QMutexLockerThread
{
public:
void run() override
{
waitForTest();
{
QMutexLocker locker(&mutex);
QVERIFY(locker.isLocked());
waitForTest();
}
waitForTest();
}
};
thread = new ScopeTestThread;
thread->start();
waitForThread();
// mutex should be unlocked before entering the scope that creates the QMutexLocker
QVERIFY(thread->mutex.tryLock());
thread->mutex.unlock();
releaseThread();
waitForThread();
// mutex should be locked by the QMutexLocker
QVERIFY(!thread->mutex.tryLock());
releaseThread();
waitForThread();
// mutex should be unlocked when the QMutexLocker goes out of scope
QVERIFY(thread->mutex.tryLock());
thread->mutex.unlock();
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
void tst_QMutexLocker::unlockAndRelockTest()
{
class UnlockAndRelockThread : public tst_QMutexLockerThread
{
public:
void run() override
{
QMutexLocker locker(&mutex);
QVERIFY(locker.isLocked());
waitForTest();
QVERIFY(locker.isLocked());
locker.unlock();
QVERIFY(!locker.isLocked());
waitForTest();
QVERIFY(!locker.isLocked());
locker.relock();
QVERIFY(locker.isLocked());
waitForTest();
QVERIFY(locker.isLocked());
}
};
thread = new UnlockAndRelockThread;
thread->start();
waitForThread();
// mutex should be locked by the QMutexLocker
QVERIFY(!thread->mutex.tryLock());
releaseThread();
waitForThread();
// mutex has been explicitly unlocked via QMutexLocker
QVERIFY(thread->mutex.tryLock());
thread->mutex.unlock();
releaseThread();
waitForThread();
// mutex has been explicitly relocked via QMutexLocker
QVERIFY(!thread->mutex.tryLock());
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
void tst_QMutexLocker::lockerStateTest()
{
class LockerStateThread : public tst_QMutexLockerThread
{
public:
void run() override
{
{
QMutexLocker locker(&mutex);
QVERIFY(locker.isLocked());
locker.unlock();
QVERIFY(!locker.isLocked());
waitForTest();
QVERIFY(!locker.isLocked());
}
waitForTest();
}
};
thread = new LockerStateThread;
thread->start();
waitForThread();
// even though we relock() after creating the QMutexLocker, it shouldn't lock the mutex more than once
QVERIFY(thread->mutex.tryLock());
thread->mutex.unlock();
releaseThread();
waitForThread();
// if we call QMutexLocker::unlock(), its destructor should do nothing
QVERIFY(thread->mutex.tryLock());
thread->mutex.unlock();
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
void tst_QMutexLocker::moveSemantics()
{
{
QMutexLocker<QMutex> locker(nullptr);
QVERIFY(!locker.isLocked());
QCOMPARE(locker.mutex(), nullptr);
QMutexLocker locker2(std::move(locker));
QVERIFY(!locker.isLocked());
QVERIFY(!locker2.isLocked());
QCOMPARE(locker.mutex(), nullptr);
QCOMPARE(locker2.mutex(), nullptr);
}
QMutex mutex;
{
QMutexLocker locker(&mutex);
QVERIFY(locker.isLocked());
QCOMPARE(locker.mutex(), &mutex);
QVERIFY(!mutex.tryLock());
QMutexLocker locker2(std::move(locker));
QVERIFY(!locker.isLocked());
QVERIFY(locker2.isLocked());
QCOMPARE(locker.mutex(), nullptr);
QCOMPARE(locker2.mutex(), &mutex);
QVERIFY(!mutex.tryLock());
}
QVERIFY(mutex.tryLock());
mutex.unlock();
{
QMutex mutex;
QMutexLocker locker(&mutex);
QVERIFY(locker.isLocked());
QCOMPARE(locker.mutex(), &mutex);
QVERIFY(!mutex.tryLock());
locker.unlock();
QVERIFY(!locker.isLocked());
QCOMPARE(locker.mutex(), &mutex);
QVERIFY(mutex.tryLock());
mutex.unlock();
QMutexLocker locker2(std::move(locker));
QVERIFY(!locker.isLocked());
QVERIFY(!locker2.isLocked());
QCOMPARE(locker.mutex(), nullptr);
QCOMPARE(locker2.mutex(), &mutex);
QVERIFY(mutex.tryLock());
mutex.unlock();
locker2.relock();
QVERIFY(!locker.isLocked());
QVERIFY(locker2.isLocked());
QCOMPARE(locker.mutex(), nullptr);
QCOMPARE(locker2.mutex(), &mutex);
QVERIFY(!mutex.tryLock());
}
QVERIFY(mutex.tryLock());
mutex.unlock();
}
QTEST_MAIN(tst_QMutexLocker)
#include "tst_qmutexlocker.moc"

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qpromise Test:
#####################################################################
qt_internal_add_test(tst_qpromise
SOURCES
tst_qpromise.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,158 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// Note: this file is published under a license that is different from a default
// test sources license. This is intentional to comply with default
// snippet license.
#include <QCoreApplication>
#include <QTest>
#include <qfuture.h>
#include <qfuturewatcher.h>
#include <qpromise.h>
#include <qscopedpointer.h>
#include <qsharedpointer.h>
class snippet_QPromise
{
public:
static void basicExample();
static void multithreadExample();
static void suspendExample();
};
void snippet_QPromise::basicExample()
{
#if QT_CONFIG(cxx11_future)
//! [basic]
QPromise<int> promise;
QFuture<int> future = promise.future();
QScopedPointer<QThread> thread(QThread::create([] (QPromise<int> promise) {
promise.start(); // notifies QFuture that the computation is started
promise.addResult(42);
promise.finish(); // notifies QFuture that the computation is finished
}, std::move(promise)));
thread->start();
future.waitForFinished(); // blocks until QPromise::finish is called
future.result(); // returns 42
//! [basic]
QCOMPARE(future.result(), 42);
thread->wait();
#endif
}
void snippet_QPromise::multithreadExample()
{
#if QT_CONFIG(cxx11_future)
//! [multithread_init]
QSharedPointer<QPromise<int>> sharedPromise(new QPromise<int>());
QFuture<int> future = sharedPromise->future();
// ...
sharedPromise->start();
//! [multithread_init]
//! [multithread_main]
// here, QPromise is shared between threads via a smart pointer
QScopedPointer<QThread> threads[] = {
QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) {
sharedPromise->addResult(0, 0); // adds value 0 by index 0
}, sharedPromise)),
QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) {
sharedPromise->addResult(-1, 1); // adds value -1 by index 1
}, sharedPromise)),
QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) {
sharedPromise->addResult(-2, 2); // adds value -2 by index 2
}, sharedPromise)),
// ...
};
// start all threads
for (auto& t : threads)
t->start();
// ...
future.resultAt(0); // waits until result at index 0 becomes available. returns value 0
future.resultAt(1); // waits until result at index 1 becomes available. returns value -1
future.resultAt(2); // waits until result at index 2 becomes available. returns value -2
//! [multithread_main]
QCOMPARE(future.resultAt(0), 0);
QCOMPARE(future.resultAt(1), -1);
QCOMPARE(future.resultAt(2), -2);
for (auto& t : threads)
t->wait();
//! [multithread_cleanup]
sharedPromise->finish();
//! [multithread_cleanup]
#endif
}
void snippet_QPromise::suspendExample()
{
#if QT_CONFIG(cxx11_future)
//! [suspend_start]
// Create promise and future
QPromise<int> promise;
QFuture<int> future = promise.future();
promise.start();
// Start a computation thread that supports suspension and cancellation
QScopedPointer<QThread> thread(QThread::create([] (QPromise<int> promise) {
for (int i = 0; i < 100; ++i) {
promise.addResult(i);
promise.suspendIfRequested(); // support suspension
if (promise.isCanceled()) // support cancellation
break;
}
promise.finish();
}, std::move(promise)));
thread->start();
//! [suspend_start]
//! [suspend_suspend]
future.suspend();
//! [suspend_suspend]
// wait in calling thread until future.isSuspended() becomes true or do
// something meanwhile
while (!future.isSuspended()) {
QThread::msleep(50);
}
//! [suspend_intermediateResults]
future.resultCount(); // returns some number between 0 and 100
for (int i = 0; i < future.resultCount(); ++i) {
// process results available before suspension
}
//! [suspend_intermediateResults]
// at least one result is available due to the logic inside a thread
QVERIFY(future.resultCount() > 0);
QVERIFY(future.resultCount() <= 100);
for (int i = 0; i < future.resultCount(); ++i) {
QCOMPARE(future.resultAt(i), i);
}
//! [suspend_end]
future.resume(); // resumes computation, this call will unblock the promise
// alternatively, call future.cancel() to stop the computation
future.waitForFinished();
future.results(); // returns all computation results - array of values from 0 to 99
//! [suspend_end]
thread->wait();
QCOMPARE(future.resultCount(), 100);
QList<int> expected(100);
std::iota(expected.begin(), expected.end(), 0);
QCOMPARE(future.results(), expected);
#endif
}

View File

@ -0,0 +1,667 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QCoreApplication>
#include <QDebug>
#define QPROMISE_TEST
#include <QTest>
#include <qfuture.h>
#include <qfuturewatcher.h>
#include <qpromise.h>
#include <algorithm>
#include <memory>
#include <chrono>
class tst_QPromise : public QObject
{
Q_OBJECT
private slots:
// simple test cases
void promise();
void futureFromPromise();
void addResult();
void addResultOutOfOrder();
#ifndef QT_NO_EXCEPTIONS
void setException();
#endif
void cancel();
void progress();
// complicated test cases
void addInThread();
void addInThreadMoveOnlyObject(); // separate test case - QTBUG-84736
void reportFromMultipleThreads();
void reportFromMultipleThreadsByMovedPromise();
void doNotCancelWhenFinished();
#ifndef QT_NO_EXCEPTIONS
void cancelWhenDestroyed();
#endif
void cancelWhenReassigned();
void cancelWhenDestroyedWithoutStarting();
void cancelWhenDestroyedRunsContinuations();
void finishWhenSwapped();
void cancelWhenMoved();
void waitUntilResumed();
void waitUntilCanceled();
// snippets (external):
void snippet_basicExample();
void snippet_multithreadExample();
void snippet_suspendExample();
};
struct TrivialType { int field = 0; };
struct CopyOnlyType {
constexpr CopyOnlyType(int field = 0) noexcept : field(field) {}
CopyOnlyType(const CopyOnlyType &) = default;
CopyOnlyType& operator=(const CopyOnlyType &) = default;
~CopyOnlyType() = default;
int field;
};
struct MoveOnlyType {
Q_DISABLE_COPY(MoveOnlyType)
constexpr MoveOnlyType(int field = 0) noexcept : field(field) {}
MoveOnlyType(MoveOnlyType &&) = default;
MoveOnlyType& operator=(MoveOnlyType &&) = default;
~MoveOnlyType() = default;
int field;
};
bool operator==(const CopyOnlyType &a, const CopyOnlyType &b) { return a.field == b.field; }
bool operator==(const MoveOnlyType &a, const MoveOnlyType &b) { return a.field == b.field; }
// A wrapper for a test function, calls the function, if it fails, reports failure
#define RUN_TEST_FUNC(test, ...) \
do { \
test(__VA_ARGS__); \
if (QTest::currentTestFailed()) \
QFAIL("Test case " #test "(" #__VA_ARGS__ ") failed"); \
} while (false)
#if QT_CONFIG(cxx11_future)
// std::thread-like wrapper that ensures that the thread is joined at the end of
// a scope to prevent potential std::terminate
struct ThreadWrapper
{
std::unique_ptr<QThread> t;
template<typename Function>
ThreadWrapper(Function &&f) : t(QThread::create(std::forward<Function>(f)))
{
t->start();
}
void join() { t->wait(); }
~ThreadWrapper()
{
t->wait();
}
};
#endif
void tst_QPromise::promise()
{
const auto testCanCreatePromise = [] (auto promise) {
promise.start();
promise.suspendIfRequested(); // should not block on its own
promise.finish();
};
RUN_TEST_FUNC(testCanCreatePromise, QPromise<void>());
RUN_TEST_FUNC(testCanCreatePromise, QPromise<int>());
RUN_TEST_FUNC(testCanCreatePromise, QPromise<QList<float>>());
RUN_TEST_FUNC(testCanCreatePromise, QPromise<TrivialType>());
RUN_TEST_FUNC(testCanCreatePromise, QPromise<CopyOnlyType>());
RUN_TEST_FUNC(testCanCreatePromise, QPromise<MoveOnlyType>());
}
void tst_QPromise::futureFromPromise()
{
const auto testCanCreateFutureFromPromise = [] (auto promise) {
auto future = promise.future();
QVERIFY(!future.isValid());
promise.start();
QCOMPARE(future.isStarted(), true);
QVERIFY(future.isValid());
promise.finish();
QCOMPARE(future.isFinished(), true);
QVERIFY(future.isValid());
future.waitForFinished();
};
RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<void>());
RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<double>());
RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<QList<int>>());
RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<TrivialType>());
RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<CopyOnlyType>());
RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<MoveOnlyType>());
}
void tst_QPromise::addResult()
{
QPromise<int> promise;
auto f = promise.future();
// add as lvalue
int resultAt0 = 456;
{
QVERIFY(promise.addResult(resultAt0));
QCOMPARE(f.resultCount(), 1);
QCOMPARE(f.result(), resultAt0);
QCOMPARE(f.resultAt(0), resultAt0);
}
// add as rvalue
{
int result = 789;
QVERIFY(promise.addResult(789));
QCOMPARE(f.resultCount(), 2);
QCOMPARE(f.resultAt(1), result);
}
// add at position
{
int result = 56238;
QVERIFY(promise.addResult(result, 2));
QCOMPARE(f.resultCount(), 3);
QCOMPARE(f.resultAt(2), result);
}
// add as lvalue at position and overwrite
{
int result = -1;
const auto originalCount = f.resultCount();
QVERIFY(!promise.addResult(result, 0));
QCOMPARE(f.resultCount(), originalCount);
QCOMPARE(f.resultAt(0), resultAt0); // overwrite does not work
}
// add as rvalue at position and overwrite
{
const auto originalCount = f.resultCount();
QVERIFY(!promise.addResult(-1, 0));
QCOMPARE(f.resultCount(), originalCount);
QCOMPARE(f.resultAt(0), resultAt0); // overwrite does not work
}
}
void tst_QPromise::addResultOutOfOrder()
{
// Compare results available in QFuture to expected results
const auto compareResults = [] (const auto &future, auto expected) {
QCOMPARE(future.resultCount(), expected.size());
// index based loop
for (int i = 0; i < future.resultCount(); ++i)
QCOMPARE(future.resultAt(i), expected.at(i));
// iterator based loop
QVERIFY(std::equal(future.begin(), future.end(), expected.begin()));
};
// out of order results without a gap
{
QPromise<int> promise;
auto f = promise.future();
QVERIFY(promise.addResult(456, 1));
QCOMPARE(f.resultCount(), 0);
QVERIFY(promise.addResult(123, 0));
QList<int> expected({123, 456});
RUN_TEST_FUNC(compareResults, f, expected);
QCOMPARE(f.results(), expected);
}
// out of order results with a gap that is closed "later"
{
QPromise<int> promise;
auto f = promise.future();
QVERIFY(promise.addResult(0, 0));
QVERIFY(promise.addResult(1, 1));
QVERIFY(promise.addResult(3, 3)); // intentional gap here
QList<int> expectedWhenGapExists({0, 1});
RUN_TEST_FUNC(compareResults, f, expectedWhenGapExists);
QCOMPARE(f.resultAt(3), 3);
QList<int> expectedWhenNoGap({0, 1, 2, 3});
QVERIFY(promise.addResult(2, 2)); // fill a gap with a value
RUN_TEST_FUNC(compareResults, f, expectedWhenNoGap);
QCOMPARE(f.results(), expectedWhenNoGap);
}
}
#ifndef QT_NO_EXCEPTIONS
void tst_QPromise::setException()
{
struct TestException {}; // custom exception class
const auto testExceptionCaught = [] (auto promise, const auto& exception) {
auto f = promise.future();
promise.start();
promise.setException(exception);
promise.finish();
bool caught = false;
try {
f.waitForFinished();
} catch (const QException&) {
caught = true;
} catch (const TestException&) {
caught = true;
}
QVERIFY(caught);
};
RUN_TEST_FUNC(testExceptionCaught, QPromise<void>(), QException());
RUN_TEST_FUNC(testExceptionCaught, QPromise<int>(), QException());
RUN_TEST_FUNC(testExceptionCaught, QPromise<void>(),
std::make_exception_ptr(TestException()));
RUN_TEST_FUNC(testExceptionCaught, QPromise<int>(),
std::make_exception_ptr(TestException()));
}
#endif
void tst_QPromise::cancel()
{
const auto testCancel = [] (auto promise) {
auto f = promise.future();
f.cancel();
QCOMPARE(promise.isCanceled(), true);
};
testCancel(QPromise<void>());
testCancel(QPromise<int>());
}
void tst_QPromise::progress()
{
const auto testProgress = [] (auto promise) {
auto f = promise.future();
promise.setProgressRange(0, 2);
QCOMPARE(f.progressMinimum(), 0);
QCOMPARE(f.progressMaximum(), 2);
QCOMPARE(f.progressValue(), 0);
promise.setProgressValue(1);
QCOMPARE(f.progressValue(), 1);
promise.setProgressValue(0); // decrement
QCOMPARE(f.progressValue(), 1);
promise.setProgressValue(10); // out of range
QCOMPARE(f.progressValue(), 1);
promise.setProgressRange(0, 100);
promise.setProgressValueAndText(50, u8"50%");
QCOMPARE(f.progressValue(), 50);
QCOMPARE(f.progressText(), u8"50%");
};
RUN_TEST_FUNC(testProgress, QPromise<void>());
RUN_TEST_FUNC(testProgress, QPromise<int>());
}
void tst_QPromise::addInThread()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
const auto testAddResult = [] (auto promise, const auto &result) {
promise.start();
auto f = promise.future();
// move construct QPromise
ThreadWrapper thr([p = std::move(promise), &result] () mutable {
p.addResult(result);
});
// Waits for result first
QCOMPARE(f.result(), result);
QCOMPARE(f.resultAt(0), result);
};
RUN_TEST_FUNC(testAddResult, QPromise<int>(), 42);
RUN_TEST_FUNC(testAddResult, QPromise<QString>(), u8"42");
RUN_TEST_FUNC(testAddResult, QPromise<CopyOnlyType>(), CopyOnlyType{99});
#endif
}
void tst_QPromise::addInThreadMoveOnlyObject()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<MoveOnlyType> promise;
promise.start();
auto f = promise.future();
ThreadWrapper thr([p = std::move(promise)] () mutable {
p.addResult(MoveOnlyType{-11});
});
// Iterators wait for result first
for (auto& result : f)
QCOMPARE(result, MoveOnlyType{-11});
#endif
}
void tst_QPromise::reportFromMultipleThreads()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<int> promise;
auto f = promise.future();
promise.start();
ThreadWrapper threads[] = {
ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
ThreadWrapper([&promise] () mutable { promise.addResult(43); }),
ThreadWrapper([&promise] () mutable { promise.addResult(44); }),
};
for (auto& t : threads)
t.join();
promise.finish();
QList<int> expected = {42, 43, 44};
for (auto actual : f.results()) {
QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end());
expected.removeOne(actual);
}
#endif
}
void tst_QPromise::reportFromMultipleThreadsByMovedPromise()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<int> initialPromise;
auto f = initialPromise.future();
{
// Move QPromise into local scope: local QPromise (as being
// move-constructed) must be able to set results, QFuture must still
// hold correct references to results.
auto promise = std::move(initialPromise);
promise.start();
ThreadWrapper threads[] = {
ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
ThreadWrapper([&promise] () mutable { promise.addResult(43); }),
ThreadWrapper([&promise] () mutable { promise.addResult(44); }),
};
for (auto& t : threads)
t.join();
promise.finish();
}
QCOMPARE(f.isFinished(), true);
QCOMPARE(f.isValid(), true);
QList<int> expected = {42, 43, 44};
for (auto actual : f.results()) {
QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end());
expected.removeOne(actual);
}
#endif
}
void tst_QPromise::doNotCancelWhenFinished()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
const auto testFinishedPromise = [] (auto promise) {
auto f = promise.future();
promise.start();
// Finish QPromise inside thread, destructor must not call cancel()
ThreadWrapper([p = std::move(promise)] () mutable { p.finish(); }).join();
f.waitForFinished();
QCOMPARE(f.isFinished(), true);
QCOMPARE(f.isCanceled(), false);
};
RUN_TEST_FUNC(testFinishedPromise, QPromise<void>());
RUN_TEST_FUNC(testFinishedPromise, QPromise<int>());
RUN_TEST_FUNC(testFinishedPromise, QPromise<QString>());
#endif
}
#ifndef QT_NO_EXCEPTIONS
void tst_QPromise::cancelWhenDestroyed()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<int> initialPromise;
auto f = initialPromise.future();
try {
// Move QPromise to local scope. On destruction, it must call cancel().
auto promise = std::move(initialPromise);
promise.start();
ThreadWrapper threads[] = {
ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
ThreadWrapper([&promise] () mutable { promise.addResult(43); }),
ThreadWrapper([&promise] () mutable { promise.addResult(44); }),
};
for (auto& t : threads)
t.join();
throw "Throw in the middle, we lose our promise here, finish() not called!";
promise.finish();
} catch (...) {}
QCOMPARE(f.isFinished(), true);
QCOMPARE(f.isCanceled(), true);
// Results are still available despite throw
QList<int> expected = {42, 43, 44};
for (auto actual : f.results()) {
QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end());
expected.removeOne(actual);
}
#endif
}
#endif
void tst_QPromise::cancelWhenReassigned()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<int> promise;
auto f = promise.future();
promise.start();
ThreadWrapper thr([p = std::move(promise)] () mutable {
QThread::msleep(100);
p = QPromise<int>(); // assign new promise, old must be correctly destroyed
});
f.waitForFinished(); // wait for the old promise
QCOMPARE(f.isFinished(), true);
QCOMPARE(f.isCanceled(), true);
#endif
}
void tst_QPromise::cancelWhenDestroyedWithoutStarting()
{
QFuture<void> future;
{
QPromise<void> promise;
future = promise.future();
}
future.waitForFinished();
QVERIFY(!future.isStarted());
QVERIFY(future.isCanceled());
QVERIFY(future.isFinished());
}
void tst_QPromise::cancelWhenDestroyedRunsContinuations()
{
QFuture<void> future;
bool onCanceledCalled = false;
bool thenCalled = false;
{
QPromise<void> promise;
future = promise.future();
future.then([&] {
thenCalled = true;
}).onCanceled([&] {
onCanceledCalled = true;
});
}
QVERIFY(future.isFinished());
QVERIFY(!thenCalled);
QVERIFY(onCanceledCalled);
}
void tst_QPromise::finishWhenSwapped()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<int> promise1;
auto f1 = promise1.future();
promise1.start();
QPromise<int> promise2;
auto f2 = promise2.future();
promise2.start();
ThreadWrapper thr([&promise1, &promise2] () mutable {
QThread::msleep(100);
promise1.addResult(0);
promise2.addResult(1);
swap(promise1, promise2); // ADL must resolve this
promise1.addResult(2);
promise2.addResult(3);
promise1.finish(); // this finish is for future #2
promise2.finish(); // this finish is for future #1
});
f1.waitForFinished();
f2.waitForFinished();
// Future #1 and #2 are finished inside thread
QCOMPARE(f1.isFinished(), true);
QCOMPARE(f1.isCanceled(), false);
QCOMPARE(f2.isFinished(), true);
QCOMPARE(f2.isCanceled(), false);
QCOMPARE(f1.resultAt(0), 0);
QCOMPARE(f1.resultAt(1), 3);
QCOMPARE(f2.resultAt(0), 1);
QCOMPARE(f2.resultAt(1), 2);
#endif
}
void tst_QPromise::cancelWhenMoved()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<int> promise1;
auto f1 = promise1.future();
promise1.start();
QPromise<int> promise2;
auto f2 = promise2.future();
promise2.start();
// Move promises to local scope to test cancellation behavior
ThreadWrapper thr([p1 = std::move(promise1), p2 = std::move(promise2)] () mutable {
QThread::msleep(100);
p1 = std::move(p2);
p1.finish(); // this finish is for future #2
});
f1.waitForFinished();
f2.waitForFinished();
// Future #1 is implicitly cancelled inside thread
QCOMPARE(f1.isFinished(), true);
QCOMPARE(f1.isCanceled(), true);
// Future #2 is explicitly finished inside thread
QCOMPARE(f2.isFinished(), true);
QCOMPARE(f2.isCanceled(), false);
#endif
}
void tst_QPromise::waitUntilResumed()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<int> promise;
promise.start();
auto f = promise.future();
f.suspend();
ThreadWrapper thr([p = std::move(promise)] () mutable {
p.suspendIfRequested();
p.addResult(42); // result added after suspend
p.finish();
});
while (!f.isSuspended()) { // busy wait until worker thread suspends
QCOMPARE(f.isFinished(), false); // exit condition in case of failure
QThread::msleep(50); // allow another thread to actually carry on
}
f.resume();
f.waitForFinished();
QCOMPARE(f.resultCount(), 1);
QCOMPARE(f.result(), 42);
#endif
}
void tst_QPromise::waitUntilCanceled()
{
#if !QT_CONFIG(cxx11_future)
QSKIP("This test requires QThread::create");
#else
QPromise<int> promise;
promise.start();
auto f = promise.future();
f.suspend();
ThreadWrapper thr([p = std::move(promise)] () mutable {
p.suspendIfRequested();
p.addResult(42); // result not added due to QFuture::cancel()
p.finish();
});
while (!f.isSuspended()) { // busy wait until worker thread suspends
QCOMPARE(f.isFinished(), false); // exit condition in case of failure
QThread::msleep(50); // allow another thread to actually carry on
}
f.cancel();
f.waitForFinished();
QCOMPARE(f.resultCount(), 0);
#endif
}
// Below is a quick and dirty hack to make snippets a part of a test suite
#include "snippet_qpromise.cpp"
void tst_QPromise::snippet_basicExample()
{
snippet_QPromise::basicExample();
}
void tst_QPromise::snippet_multithreadExample()
{
snippet_QPromise::multithreadExample();
}
void tst_QPromise::snippet_suspendExample()
{
snippet_QPromise::suspendExample();
}
QTEST_MAIN(tst_QPromise)
#include "tst_qpromise.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qreadlocker Test:
#####################################################################
qt_internal_add_test(tst_qreadlocker
SOURCES
tst_qreadlocker.cpp
)

View File

@ -0,0 +1,178 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QCoreApplication>
#include <QReadLocker>
#include <QSemaphore>
#include <QThread>
class tst_QReadLockerThread : public QThread
{
public:
QReadWriteLock lock;
QSemaphore semaphore, testSemaphore;
void waitForTest()
{
semaphore.release();
testSemaphore.acquire();
}
};
class tst_QReadLocker : public QObject
{
Q_OBJECT
public:
tst_QReadLockerThread *thread;
void waitForThread()
{
thread->semaphore.acquire();
}
void releaseThread()
{
thread->testSemaphore.release();
}
private slots:
void scopeTest();
void unlockAndRelockTest();
void lockerStateTest();
};
void tst_QReadLocker::scopeTest()
{
class ScopeTestThread : public tst_QReadLockerThread
{
public:
void run() override
{
waitForTest();
{
QReadLocker locker(&lock);
waitForTest();
}
waitForTest();
}
};
thread = new ScopeTestThread;
thread->start();
waitForThread();
// lock should be unlocked before entering the scope that creates the QReadLocker
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
waitForThread();
// lock should be locked by the QReadLocker
QVERIFY(!thread->lock.tryLockForWrite());
releaseThread();
waitForThread();
// lock should be unlocked when the QReadLocker goes out of scope
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
void tst_QReadLocker::unlockAndRelockTest()
{
class UnlockAndRelockThread : public tst_QReadLockerThread
{
public:
void run() override
{
QReadLocker locker(&lock);
waitForTest();
locker.unlock();
waitForTest();
locker.relock();
waitForTest();
}
};
thread = new UnlockAndRelockThread;
thread->start();
waitForThread();
// lock should be locked by the QReadLocker
QVERIFY(!thread->lock.tryLockForWrite());
releaseThread();
waitForThread();
// lock has been explicitly unlocked via QReadLocker
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
waitForThread();
// lock has been explicitly relocked via QReadLocker
QVERIFY(!thread->lock.tryLockForWrite());
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
void tst_QReadLocker::lockerStateTest()
{
class LockerStateThread : public tst_QReadLockerThread
{
public:
void run() override
{
{
QReadLocker locker(&lock);
locker.relock();
locker.unlock();
waitForTest();
}
waitForTest();
}
};
thread = new LockerStateThread;
thread->start();
waitForThread();
// even though we relock() after creating the QReadLocker, it shouldn't lock the lock more than once
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
waitForThread();
// if we call QReadLocker::unlock(), its destructor should do nothing
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
QTEST_MAIN(tst_QReadLocker)
#include "tst_qreadlocker.moc"

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qreadwritelock Test:
#####################################################################
qt_internal_add_test(tst_qreadwritelock
SOURCES
tst_qreadwritelock.cpp
LIBRARIES
Qt::CorePrivate
Qt::TestPrivate
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qresultstore Test:
#####################################################################
qt_internal_add_test(tst_qresultstore
SOURCES
tst_qresultstore.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,589 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <qresultstore.h>
using namespace QtPrivate;
class IntResultsCleaner
{
public:
IntResultsCleaner(QtPrivate::ResultStoreBase &s) : store(s) { }
~IntResultsCleaner() { store.clear<int>(); }
private:
QtPrivate::ResultStoreBase &store;
};
class tst_QtConcurrentResultStore : public QObject
{
Q_OBJECT
public slots:
void init();
private slots:
void construction();
void iterators();
void addResult();
void addResults();
void resultIndex();
void resultAt();
void contains();
void filterMode();
void addCanceledResult();
void count();
void pendingResultsDoNotLeak_data();
void pendingResultsDoNotLeak();
private:
int int0;
int int1;
int int2;
QList<int> vec0;
QList<int> vec1;
};
void tst_QtConcurrentResultStore::init()
{
int0 = 0;
int1 = 1;
int2 = 2;
vec0 = QList<int> { 2, 3 };
vec1 = QList<int> { 4, 5 };
}
void tst_QtConcurrentResultStore::construction()
{
ResultStoreBase store;
QCOMPARE(store.count(), 0);
}
void tst_QtConcurrentResultStore::iterators()
{
{
ResultStoreBase store;
QCOMPARE(store.begin(), store.end());
QCOMPARE(store.resultAt(0), store.end());
QCOMPARE(store.resultAt(1), store.end());
}
{
QtPrivate::ResultStoreBase storebase;
IntResultsCleaner cleanGuard(storebase);
storebase.addResult(-1, &int0); // note to self: adding a pointer to the stack here is ok since
storebase.addResult(1, &int1); // ResultStoreBase does not take ownership, only ResultStore<> does.
ResultIteratorBase it = storebase.begin();
QCOMPARE(it.resultIndex(), 0);
QCOMPARE(it, storebase.begin());
QVERIFY(it != storebase.end());
++it;
QCOMPARE(it.resultIndex(), 1);
QVERIFY(it != storebase.begin());
QVERIFY(it != storebase.end());
++it;
QVERIFY(it != storebase.begin());
QCOMPARE(it, storebase.end());
}
}
void tst_QtConcurrentResultStore::addResult()
{
{
// test addResult return value
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.setFilterMode(true);
QCOMPARE(store.addResult(0, &int0), 0);
QCOMPARE(store.count(), 1); // result 0 becomes available
QCOMPARE(store.contains(0), true);
QCOMPARE(store.addResult(2, &int0), 2);
QCOMPARE(store.count(), 1);
QCOMPARE(store.contains(2), false);
QCOMPARE(store.addCanceledResult(1), 1);
QCOMPARE(store.count(), 2); // result 2 is renamed to 1 and becomes available
QCOMPARE(store.contains(0), true);
QCOMPARE(store.contains(1), true);
QCOMPARE(store.contains(2), false);
QCOMPARE(store.addResult(3, &int0), 3);
QCOMPARE(store.count(), 3);
QCOMPARE(store.contains(2), true);
QCOMPARE(store.addResult(6, &int0), 6);
QCOMPARE(store.count(), 3);
QCOMPARE(store.addResult(7, &int0), 7);
QCOMPARE(store.count(), 3);
QCOMPARE(store.contains(3), false);
QCOMPARE(store.addCanceledResult(4), 4);
QCOMPARE(store.addCanceledResult(5), 5);
QCOMPARE(store.count(), 5); // 6 and 7 is renamed to 3 and 4 and becomes available
QCOMPARE(store.contains(3), true);
QCOMPARE(store.contains(4), true);
QCOMPARE(store.addResult(8, &int0), 8);
QCOMPARE(store.contains(5), true);
QCOMPARE(store.count(), 6);
QCOMPARE(store.contains(6), false);
QCOMPARE(store.contains(7), false);
}
}
void tst_QtConcurrentResultStore::addResults()
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResults(-1, &vec0);
store.addResults(-1, &vec1);
ResultIteratorBase it = store.begin();
QCOMPARE(it.resultIndex(), 0);
QCOMPARE(it, store.begin());
QVERIFY(it != store.end());
++it;
QCOMPARE(it.resultIndex(), 1);
QVERIFY(it != store.begin());
QVERIFY(it != store.end());
++it;
QCOMPARE(it.resultIndex(), 2);
++it;
QCOMPARE(it.resultIndex(), 3);
++it;
QCOMPARE(it, store.end());
QList<int> empty;
const auto countBefore = store.count();
QCOMPARE(store.addResults(countBefore, &empty), -1);
QCOMPARE(store.count(), countBefore);
QCOMPARE(store.addResults(countBefore, &vec1), countBefore);
QCOMPARE(store.count(), countBefore + vec1.size());
}
void tst_QtConcurrentResultStore::resultIndex()
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(-1, &int1);
ResultIteratorBase it = store.begin();
QCOMPARE(it.resultIndex(), 0);
QVERIFY(it == store.begin());
QVERIFY(it != store.end());
++it;
QCOMPARE(it.resultIndex(), 1);
QVERIFY(it != store.begin());
QVERIFY(it != store.end());
++it;
QCOMPARE(it.resultIndex(), 2);
QVERIFY(it != store.end());
++it;
QCOMPARE(it.resultIndex(), 3);
QVERIFY(it != store.end());
++it;
QVERIFY(it == store.end());
QCOMPARE(store.resultAt(0).value<int>(), int0);
QCOMPARE(store.resultAt(1).value<int>(), vec0[0]);
QCOMPARE(store.resultAt(2).value<int>(), vec0[1]);
QCOMPARE(store.resultAt(3).value<int>(), int1);
}
void tst_QtConcurrentResultStore::resultAt()
{
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(200, &int1);
QCOMPARE(store.resultAt(0).value<int>(), int0);
QCOMPARE(store.resultAt(1).value<int>(), vec0[0]);
QCOMPARE(store.resultAt(2).value<int>(), vec0[1]);
QCOMPARE(store.resultAt(200).value<int>(), int1);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResult(1, &int1);
store.addResult(0, &int0);
store.addResult(-1, &int2);
QCOMPARE(store.resultAt(0).value<int>(), int0);
QCOMPARE(store.resultAt(1).value<int>(), int1);
QCOMPARE(store.resultAt(2).value<int>(), int2);
}
}
void tst_QtConcurrentResultStore::contains()
{
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
QCOMPARE(store.contains(0), false);
QCOMPARE(store.contains(1), false);
QCOMPARE(store.contains(INT_MAX), false);
store.addResult(1, &int1);
QVERIFY(store.contains(int1));
store.addResult(0, &int0);
QVERIFY(store.contains(int0));
store.addResult(-1, &int2);
QVERIFY(store.contains(int2));
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResult(1, &int0);
store.addResult(3, &int0);
store.addResults(6, &vec0);
QCOMPARE(store.contains(0), false);
QCOMPARE(store.contains(1), true);
QCOMPARE(store.contains(2), false);
QCOMPARE(store.contains(3), true);
QCOMPARE(store.contains(4), false);
QCOMPARE(store.contains(5), false);
QCOMPARE(store.contains(6), true);
QCOMPARE(store.contains(7), true);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.setFilterMode(true);
store.addResult(1, &int0);
store.addResult(3, &int0);
store.addResults(6, &vec0);
QCOMPARE(store.contains(0), false);
QCOMPARE(store.contains(1), false);
QCOMPARE(store.contains(2), false);
QCOMPARE(store.contains(3), false);
QCOMPARE(store.contains(4), false);
QCOMPARE(store.contains(5), false);
QCOMPARE(store.contains(6), false);
QCOMPARE(store.contains(7), false);
store.addCanceledResult(0);
store.addCanceledResult(2);
store.addCanceledResults<int>(4, 2);
QCOMPARE(store.contains(0), true);
QCOMPARE(store.contains(1), true);
QCOMPARE(store.contains(2), true);
QCOMPARE(store.contains(3), true);
QCOMPARE(store.contains(4), false);
QCOMPARE(store.contains(5), false);
QCOMPARE(store.contains(6), false);
QCOMPARE(store.contains(7), false);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.setFilterMode(true);
store.addCanceledResult(0);
QCOMPARE(store.contains(0), false);
store.addResult(1, &int0);
QCOMPARE(store.contains(0), true);
QCOMPARE(store.contains(1), false);
}
}
void tst_QtConcurrentResultStore::filterMode()
{
// Test filter mode, where "gaps" in the result array aren't allowed.
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
QCOMPARE(store.filterMode(), false);
store.setFilterMode(true);
QVERIFY(store.filterMode());
store.addResult(0, &int0);
QCOMPARE(store.contains(0), true);
store.addResult(2, &int2); // add result at index 2
QCOMPARE(store.contains(2), false); // but 1 is missing, so this 2 won't be reported yet.
store.addResult(1, &int1);
QCOMPARE(store.contains(1), true);
QCOMPARE(store.contains(2), true); // 2 should be visible now.
store.addResult(4, &int0);
store.addResult(5, &int0);
store.addResult(7, &int0);
QCOMPARE(store.contains(4), false);
QCOMPARE(store.contains(5), false);
QCOMPARE(store.contains(7), false);
store.addResult(3, &int0); // adding 3 makes 4 and 5 visible
QCOMPARE(store.contains(4), true);
QCOMPARE(store.contains(5), true);
QCOMPARE(store.contains(7), false);
store.addResult(6, &int0); // adding 6 makes 7 visible
QCOMPARE(store.contains(6), true);
QCOMPARE(store.contains(7), true);
QCOMPARE(store.contains(8), false);
QList<int> empty;
const auto countBefore = store.count();
QCOMPARE(store.addResults(countBefore, &empty), -1);
QCOMPARE(store.count(), countBefore);
QCOMPARE(store.addResult(countBefore, &int2), countBefore);
QCOMPARE(store.count(), countBefore + 1);
}
void tst_QtConcurrentResultStore::addCanceledResult()
{
// test canceled results
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.setFilterMode(true);
store.addResult(0, &int0);
QCOMPARE(store.contains(0), true);
store.addResult(2, &int0);
QCOMPARE(store.contains(2), false);
store.addCanceledResult(1); // report no result at 1
QCOMPARE(store.contains(0), true);
QCOMPARE(store.contains(1), true); // 2 gets renamed to 1
QCOMPARE(store.contains(2), false);
store.addResult(3, &int0);
QCOMPARE(store.contains(2), true); //3 gets renamed to 2
store.addResult(6, &int0);
store.addResult(7, &int0);
QCOMPARE(store.contains(3), false);
store.addCanceledResult(4);
store.addCanceledResult(5);
QCOMPARE(store.contains(3), true); //6 gets renamed to 3
QCOMPARE(store.contains(4), true); //7 gets renamed to 4
store.addResult(8, &int0);
QCOMPARE(store.contains(5), true); //8 gets renamed to 4
QCOMPARE(store.contains(6), false);
QCOMPARE(store.contains(7), false);
}
void tst_QtConcurrentResultStore::count()
{
{
// test resultCount in non-filtered mode. It should always be possible
// to iterate through the results 0 to resultCount.
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResult(0, &int0);
QCOMPARE(store.count(), 1);
store.addResult(2, &int0);
QCOMPARE(store.count(), 1);
store.addResult(1, &int0);
QCOMPARE(store.count(), 3);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResult(2, &int0);
QCOMPARE(store.count(), 0);
store.addResult(1, &int0);
QCOMPARE(store.count(), 0);
store.addResult(0, &int0);
QCOMPARE(store.count(), 3);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
store.addResult(1, &int0);
QCOMPARE(store.count(), 0);
store.addResult(0, &int0);
QCOMPARE(store.count(), 4);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
store.addResults(0, &vec0);
QCOMPARE(store.count(), 4);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
store.addResults(0, &vec0);
QCOMPARE(store.count(), 2);
store.addResult(2, &int0);
QCOMPARE(store.count(), 5);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
store.addResults(0, &vec0);
QCOMPARE(store.count(), 2);
store.addCanceledResult(2);
QCOMPARE(store.count(), 4);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
store.addCanceledResults<int>(0, 3);
QCOMPARE(store.count(), 2);
}
{
QtPrivate::ResultStoreBase store;
IntResultsCleaner cleanGuard(store);
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
store.addCanceledResults<int>(0, 3);
QCOMPARE(store.count(), 2); // results at 3 and 4 become available at index 0, 1
store.addResult(5, &int0);
QCOMPARE(store.count(), 3);// result 5 becomes available at index 2
}
}
// simplified version of CountedObject from tst_qarraydata.cpp
struct CountedObject
{
CountedObject() : id(liveCount++)
{ }
CountedObject(const CountedObject &other) : id(other.id)
{
++liveCount;
}
~CountedObject()
{
--liveCount;
}
CountedObject &operator=(const CountedObject &) = default;
struct LeakChecker
{
LeakChecker()
: previousLiveCount(liveCount)
{
}
~LeakChecker()
{
QCOMPARE(liveCount, previousLiveCount);
}
private:
const size_t previousLiveCount;
};
int id = 0;
static size_t liveCount;
};
size_t CountedObject::liveCount = 0;
void tst_QtConcurrentResultStore::pendingResultsDoNotLeak_data()
{
QTest::addColumn<bool>("filterMode");
QTest::addRow("filter-mode-off") << false;
QTest::addRow("filter-mode-on") << true;
}
void tst_QtConcurrentResultStore::pendingResultsDoNotLeak()
{
QFETCH(bool, filterMode);
CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker)
QtPrivate::ResultStoreBase store;
auto cleanGaurd = qScopeGuard([&] { store.clear<CountedObject>(); });
store.setFilterMode(filterMode);
// lvalue
auto lvalueObj = CountedObject();
store.addResult(42, &lvalueObj);
// rvalue
store.moveResult(43, CountedObject());
// array
auto lvalueListOfObj = QList<CountedObject>({CountedObject(), CountedObject()});
store.addResults(44, &lvalueListOfObj);
}
QTEST_MAIN(tst_QtConcurrentResultStore)
#include "tst_qresultstore.moc"

View File

@ -0,0 +1,2 @@
[tryAcquireWithTimeout]
macos

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qsemaphore Test:
#####################################################################
qt_internal_add_test(tst_qsemaphore
SOURCES
tst_qsemaphore.cpp
)

View File

@ -0,0 +1,612 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <qcoreapplication.h>
#include <qthread.h>
#include <qsemaphore.h>
#include <chrono>
using namespace std::chrono_literals;
class tst_QSemaphore : public QObject
{
Q_OBJECT
private slots:
void acquire();
void multiRelease();
void multiAcquireRelease();
void tryAcquire();
void tryAcquireWithTimeout_data();
void tryAcquireWithTimeout();
void tryAcquireWithTimeoutStarvation();
void tryAcquireWithTimeoutForever_data();
void tryAcquireWithTimeoutForever();
void producerConsumer();
void raii();
void stdCompat();
};
static QSemaphore *semaphore = nullptr;
class ThreadOne : public QThread
{
public:
ThreadOne() {}
protected:
void run() override
{
int i = 0;
while ( i < 100 ) {
semaphore->acquire();
i++;
semaphore->release();
}
}
};
class ThreadN : public QThread
{
int N;
public:
ThreadN(int n) :N(n) { }
protected:
void run() override
{
int i = 0;
while ( i < 100 ) {
semaphore->acquire(N);
i++;
semaphore->release(N);
}
}
};
void tst_QSemaphore::acquire()
{
{
// old incrementOne() test
QVERIFY(!semaphore);
semaphore = new QSemaphore;
// make some "thing" available
semaphore->release();
ThreadOne t1;
ThreadOne t2;
t1.start();
t2.start();
QVERIFY(t1.wait(4000));
QVERIFY(t2.wait(4000));
delete semaphore;
semaphore = nullptr;
}
// old incrementN() test
{
QVERIFY(!semaphore);
semaphore = new QSemaphore;
// make 4 "things" available
semaphore->release(4);
ThreadN t1(2);
ThreadN t2(3);
t1.start();
t2.start();
QVERIFY(t1.wait(4000));
QVERIFY(t2.wait(4000));
delete semaphore;
semaphore = nullptr;
}
QSemaphore semaphore;
QCOMPARE(semaphore.available(), 0);
semaphore.release();
QCOMPARE(semaphore.available(), 1);
semaphore.release();
QCOMPARE(semaphore.available(), 2);
semaphore.release(10);
QCOMPARE(semaphore.available(), 12);
semaphore.release(10);
QCOMPARE(semaphore.available(), 22);
semaphore.acquire();
QCOMPARE(semaphore.available(), 21);
semaphore.acquire();
QCOMPARE(semaphore.available(), 20);
semaphore.acquire(10);
QCOMPARE(semaphore.available(), 10);
semaphore.acquire(10);
QCOMPARE(semaphore.available(), 0);
}
void tst_QSemaphore::multiRelease()
{
class Thread : public QThread
{
public:
QSemaphore &sem;
Thread(QSemaphore &sem) : sem(sem) {}
void run() override
{
sem.acquire();
}
};
QSemaphore sem;
QList<Thread *> threads;
threads.resize(4);
for (Thread *&t : threads)
t = new Thread(sem);
for (Thread *&t : threads)
t->start();
// wait for all threads to reach the sem.acquire() and then
// release them all
QTest::qSleep(1);
sem.release(int(threads.size()));
for (Thread *&t : threads)
t->wait();
qDeleteAll(threads);
}
void tst_QSemaphore::multiAcquireRelease()
{
class Thread : public QThread
{
public:
QSemaphore &sem;
Thread(QSemaphore &sem) : sem(sem) {}
void run() override
{
sem.acquire();
sem.release();
}
};
QSemaphore sem;
QList<Thread *> threads;
threads.resize(4);
for (Thread *&t : threads)
t = new Thread(sem);
for (Thread *&t : threads)
t->start();
// wait for all threads to reach the sem.acquire() and then
// release them all
QTest::qSleep(1);
sem.release();
for (Thread *&t : threads)
t->wait();
qDeleteAll(threads);
}
void tst_QSemaphore::tryAcquire()
{
QSemaphore semaphore;
QCOMPARE(semaphore.available(), 0);
semaphore.release();
QCOMPARE(semaphore.available(), 1);
QVERIFY(!semaphore.tryAcquire(2));
QVERIFY(!semaphore.tryAcquire(2, 0));
QCOMPARE(semaphore.available(), 1);
semaphore.release();
QCOMPARE(semaphore.available(), 2);
QVERIFY(!semaphore.tryAcquire(3));
QVERIFY(!semaphore.tryAcquire(3, 0));
QCOMPARE(semaphore.available(), 2);
semaphore.release(10);
QCOMPARE(semaphore.available(), 12);
QVERIFY(!semaphore.tryAcquire(100));
QVERIFY(!semaphore.tryAcquire(100, 0));
QCOMPARE(semaphore.available(), 12);
semaphore.release(10);
QCOMPARE(semaphore.available(), 22);
QVERIFY(!semaphore.tryAcquire(100));
QVERIFY(!semaphore.tryAcquire(100, 0));
QCOMPARE(semaphore.available(), 22);
QVERIFY(semaphore.tryAcquire());
QCOMPARE(semaphore.available(), 21);
QVERIFY(semaphore.tryAcquire());
QCOMPARE(semaphore.available(), 20);
semaphore.release(2);
QVERIFY(semaphore.tryAcquire(1, 0));
QCOMPARE(semaphore.available(), 21);
QVERIFY(semaphore.tryAcquire(1, 0));
QCOMPARE(semaphore.available(), 20);
QVERIFY(semaphore.tryAcquire(10));
QCOMPARE(semaphore.available(), 10);
semaphore.release(10);
QVERIFY(semaphore.tryAcquire(10, 0));
QCOMPARE(semaphore.available(), 10);
QVERIFY(semaphore.tryAcquire(10));
QCOMPARE(semaphore.available(), 0);
// should not be able to acquire more
QVERIFY(!semaphore.tryAcquire());
QVERIFY(!semaphore.tryAcquire(1, 0));
QCOMPARE(semaphore.available(), 0);
QVERIFY(!semaphore.tryAcquire());
QVERIFY(!semaphore.tryAcquire(1, 0));
QCOMPARE(semaphore.available(), 0);
QVERIFY(!semaphore.tryAcquire(10));
QVERIFY(!semaphore.tryAcquire(10, 0));
QCOMPARE(semaphore.available(), 0);
QVERIFY(!semaphore.tryAcquire(10));
QVERIFY(!semaphore.tryAcquire(10, 0));
QCOMPARE(semaphore.available(), 0);
}
void tst_QSemaphore::tryAcquireWithTimeout_data()
{
QTest::addColumn<int>("timeout");
QTest::newRow("0.2s") << 200;
QTest::newRow("2s") << 2000;
}
void tst_QSemaphore::tryAcquireWithTimeout()
{
QFETCH(int, timeout);
// timers are not guaranteed to be accurate down to the last millisecond,
// so we permit the elapsed times to be up to this far from the expected value.
int fuzz = 50 + (timeout / 20);
QSemaphore semaphore;
QElapsedTimer time;
#define FUZZYCOMPARE(a,e) \
do { \
int a1 = a; \
int e1 = e; \
QVERIFY2(qAbs(a1-e1) < fuzz, \
qPrintable(QString("(%1=%2) is more than %3 milliseconds different from (%4=%5)") \
.arg(#a).arg(a1).arg(fuzz).arg(#e).arg(e1))); \
} while (0)
QCOMPARE(semaphore.available(), 0);
semaphore.release();
QCOMPARE(semaphore.available(), 1);
time.start();
QVERIFY(!semaphore.tryAcquire(2, timeout));
FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 1);
semaphore.release();
QCOMPARE(semaphore.available(), 2);
time.start();
QVERIFY(!semaphore.tryAcquire(3, timeout));
FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 2);
semaphore.release(10);
QCOMPARE(semaphore.available(), 12);
time.start();
QVERIFY(!semaphore.tryAcquire(100, timeout));
FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 12);
semaphore.release(10);
QCOMPARE(semaphore.available(), 22);
time.start();
QVERIFY(!semaphore.tryAcquire(100, timeout));
FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 22);
time.start();
QVERIFY(semaphore.tryAcquire(1, timeout));
FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 21);
time.start();
QVERIFY(semaphore.tryAcquire(1, timeout));
FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 20);
time.start();
QVERIFY(semaphore.tryAcquire(10, timeout));
FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 10);
time.start();
QVERIFY(semaphore.tryAcquire(10, timeout));
FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 0);
// should not be able to acquire more
time.start();
QVERIFY(!semaphore.tryAcquire(1, timeout));
FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(1, timeout));
FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(10, timeout));
FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(10, timeout));
FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
#undef FUZZYCOMPARE
}
void tst_QSemaphore::tryAcquireWithTimeoutStarvation()
{
class Thread : public QThread
{
public:
QSemaphore startup;
QSemaphore *semaphore;
int amountToConsume, timeout;
void run() override
{
startup.release();
forever {
if (!semaphore->tryAcquire(amountToConsume, timeout))
break;
semaphore->release(amountToConsume);
}
}
};
QSemaphore semaphore;
semaphore.release(1);
Thread consumer;
consumer.semaphore = &semaphore;
consumer.amountToConsume = 1;
consumer.timeout = 1000;
// start the thread and wait for it to start consuming
consumer.start();
consumer.startup.acquire();
// try to consume more than the thread we started is, and provide a longer
// timeout... we should timeout, not wait indefinitely
QVERIFY(!semaphore.tryAcquire(consumer.amountToConsume * 2, consumer.timeout * 2));
// the consumer should still be running
QVERIFY(consumer.isRunning() && !consumer.isFinished());
// acquire, and wait for smallConsumer to timeout
semaphore.acquire();
QVERIFY(consumer.wait());
}
void tst_QSemaphore::tryAcquireWithTimeoutForever_data()
{
QTest::addColumn<int>("timeout");
QTest::newRow("-1") << -1;
// tryAcquire is documented to take any negative value as "forever"
QTest::newRow("INT_MIN") << INT_MIN;
}
void tst_QSemaphore::tryAcquireWithTimeoutForever()
{
enum { WaitTime = 1000 };
struct Thread : public QThread {
QSemaphore sem;
void run() override
{
QTest::qWait(WaitTime);
sem.release(2);
}
};
QFETCH(int, timeout);
Thread t;
// sanity check it works if we can immediately acquire
t.sem.release(11);
QVERIFY(t.sem.tryAcquire(1, timeout));
QVERIFY(t.sem.tryAcquire(10, timeout));
// verify that we do wait for at least WaitTime if we can't acquire immediately
QElapsedTimer timer;
timer.start();
t.start();
QVERIFY(t.sem.tryAcquire(1, timeout));
QVERIFY(timer.elapsed() >= WaitTime);
QVERIFY(t.wait());
QCOMPARE(t.sem.available(), 1);
}
const char alphabet[] = "ACGTH";
const int AlphabetSize = sizeof(alphabet) - 1;
const int BufferSize = 4096; // GCD of BufferSize and alphabet size must be 1
static char buffer[BufferSize];
const int ProducerChunkSize = 3;
const int ConsumerChunkSize = 7;
const int Multiplier = 10;
// note: the code depends on the fact that DataSize is a multiple of
// ProducerChunkSize, ConsumerChunkSize, and BufferSize
const int DataSize = ProducerChunkSize * ConsumerChunkSize * BufferSize * Multiplier;
static QSemaphore freeSpace(BufferSize);
static QSemaphore usedSpace;
class Producer : public QThread
{
public:
void run() override;
};
static const auto Timeout = 1min;
void Producer::run()
{
for (int i = 0; i < DataSize; ++i) {
QVERIFY(freeSpace.tryAcquire(1, Timeout));
buffer[i % BufferSize] = alphabet[i % AlphabetSize];
usedSpace.release();
}
for (int i = 0; i < DataSize; ++i) {
if ((i % ProducerChunkSize) == 0)
QVERIFY(freeSpace.tryAcquire(ProducerChunkSize, Timeout));
buffer[i % BufferSize] = alphabet[i % AlphabetSize];
if ((i % ProducerChunkSize) == (ProducerChunkSize - 1))
usedSpace.release(ProducerChunkSize);
}
}
class Consumer : public QThread
{
public:
void run() override;
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
usedSpace.acquire();
QCOMPARE(buffer[i % BufferSize], alphabet[i % AlphabetSize]);
freeSpace.release();
}
for (int i = 0; i < DataSize; ++i) {
if ((i % ConsumerChunkSize) == 0)
usedSpace.acquire(ConsumerChunkSize);
QCOMPARE(buffer[i % BufferSize], alphabet[i % AlphabetSize]);
if ((i % ConsumerChunkSize) == (ConsumerChunkSize - 1))
freeSpace.release(ConsumerChunkSize);
}
}
void tst_QSemaphore::producerConsumer()
{
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
}
void tst_QSemaphore::raii()
{
QSemaphore sem;
QCOMPARE(sem.available(), 0);
// basic operation:
{
QSemaphoreReleaser r0;
const QSemaphoreReleaser r1(sem);
const QSemaphoreReleaser r2(sem, 2);
QCOMPARE(r0.semaphore(), nullptr);
QCOMPARE(r1.semaphore(), &sem);
QCOMPARE(r2.semaphore(), &sem);
}
QCOMPARE(sem.available(), 3);
// cancel:
{
const QSemaphoreReleaser r1(sem);
QSemaphoreReleaser r2(sem, 2);
QCOMPARE(r2.cancel(), &sem);
QCOMPARE(r2.semaphore(), nullptr);
}
QCOMPARE(sem.available(), 4);
// move-assignment:
{
const QSemaphoreReleaser r1(sem);
QSemaphoreReleaser r2(sem, 2);
QCOMPARE(sem.available(), 4);
r2 = QSemaphoreReleaser();
QCOMPARE(sem.available(), 6);
r2 = QSemaphoreReleaser(sem, 42);
QCOMPARE(sem.available(), 6);
}
QCOMPARE(sem.available(), 49);
}
void tst_QSemaphore::stdCompat()
{
QSemaphore sem(1);
auto now = [] { return std::chrono::steady_clock::now(); };
QVERIFY(sem.try_acquire());
QCOMPARE(sem.available(), 0);
QVERIFY(!sem.try_acquire_for(10ms));
QCOMPARE(sem.available(), 0);
QVERIFY(!sem.try_acquire_until(now() + 10ms));
QCOMPARE(sem.available(), 0);
sem.release(2);
QVERIFY(sem.try_acquire());
QVERIFY(sem.try_acquire_for(5ms));
QCOMPARE(sem.available(), 0);
QVERIFY(!sem.try_acquire_until(now() + 5ms));
QCOMPARE(sem.available(), 0);
sem.release(3);
QVERIFY(sem.try_acquire());
QVERIFY(sem.try_acquire_for(5s));
QVERIFY(sem.try_acquire_until(now() + 5s));
QCOMPARE(sem.available(), 0);
}
QTEST_MAIN(tst_QSemaphore)
#include "tst_qsemaphore.moc"

View File

@ -0,0 +1,5 @@
[wait3_slowDestructor]
windows-10
[sleep]
windows-7sp1

View File

@ -0,0 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthread Test:
#####################################################################
qt_internal_add_test(tst_qthread
SOURCES
tst_qthread.cpp
LIBRARIES
Qt::TestPrivate
)
## Scopes:
#####################################################################

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthreadonce Test:
#####################################################################
qt_internal_add_test(tst_qthreadonce
SOURCES
qthreadonce.cpp
tst_qthreadonce.cpp
LIBRARIES
Qt::TestPrivate
)

View File

@ -0,0 +1,80 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "qplatformdefs.h"
#include "qthreadonce.h"
#include "qmutex.h"
Q_GLOBAL_STATIC(QRecursiveMutex, onceInitializationMutex)
enum QOnceExtra {
MustRunCode = 0x01,
MustUnlockMutex = 0x02
};
/*!
\internal
Initialize the Q_ONCE structure.
Q_ONCE consists of two variables:
- a static POD QOnceControl::ControlVariable (it's a QBasicAtomicInt)
- an automatic QOnceControl that controls the former
The POD is initialized to 0.
When QOnceControl's constructor starts, it'll lock the global
initialization mutex. It'll then check if it's the first to up
the control variable and will take note.
The QOnceControl's destructor will unlock the global
initialization mutex.
*/
QOnceControl::QOnceControl(QBasicAtomicInt *control)
{
d = 0;
gv = control;
// check if code has already run once
if (gv->loadAcquire() == 2) {
// uncontended case: it has already initialized
// no waiting
return;
}
// acquire the path
onceInitializationMutex()->lock();
extra = MustUnlockMutex;
if (gv->testAndSetAcquire(0, 1)) {
// path acquired, we're the first
extra |= MustRunCode;
}
}
QOnceControl::~QOnceControl()
{
if (mustRunCode())
// code wasn't run!
gv->testAndSetRelease(1, 0);
else
gv->testAndSetRelease(1, 2);
if (extra & MustUnlockMutex)
onceInitializationMutex()->unlock();
}
/*!
\internal
Returns true if the initialization code must be run.
Obviously, the initialization code must be run only once...
*/
bool QOnceControl::mustRunCode()
{
return extra & MustRunCode;
}
void QOnceControl::done()
{
extra &= ~MustRunCode;
}

View File

@ -0,0 +1,67 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QTHREADONCE_H
#define QTHREADONCE_H
#include <QtCore/qglobal.h>
#include <QtCore/qatomic.h>
class QOnceControl
{
public:
QOnceControl(QBasicAtomicInt *);
~QOnceControl();
bool mustRunCode();
void done();
private:
QBasicAtomicInt *gv;
union {
qint32 extra;
void *d;
};
};
#define Q_ONCE_GV_NAME2(prefix, line) prefix ## line
#define Q_ONCE_GV_NAME(prefix, line) Q_ONCE_GV_NAME2(prefix, line)
#define Q_ONCE_GV Q_ONCE_GV_NAME(_q_once_, __LINE__)
#define Q_ONCE \
static QBasicAtomicInt Q_ONCE_GV = Q_BASIC_ATOMIC_INITIALIZER(0); \
if (0){} else \
for (QOnceControl _control_(&Q_ONCE_GV); _control_.mustRunCode(); _control_.done())
template<typename T>
class QSingleton
{
// this is a POD-like class
struct Destructor
{
T *&pointer;
Destructor(T *&ptr) : pointer(ptr) {}
~Destructor() { delete pointer; }
};
public:
T *_q_value;
QBasicAtomicInt _q_guard;
inline T *value()
{
for (QOnceControl control(&_q_guard); control.mustRunCode(); control.done()) {
_q_value = new T();
static Destructor cleanup(_q_value);
}
return _q_value;
}
inline T& operator*() { return *value(); }
inline T* operator->() { return value(); }
inline operator T*() { return value(); }
};
#endif

View File

@ -0,0 +1,203 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QSemaphore>
#include <qcoreapplication.h>
#include <qmutex.h>
#include <qthread.h>
#include <qwaitcondition.h>
#include "qthreadonce.h"
#include <QtTest/private/qemulationdetector_p.h>
class tst_QThreadOnce : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void sameThread();
void sameThread_data();
void multipleThreads();
void nesting();
void reentering();
#ifndef QT_NO_EXCEPTIONS
void exception();
#endif
};
void tst_QThreadOnce::initTestCase()
{
if (QTestPrivate::isRunningArmOnX86())
QSKIP("Flaky on QEMU, QTBUG-94737");
}
class SingletonObject: public QObject
{
Q_OBJECT
public:
static int runCount;
SingletonObject() { val.storeRelaxed(42); ++runCount; }
~SingletonObject() { }
QBasicAtomicInt val;
};
class IncrementThread: public QThread
{
public:
static QBasicAtomicInt runCount;
static QSingleton<SingletonObject> singleton;
QSemaphore &sem1, &sem2;
int &var;
IncrementThread(QSemaphore *psem1, QSemaphore *psem2, int *pvar, QObject *parent)
: QThread(parent), sem1(*psem1), sem2(*psem2), var(*pvar)
{ start(); }
~IncrementThread() { wait(); }
protected:
void run() override
{
sem2.release();
sem1.acquire(); // synchronize
Q_ONCE {
++var;
}
runCount.ref();
singleton->val.ref();
}
};
int SingletonObject::runCount = 0;
QBasicAtomicInt IncrementThread::runCount = Q_BASIC_ATOMIC_INITIALIZER(0);
QSingleton<SingletonObject> IncrementThread::singleton;
void tst_QThreadOnce::sameThread_data()
{
SingletonObject::runCount = 0;
QTest::addColumn<int>("expectedValue");
QTest::newRow("first") << 42;
QTest::newRow("second") << 43;
}
void tst_QThreadOnce::sameThread()
{
static int controlVariable = 0;
Q_ONCE {
QCOMPARE(controlVariable, 0);
++controlVariable;
}
QCOMPARE(controlVariable, 1);
static QSingleton<SingletonObject> s;
QTEST(int(s->val.loadRelaxed()), "expectedValue");
s->val.ref();
QCOMPARE(SingletonObject::runCount, 1);
}
void tst_QThreadOnce::multipleThreads()
{
#if defined(Q_OS_VXWORKS)
const int NumberOfThreads = 20;
#else
const int NumberOfThreads = 100;
#endif
int controlVariable = 0;
QSemaphore sem1, sem2(NumberOfThreads);
QObject *parent = new QObject;
for (int i = 0; i < NumberOfThreads; ++i)
new IncrementThread(&sem1, &sem2, &controlVariable, parent);
QCOMPARE(controlVariable, 0); // nothing must have set them yet
SingletonObject::runCount = 0;
IncrementThread::runCount.storeRelaxed(0);
// wait for all of them to be ready
sem2.acquire(NumberOfThreads);
// unleash the threads
sem1.release(NumberOfThreads);
// wait for all of them to terminate:
delete parent;
QCOMPARE(controlVariable, 1);
QCOMPARE(int(IncrementThread::runCount.loadRelaxed()), NumberOfThreads);
QCOMPARE(SingletonObject::runCount, 1);
}
void tst_QThreadOnce::nesting()
{
int variable = 0;
Q_ONCE {
Q_ONCE {
++variable;
}
}
QCOMPARE(variable, 1);
}
static void reentrant(int control, int &counter)
{
Q_ONCE {
if (counter)
reentrant(--control, counter);
++counter;
}
static QSingleton<SingletonObject> s;
s->val.ref();
}
void tst_QThreadOnce::reentering()
{
const int WantedRecursions = 5;
int count = 0;
SingletonObject::runCount = 0;
reentrant(WantedRecursions, count);
// reentrancy is undefined behavior:
QVERIFY(count == 1 || count == WantedRecursions);
QCOMPARE(SingletonObject::runCount, 1);
}
#if !defined(QT_NO_EXCEPTIONS)
static void exception_helper(int &val)
{
Q_ONCE {
if (val++ == 0) throw 0;
}
}
#endif
#ifndef QT_NO_EXCEPTIONS
void tst_QThreadOnce::exception()
{
int count = 0;
try {
exception_helper(count);
} catch (...) {
// nothing
}
QCOMPARE(count, 1);
try {
exception_helper(count);
} catch (...) {
QVERIFY2(false, "Exception shouldn't have been thrown...");
}
QCOMPARE(count, 2);
}
#endif
QTEST_MAIN(tst_QThreadOnce)
#include "tst_qthreadonce.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthreadpool Test:
#####################################################################
qt_internal_add_test(tst_qthreadpool
SOURCES
tst_qthreadpool.cpp
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthreadstorage Test:
#####################################################################
qt_internal_add_test(tst_qthreadstorage
SOURCES
tst_qthreadstorage.cpp
)
## Scopes:
#####################################################################
if(NOT ANDROID)
add_subdirectory(crashonexit)
if(QT_FEATURE_process)
add_dependencies(tst_qthreadstorage crashOnExit_helper)
endif()
endif()

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## crashonexit Binary:
#####################################################################
qt_internal_add_executable(crashOnExit_helper
OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.."
INSTALL_DIRECTORY "${INSTALL_TESTSDIR}/tst_qthreadstorage/crashOnExit_helper"
SOURCES
crashOnExit.cpp
)

View File

@ -0,0 +1,27 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/QCoreApplication>
#include <QtCore/QThreadStorage>
class Class
{
public:
~Class()
{
// trigger creation of a new QThreadStorage, after the previous QThreadStorage from main() was destructed
static QThreadStorage<int *> threadstorage;
threadstorage.setLocalData(new int);
threadstorage.setLocalData(new int);
}
};
int main()
{
// instantiate the class that will use QThreadStorage from its destructor, it's destructor will be run last
static Class instance;
// instantiate QThreadStorage, it's destructor (and the global destructors for QThreadStorages internals) will run first
static QThreadStorage<int *> threadstorage;
threadstorage.setLocalData(new int);
threadstorage.setLocalData(new int);
}

View File

@ -0,0 +1,475 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#if QT_CONFIG(process)
#include <QProcess>
#endif
#include <QTestEventLoop>
#include <qcoreapplication.h>
#include <qmutex.h>
#include <qthread.h>
#include <qwaitcondition.h>
#include <qthreadstorage.h>
#include <qdir.h>
#include <qfileinfo.h>
#ifdef Q_OS_UNIX
#include <pthread.h>
#endif
#ifdef Q_OS_WIN
# include <process.h>
# include <qt_windows.h>
#endif
class tst_QThreadStorage : public QObject
{
Q_OBJECT
private slots:
void hasLocalData();
void localData();
void localData_const();
void setLocalData();
void autoDelete();
void adoptedThreads();
void ensureCleanupOrder();
void crashOnExit();
void leakInDestructor();
void resetInDestructor();
void valueBased();
};
class Pointer
{
public:
static int count;
inline Pointer() { ++count; }
inline ~Pointer() { --count; }
};
int Pointer::count = 0;
void tst_QThreadStorage::hasLocalData()
{
QThreadStorage<Pointer *> pointers;
QVERIFY(!pointers.hasLocalData());
pointers.setLocalData(new Pointer);
QVERIFY(pointers.hasLocalData());
pointers.setLocalData(nullptr);
QVERIFY(!pointers.hasLocalData());
}
void tst_QThreadStorage::localData()
{
QThreadStorage<Pointer*> pointers;
Pointer *p = new Pointer;
QVERIFY(!pointers.hasLocalData());
pointers.setLocalData(p);
QVERIFY(pointers.hasLocalData());
QCOMPARE(pointers.localData(), p);
pointers.setLocalData(nullptr);
QCOMPARE(pointers.localData(), nullptr);
QVERIFY(!pointers.hasLocalData());
}
void tst_QThreadStorage::localData_const()
{
QThreadStorage<Pointer *> pointers;
const QThreadStorage<Pointer *> &const_pointers = pointers;
Pointer *p = new Pointer;
QVERIFY(!pointers.hasLocalData());
pointers.setLocalData(p);
QVERIFY(pointers.hasLocalData());
QCOMPARE(const_pointers.localData(), p);
pointers.setLocalData(nullptr);
QCOMPARE(const_pointers.localData(), nullptr);
QVERIFY(!pointers.hasLocalData());
}
void tst_QThreadStorage::setLocalData()
{
QThreadStorage<Pointer *> pointers;
QVERIFY(!pointers.hasLocalData());
pointers.setLocalData(new Pointer);
QVERIFY(pointers.hasLocalData());
pointers.setLocalData(nullptr);
QVERIFY(!pointers.hasLocalData());
}
class Thread : public QThread
{
public:
QThreadStorage<Pointer *> &pointers;
QMutex mutex;
QWaitCondition cond;
Thread(QThreadStorage<Pointer *> &p)
: pointers(p)
{ }
void run() override
{
pointers.setLocalData(new Pointer);
QMutexLocker locker(&mutex);
cond.wakeOne();
cond.wait(&mutex);
}
};
void tst_QThreadStorage::autoDelete()
{
QThreadStorage<Pointer *> pointers;
QVERIFY(!pointers.hasLocalData());
Thread thread(pointers);
int c = Pointer::count;
{
QMutexLocker locker(&thread.mutex);
thread.start();
thread.cond.wait(&thread.mutex);
// QCOMPARE(Pointer::count, c + 1);
thread.cond.wakeOne();
}
thread.wait();
QCOMPARE(Pointer::count, c);
}
static bool threadStorageOk;
void testAdoptedThreadStorageWin(void *p)
{
QThreadStorage<Pointer *> *pointers = reinterpret_cast<QThreadStorage<Pointer *> *>(p);
if (pointers->hasLocalData()) {
threadStorageOk = false;
return;
}
Pointer *pointer = new Pointer();
pointers->setLocalData(pointer);
if (pointers->hasLocalData() == false) {
threadStorageOk = false;
return;
}
if (pointers->localData() != pointer) {
threadStorageOk = false;
return;
}
QObject::connect(QThread::currentThread(), SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
}
void *testAdoptedThreadStorageUnix(void *pointers)
{
testAdoptedThreadStorageWin(pointers);
return nullptr;
}
void tst_QThreadStorage::adoptedThreads()
{
QTestEventLoop::instance(); // Make sure the instance is created in this thread.
QThreadStorage<Pointer *> pointers;
int c = Pointer::count;
threadStorageOk = true;
{
#ifdef Q_OS_UNIX
pthread_t thread;
const int state = pthread_create(&thread, nullptr, testAdoptedThreadStorageUnix, &pointers);
QCOMPARE(state, 0);
pthread_join(thread, nullptr);
#elif defined Q_OS_WIN
HANDLE thread;
thread = (HANDLE)_beginthread(testAdoptedThreadStorageWin, 0, &pointers);
QVERIFY(thread);
WaitForSingleObject(thread, INFINITE);
#endif
}
QVERIFY(threadStorageOk);
QTestEventLoop::instance().enterLoop(2);
QVERIFY(!QTestEventLoop::instance().timeout());
QTRY_COMPARE(Pointer::count, c);
}
static QBasicAtomicInt cleanupOrder = Q_BASIC_ATOMIC_INITIALIZER(0);
class First
{
public:
~First()
{
order = cleanupOrder.fetchAndAddRelaxed(1);
}
static int order;
};
int First::order = -1;
class Second
{
public:
~Second()
{
order = cleanupOrder.fetchAndAddRelaxed(1);
}
static int order;
};
int Second::order = -1;
void tst_QThreadStorage::ensureCleanupOrder()
{
class Thread : public QThread
{
public:
QThreadStorage<First *> &first;
QThreadStorage<Second *> &second;
Thread(QThreadStorage<First *> &first,
QThreadStorage<Second *> &second)
: first(first), second(second)
{ }
void run() override
{
// set in reverse order, but shouldn't matter, the data
// will be deleted in the order the thread storage objects
// were created
second.setLocalData(new Second);
first.setLocalData(new First);
}
};
QThreadStorage<Second *> second;
QThreadStorage<First *> first;
Thread thread(first, second);
thread.start();
thread.wait();
QVERIFY(First::order < Second::order);
}
#if QT_CONFIG(process)
static inline bool runCrashOnExit(const QString &binary, QString *errorMessage)
{
const int timeout = 60000;
QProcess process;
process.start(binary);
if (!process.waitForStarted()) {
*errorMessage = QString::fromLatin1("Could not start '%1': %2").arg(binary, process.errorString());
return false;
}
if (!process.waitForFinished(timeout)) {
process.kill();
*errorMessage = QString::fromLatin1("Timeout (%1ms) waiting for %2.").arg(timeout).arg(binary);
return false;
}
if (process.exitStatus() != QProcess::NormalExit) {
*errorMessage = binary + QStringLiteral(" crashed.");
return false;
}
return true;
}
#endif
void tst_QThreadStorage::crashOnExit()
{
#ifdef Q_OS_ANDROID
QSKIP("Can't start QProcess to run a custom user binary on Android");
#endif
#if !QT_CONFIG(process)
QSKIP("No qprocess support", SkipAll);
#else
QString errorMessage;
QVERIFY2(runCrashOnExit("./crashOnExit_helper", &errorMessage),
qPrintable(errorMessage));
#endif
}
// S stands for thread Safe.
class SPointer
{
public:
static QBasicAtomicInt count;
inline SPointer() { count.ref(); }
inline ~SPointer() { count.deref(); }
inline SPointer(const SPointer & /* other */) { count.ref(); }
};
QBasicAtomicInt SPointer::count = Q_BASIC_ATOMIC_INITIALIZER(0);
Q_GLOBAL_STATIC(QThreadStorage<SPointer *>, threadStoragePointers1)
Q_GLOBAL_STATIC(QThreadStorage<SPointer *>, threadStoragePointers2)
class ThreadStorageLocalDataTester
{
public:
SPointer member;
inline ~ThreadStorageLocalDataTester() {
QVERIFY(!threadStoragePointers1()->hasLocalData());
QVERIFY(!threadStoragePointers2()->hasLocalData());
threadStoragePointers2()->setLocalData(new SPointer);
threadStoragePointers1()->setLocalData(new SPointer);
QVERIFY(threadStoragePointers1()->hasLocalData());
QVERIFY(threadStoragePointers2()->hasLocalData());
}
};
void tst_QThreadStorage::leakInDestructor()
{
class Thread : public QThread
{
public:
QThreadStorage<ThreadStorageLocalDataTester *> &tls;
Thread(QThreadStorage<ThreadStorageLocalDataTester *> &t) : tls(t) { }
void run() override
{
QVERIFY(!tls.hasLocalData());
tls.setLocalData(new ThreadStorageLocalDataTester);
QVERIFY(tls.hasLocalData());
}
};
int c = SPointer::count.loadRelaxed();
QThreadStorage<ThreadStorageLocalDataTester *> tls;
QVERIFY(!threadStoragePointers1()->hasLocalData());
QThreadStorage<int *> tls2; //add some more tls to make sure ids are not following each other too much
QThreadStorage<int *> tls3;
QVERIFY(!tls2.hasLocalData());
QVERIFY(!tls3.hasLocalData());
QVERIFY(!tls.hasLocalData());
Thread t1(tls);
Thread t2(tls);
Thread t3(tls);
t1.start();
t2.start();
t3.start();
QVERIFY(t1.wait());
QVERIFY(t2.wait());
QVERIFY(t3.wait());
//check all the constructed things have been destructed
QCOMPARE(int(SPointer::count.loadRelaxed()), c);
}
class ThreadStorageResetLocalDataTester {
public:
SPointer member;
~ThreadStorageResetLocalDataTester();
};
Q_GLOBAL_STATIC(QThreadStorage<ThreadStorageResetLocalDataTester *>, ThreadStorageResetLocalDataTesterTls)
ThreadStorageResetLocalDataTester::~ThreadStorageResetLocalDataTester() {
//Quite stupid, but WTF::ThreadSpecific<T>::destroy does it.
ThreadStorageResetLocalDataTesterTls()->setLocalData(this);
}
void tst_QThreadStorage::resetInDestructor()
{
class Thread : public QThread
{
public:
void run() override
{
QVERIFY(!ThreadStorageResetLocalDataTesterTls()->hasLocalData());
ThreadStorageResetLocalDataTesterTls()->setLocalData(new ThreadStorageResetLocalDataTester);
QVERIFY(ThreadStorageResetLocalDataTesterTls()->hasLocalData());
}
};
int c = SPointer::count.loadRelaxed();
Thread t1;
Thread t2;
Thread t3;
t1.start();
t2.start();
t3.start();
QVERIFY(t1.wait());
QVERIFY(t2.wait());
QVERIFY(t3.wait());
//check all the constructed things have been destructed
QCOMPARE(int(SPointer::count.loadRelaxed()), c);
}
void tst_QThreadStorage::valueBased()
{
struct Thread : QThread {
QThreadStorage<SPointer> &tlsSPointer;
QThreadStorage<QString> &tlsString;
QThreadStorage<int> &tlsInt;
int someNumber;
QString someString;
Thread(QThreadStorage<SPointer> &t1, QThreadStorage<QString> &t2, QThreadStorage<int> &t3)
: tlsSPointer(t1), tlsString(t2), tlsInt(t3) { }
void run() override
{
/*QVERIFY(!tlsSPointer.hasLocalData());
QVERIFY(!tlsString.hasLocalData());
QVERIFY(!tlsInt.hasLocalData());*/
SPointer pointercopy = tlsSPointer.localData();
//Default constructed values
QVERIFY(tlsString.localData().isNull());
QCOMPARE(tlsInt.localData(), 0);
//setting
tlsString.setLocalData(someString);
tlsInt.setLocalData(someNumber);
QCOMPARE(tlsString.localData(), someString);
QCOMPARE(tlsInt.localData(), someNumber);
//changing
tlsSPointer.setLocalData(SPointer());
tlsInt.localData() += 42;
tlsString.localData().append(QLatin1String(" world"));
QCOMPARE(tlsString.localData(), (someString + QLatin1String(" world")));
QCOMPARE(tlsInt.localData(), (someNumber + 42));
// operator=
tlsString.localData() = QString::number(someNumber);
QCOMPARE(tlsString.localData().toInt(), someNumber);
}
};
QThreadStorage<SPointer> tlsSPointer;
QThreadStorage<QString> tlsString;
QThreadStorage<int> tlsInt;
int c = SPointer::count.loadRelaxed();
Thread t1(tlsSPointer, tlsString, tlsInt);
Thread t2(tlsSPointer, tlsString, tlsInt);
Thread t3(tlsSPointer, tlsString, tlsInt);
t1.someNumber = 42;
t2.someNumber = -128;
t3.someNumber = 78;
t1.someString = "hello";
t2.someString = "australia";
t3.someString = "nokia";
t1.start();
t2.start();
t3.start();
QVERIFY(t1.wait());
QVERIFY(t2.wait());
QVERIFY(t3.wait());
QCOMPARE(c, int(SPointer::count.loadRelaxed()));
}
QTEST_MAIN(tst_QThreadStorage)
#include "tst_qthreadstorage.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qwaitcondition Test:
#####################################################################
qt_internal_add_test(tst_qwaitcondition
SOURCES
tst_qwaitcondition.cpp
)

View File

@ -0,0 +1,853 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QReadWriteLock>
#include <qatomic.h>
#include <qcoreapplication.h>
#include <qmutex.h>
#include <qthread.h>
#include <qwaitcondition.h>
#define COND_WAIT_TIME 1
class tst_QWaitCondition : public QObject
{
Q_OBJECT
private slots:
void wait_QMutex();
void wait_QReadWriteLock();
void wakeOne();
void wakeAll();
void wait_RaceCondition();
};
static const int iterations = 4;
static const int ThreadCount = 4;
// Terminate thread in destructor for threads instantiated on the stack
class TerminatingThread : public QThread
{
public:
explicit TerminatingThread()
{
setTerminationEnabled(true);
}
~TerminatingThread()
{
if (isRunning()) {
qWarning() << "forcibly terminating " << objectName();
terminate();
}
}
};
class wait_QMutex_Thread_1 : public TerminatingThread
{
public:
QMutex mutex;
QWaitCondition cond;
inline wait_QMutex_Thread_1()
{ }
void run() override
{
mutex.lock();
cond.wakeOne();
cond.wait(&mutex);
mutex.unlock();
}
};
class wait_QMutex_Thread_2 : public TerminatingThread
{
public:
QWaitCondition started;
QMutex *mutex;
QWaitCondition *cond;
inline wait_QMutex_Thread_2()
: mutex(nullptr), cond(nullptr)
{ }
void run() override
{
mutex->lock();
started.wakeOne();
cond->wait(mutex);
mutex->unlock();
}
};
class wait_QReadWriteLock_Thread_1 : public TerminatingThread
{
public:
QReadWriteLock readWriteLock;
QWaitCondition cond;
inline wait_QReadWriteLock_Thread_1()
{ }
void run() override
{
readWriteLock.lockForWrite();
cond.wakeOne();
cond.wait(&readWriteLock);
readWriteLock.unlock();
}
};
class wait_QReadWriteLock_Thread_2 : public TerminatingThread
{
public:
QWaitCondition started;
QReadWriteLock *readWriteLock;
QWaitCondition *cond;
inline wait_QReadWriteLock_Thread_2()
: readWriteLock(nullptr), cond(nullptr)
{ }
void run() override
{
readWriteLock->lockForRead();
started.wakeOne();
cond->wait(readWriteLock);
readWriteLock->unlock();
}
};
void tst_QWaitCondition::wait_QMutex()
{
int x;
for (int i = 0; i < iterations; ++i) {
{
QMutex mutex;
QWaitCondition cond;
mutex.lock();
cond.wakeOne();
QVERIFY(!cond.wait(&mutex, 1));
cond.wakeAll();
QVERIFY(!cond.wait(&mutex, 1));
mutex.unlock();
}
{
// test multiple threads waiting on separate wait conditions
wait_QMutex_Thread_1 thread[ThreadCount];
const QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex_")
+ QString::number(i) + QLatin1Char('_');
for (x = 0; x < ThreadCount; ++x) {
thread[x].setObjectName(prefix + QString::number(x));
thread[x].mutex.lock();
thread[x].start();
// wait for thread to start
QVERIFY(thread[x].cond.wait(&thread[x].mutex, 1000));
thread[x].mutex.unlock();
}
for (x = 0; x < ThreadCount; ++x) {
QVERIFY(thread[x].isRunning());
QVERIFY(!thread[x].isFinished());
}
for (x = 0; x < ThreadCount; ++x) {
thread[x].mutex.lock();
thread[x].cond.wakeOne();
thread[x].mutex.unlock();
}
for (x = 0; x < ThreadCount; ++x) {
QVERIFY(thread[x].wait(1000));
}
}
{
// test multiple threads waiting on a wait condition
QMutex mutex;
QWaitCondition cond1, cond2;
wait_QMutex_Thread_2 thread[ThreadCount];
const QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex_")
+ QString::number(i) + QLatin1Char('_');
mutex.lock();
for (x = 0; x < ThreadCount; ++x) {
thread[x].setObjectName(prefix + QString::number(x));
thread[x].mutex = &mutex;
thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2;
thread[x].start();
// wait for thread to start
QVERIFY(thread[x].started.wait(&mutex, 1000));
}
mutex.unlock();
for (x = 0; x < ThreadCount; ++x) {
QVERIFY(thread[x].isRunning());
QVERIFY(!thread[x].isFinished());
}
mutex.lock();
cond1.wakeAll();
cond2.wakeAll();
mutex.unlock();
for (x = 0; x < ThreadCount; ++x) {
QVERIFY(thread[x].wait(1000));
}
}
}
}
void tst_QWaitCondition::wait_QReadWriteLock()
{
{
QReadWriteLock readWriteLock(QReadWriteLock::Recursive);
QWaitCondition waitCondition;
// ensure that the lockForRead is correctly restored
readWriteLock.lockForRead();
QVERIFY(!waitCondition.wait(&readWriteLock, 1));
QVERIFY(!readWriteLock.tryLockForWrite());
QVERIFY(readWriteLock.tryLockForRead());
readWriteLock.unlock();
QVERIFY(!readWriteLock.tryLockForWrite());
readWriteLock.unlock();
QVERIFY(readWriteLock.tryLockForWrite());
readWriteLock.unlock();
}
{
QReadWriteLock readWriteLock(QReadWriteLock::Recursive);
QWaitCondition waitCondition;
// ensure that the lockForWrite is correctly restored
readWriteLock.lockForWrite();
QVERIFY(!waitCondition.wait(&readWriteLock, 1));
QVERIFY(!readWriteLock.tryLockForRead());
QVERIFY(readWriteLock.tryLockForWrite());
readWriteLock.unlock();
QVERIFY(!readWriteLock.tryLockForRead());
readWriteLock.unlock();
QVERIFY(readWriteLock.tryLockForRead());
readWriteLock.unlock();
}
int x;
for (int i = 0; i < iterations; ++i) {
{
QReadWriteLock readWriteLock;
QWaitCondition waitCondition;
readWriteLock.lockForRead();
waitCondition.wakeOne();
QVERIFY(!waitCondition.wait(&readWriteLock, 1));
waitCondition.wakeAll();
QVERIFY(!waitCondition.wait(&readWriteLock, 1));
readWriteLock.unlock();
}
{
QReadWriteLock readWriteLock;
QWaitCondition waitCondition;
readWriteLock.lockForWrite();
waitCondition.wakeOne();
QVERIFY(!waitCondition.wait(&readWriteLock, 1));
waitCondition.wakeAll();
QVERIFY(!waitCondition.wait(&readWriteLock, 1));
readWriteLock.unlock();
}
{
// test multiple threads waiting on separate wait conditions
wait_QReadWriteLock_Thread_1 thread[ThreadCount];
const QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_lockforread_");
for (x = 0; x < ThreadCount; ++x) {
thread[x].setObjectName(prefix + QString::number(x));
thread[x].readWriteLock.lockForRead();
thread[x].start();
// wait for thread to start
QVERIFY(thread[x].cond.wait(&thread[x].readWriteLock, 1000));
thread[x].readWriteLock.unlock();
}
for (x = 0; x < ThreadCount; ++x) {
QVERIFY(thread[x].isRunning());
QVERIFY(!thread[x].isFinished());
}
for (x = 0; x < ThreadCount; ++x) {
thread[x].readWriteLock.lockForRead();
thread[x].cond.wakeOne();
thread[x].readWriteLock.unlock();
}
for (x = 0; x < ThreadCount; ++x) {
QVERIFY(thread[x].wait(1000));
}
}
{
// test multiple threads waiting on a wait condition
QReadWriteLock readWriteLock;
QWaitCondition cond1, cond2;
wait_QReadWriteLock_Thread_2 thread[ThreadCount];
const QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_lockforwrite_");
readWriteLock.lockForWrite();
for (x = 0; x < ThreadCount; ++x) {
thread[x].setObjectName(prefix + QString::number(x));
thread[x].readWriteLock = &readWriteLock;
thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2;
thread[x].start();
// wait for thread to start
QVERIFY(thread[x].started.wait(&readWriteLock, 1000));
}
readWriteLock.unlock();
for (x = 0; x < ThreadCount; ++x) {
QVERIFY(thread[x].isRunning());
QVERIFY(!thread[x].isFinished());
}
readWriteLock.lockForWrite();
cond1.wakeAll();
cond2.wakeAll();
readWriteLock.unlock();
for (x = 0; x < ThreadCount; ++x) {
QVERIFY(thread[x].wait(1000));
}
}
}
}
class WakeThreadBase : public TerminatingThread
{
public:
QAtomicInt *count;
WakeThreadBase() : count(nullptr) {}
};
class wake_Thread : public WakeThreadBase
{
public:
QWaitCondition started;
QWaitCondition dummy;
QMutex *mutex;
QWaitCondition *cond;
inline wake_Thread()
: mutex(nullptr), cond(nullptr)
{ }
static inline void sleep(ulong s)
{ QThread::sleep(s); }
void run() override
{
Q_ASSERT(count);
Q_ASSERT(mutex);
Q_ASSERT(cond);
mutex->lock();
++*count;
dummy.wakeOne(); // this wakeup should be lost
started.wakeOne();
dummy.wakeAll(); // this one too
cond->wait(mutex);
--*count;
mutex->unlock();
}
};
class wake_Thread_2 : public WakeThreadBase
{
public:
QWaitCondition started;
QWaitCondition dummy;
QReadWriteLock *readWriteLock;
QWaitCondition *cond;
inline wake_Thread_2()
: readWriteLock(nullptr), cond(nullptr)
{ }
static inline void sleep(ulong s)
{ QThread::sleep(s); }
void run() override
{
Q_ASSERT(count);
Q_ASSERT(readWriteLock);
Q_ASSERT(cond);
readWriteLock->lockForWrite();
++*count;
dummy.wakeOne(); // this wakeup should be lost
started.wakeOne();
dummy.wakeAll(); // this one too
cond->wait(readWriteLock);
--*count;
readWriteLock->unlock();
}
};
void tst_QWaitCondition::wakeOne()
{
static const int firstWaitInterval = 1000;
static const int waitInterval = 30;
int x;
QAtomicInt count;
// wake up threads, one at a time
for (int i = 0; i < iterations; ++i) {
QMutex mutex;
QWaitCondition cond;
// QMutex
wake_Thread thread[ThreadCount];
bool thread_exited[ThreadCount];
QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex_")
+ QString::number(i) + QLatin1Char('_');
mutex.lock();
for (x = 0; x < ThreadCount; ++x) {
thread[x].setObjectName(prefix + QString::number(x));
thread[x].count = &count;
thread[x].mutex = &mutex;
thread[x].cond = &cond;
thread_exited[x] = false;
thread[x].start();
// wait for thread to start
QVERIFY(thread[x].started.wait(&mutex, 1000));
// make sure wakeups are not queued... if nothing is
// waiting at the time of the wakeup, nothing happens
QVERIFY(!thread[x].dummy.wait(&mutex, 1));
}
mutex.unlock();
QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up threads one at a time
for (x = 0; x < ThreadCount; ++x) {
mutex.lock();
cond.wakeOne();
QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
QVERIFY(!thread[x].dummy.wait(&mutex, 1));
mutex.unlock();
int exited = 0;
for (int y = 0; y < ThreadCount; ++y) {
if (thread_exited[y])
continue;
if (thread[y].wait(exited > 0 ? waitInterval : firstWaitInterval)) {
thread_exited[y] = true;
++exited;
}
}
QCOMPARE(exited, 1);
QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 1));
}
QCOMPARE(count.loadRelaxed(), 0);
// QReadWriteLock
QReadWriteLock readWriteLock;
wake_Thread_2 rwthread[ThreadCount];
prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_readwritelock_")
+ QString::number(i) + QLatin1Char('_');
readWriteLock.lockForWrite();
for (x = 0; x < ThreadCount; ++x) {
rwthread[x].setObjectName(prefix + QString::number(x));
rwthread[x].count = &count;
rwthread[x].readWriteLock = &readWriteLock;
rwthread[x].cond = &cond;
thread_exited[x] = false;
rwthread[x].start();
// wait for thread to start
QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
// make sure wakeups are not queued... if nothing is
// waiting at the time of the wakeup, nothing happens
QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
}
readWriteLock.unlock();
QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up threads one at a time
for (x = 0; x < ThreadCount; ++x) {
readWriteLock.lockForWrite();
cond.wakeOne();
QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
readWriteLock.unlock();
int exited = 0;
for (int y = 0; y < ThreadCount; ++y) {
if (thread_exited[y])
continue;
if (rwthread[y].wait(exited > 0 ? waitInterval : firstWaitInterval)) {
thread_exited[y] = true;
++exited;
}
}
QCOMPARE(exited, 1);
QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 1));
}
QCOMPARE(count.loadRelaxed(), 0);
}
// wake up threads, two at a time
for (int i = 0; i < iterations; ++i) {
QMutex mutex;
QWaitCondition cond;
// QMutex
wake_Thread thread[ThreadCount];
bool thread_exited[ThreadCount];
QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex2_")
+ QString::number(i) + QLatin1Char('_');
mutex.lock();
for (x = 0; x < ThreadCount; ++x) {
thread[x].setObjectName(prefix + QString::number(x));
thread[x].count = &count;
thread[x].mutex = &mutex;
thread[x].cond = &cond;
thread_exited[x] = false;
thread[x].start();
// wait for thread to start
QVERIFY(thread[x].started.wait(&mutex, 1000));
// make sure wakeups are not queued... if nothing is
// waiting at the time of the wakeup, nothing happens
QVERIFY(!thread[x].dummy.wait(&mutex, 1));
}
mutex.unlock();
QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up threads one at a time
for (x = 0; x < ThreadCount; x += 2) {
mutex.lock();
cond.wakeOne();
cond.wakeOne();
QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
QVERIFY(!thread[x].dummy.wait(&mutex, 1));
QVERIFY(!thread[x + 1].dummy.wait(&mutex, 1));
mutex.unlock();
int exited = 0;
for (int y = 0; y < ThreadCount; ++y) {
if (thread_exited[y])
continue;
if (thread[y].wait(exited > 0 ? waitInterval : firstWaitInterval)) {
thread_exited[y] = true;
++exited;
}
}
QCOMPARE(exited, 2);
QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 2));
}
QCOMPARE(count.loadRelaxed(), 0);
// QReadWriteLock
QReadWriteLock readWriteLock;
wake_Thread_2 rwthread[ThreadCount];
prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_readwritelock_")
+ QString::number(i) + QLatin1Char('_');
readWriteLock.lockForWrite();
for (x = 0; x < ThreadCount; ++x) {
rwthread[x].setObjectName(prefix + QString::number(x));
rwthread[x].count = &count;
rwthread[x].readWriteLock = &readWriteLock;
rwthread[x].cond = &cond;
thread_exited[x] = false;
rwthread[x].start();
// wait for thread to start
QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
// make sure wakeups are not queued... if nothing is
// waiting at the time of the wakeup, nothing happens
QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
}
readWriteLock.unlock();
QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up threads one at a time
for (x = 0; x < ThreadCount; x += 2) {
readWriteLock.lockForWrite();
cond.wakeOne();
cond.wakeOne();
QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
QVERIFY(!rwthread[x + 1].dummy.wait(&readWriteLock, 1));
readWriteLock.unlock();
int exited = 0;
for (int y = 0; y < ThreadCount; ++y) {
if (thread_exited[y])
continue;
if (rwthread[y].wait(exited > 0 ? waitInterval : firstWaitInterval)) {
thread_exited[y] = true;
++exited;
}
}
QCOMPARE(exited, 2);
QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 2));
}
QCOMPARE(count.loadRelaxed(), 0);
}
}
void tst_QWaitCondition::wakeAll()
{
int x;
QAtomicInt count;
for (int i = 0; i < iterations; ++i) {
QMutex mutex;
QWaitCondition cond;
// QMutex
wake_Thread thread[ThreadCount];
QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex_")
+ QString::number(i) + QLatin1Char('_');
mutex.lock();
for (x = 0; x < ThreadCount; ++x) {
thread[x].setObjectName(prefix + QString::number(x));
thread[x].count = &count;
thread[x].mutex = &mutex;
thread[x].cond = &cond;
thread[x].start();
// wait for thread to start
QVERIFY(thread[x].started.wait(&mutex, 1000));
}
mutex.unlock();
QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up all threads at once
mutex.lock();
cond.wakeAll();
QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
mutex.unlock();
int exited = 0;
for (x = 0; x < ThreadCount; ++x) {
if (thread[x].wait(1000))
++exited;
}
QCOMPARE(exited, ThreadCount);
QCOMPARE(count.loadRelaxed(), 0);
// QReadWriteLock
QReadWriteLock readWriteLock;
wake_Thread_2 rwthread[ThreadCount];
prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_readwritelock_")
+ QString::number(i) + QLatin1Char('_');
readWriteLock.lockForWrite();
for (x = 0; x < ThreadCount; ++x) {
rwthread[x].setObjectName(prefix + QString::number(x));
rwthread[x].count = &count;
rwthread[x].readWriteLock = &readWriteLock;
rwthread[x].cond = &cond;
rwthread[x].start();
// wait for thread to start
QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
}
readWriteLock.unlock();
QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up all threads at once
readWriteLock.lockForWrite();
cond.wakeAll();
QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
readWriteLock.unlock();
exited = 0;
for (x = 0; x < ThreadCount; ++x) {
if (rwthread[x].wait(1000))
++exited;
}
QCOMPARE(exited, ThreadCount);
QCOMPARE(count.loadRelaxed(), 0);
}
}
class wait_RaceConditionThread : public TerminatingThread
{
public:
wait_RaceConditionThread(QMutex *mutex, QWaitCondition *startup, QWaitCondition *waitCondition,
ulong timeout = ULONG_MAX)
: timeout(timeout), returnValue(false), ready(false),
mutex(mutex), startup(startup), waitCondition(waitCondition) {}
unsigned long timeout;
bool returnValue;
bool ready;
QMutex *mutex;
QWaitCondition *startup;
QWaitCondition *waitCondition;
void run() override
{
mutex->lock();
ready = true;
startup->wakeOne();
returnValue = waitCondition->wait(mutex, timeout);
mutex->unlock();
}
};
class wait_RaceConditionThread_2 : public TerminatingThread
{
public:
wait_RaceConditionThread_2(QReadWriteLock *readWriteLock,
QWaitCondition *startup,
QWaitCondition *waitCondition,
ulong timeout = ULONG_MAX)
: timeout(timeout), returnValue(false), ready(false),
readWriteLock(readWriteLock), startup(startup), waitCondition(waitCondition)
{ }
unsigned long timeout;
bool returnValue;
bool ready;
QReadWriteLock *readWriteLock;
QWaitCondition *startup;
QWaitCondition *waitCondition;
void run() override
{
readWriteLock->lockForWrite();
ready = true;
startup->wakeOne();
returnValue = waitCondition->wait(readWriteLock, timeout);
readWriteLock->unlock();
}
};
void tst_QWaitCondition::wait_RaceCondition()
{
{
QMutex mutex;
QWaitCondition startup;
QWaitCondition waitCondition;
wait_RaceConditionThread timeoutThread(&mutex, &startup, &waitCondition, 1000),
waitingThread1(&mutex, &startup, &waitCondition);
timeoutThread.start();
waitingThread1.start();
mutex.lock();
// wait for the threads to start up
while (!timeoutThread.ready
|| !waitingThread1.ready) {
startup.wait(&mutex);
}
QTest::qWait(2000);
waitCondition.wakeOne();
mutex.unlock();
QVERIFY(timeoutThread.wait(5000));
QVERIFY(!timeoutThread.returnValue);
QVERIFY(waitingThread1.wait(5000));
QVERIFY(waitingThread1.returnValue);
}
{
QReadWriteLock readWriteLock;
QWaitCondition startup;
QWaitCondition waitCondition;
wait_RaceConditionThread_2 timeoutThread(&readWriteLock, &startup, &waitCondition, 1000),
waitingThread1(&readWriteLock, &startup, &waitCondition);
timeoutThread.start();
waitingThread1.start();
readWriteLock.lockForRead();
// wait for the threads to start up
while (!timeoutThread.ready
|| !waitingThread1.ready) {
startup.wait(&readWriteLock);
}
QTest::qWait(2000);
waitCondition.wakeOne();
readWriteLock.unlock();
QVERIFY(timeoutThread.wait(5000));
QVERIFY(!timeoutThread.returnValue);
QVERIFY(waitingThread1.wait(5000));
QVERIFY(waitingThread1.returnValue);
}
}
QTEST_MAIN(tst_QWaitCondition)
#include "tst_qwaitcondition.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qwritelocker Test:
#####################################################################
qt_internal_add_test(tst_qwritelocker
SOURCES
tst_qwritelocker.cpp
)

View File

@ -0,0 +1,178 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QCoreApplication>
#include <QWriteLocker>
#include <QSemaphore>
#include <QThread>
class tst_QWriteLockerThread : public QThread
{
public:
QReadWriteLock lock;
QSemaphore semaphore, testSemaphore;
void waitForTest()
{
semaphore.release();
testSemaphore.acquire();
}
};
class tst_QWriteLocker : public QObject
{
Q_OBJECT
public:
tst_QWriteLockerThread *thread;
void waitForThread()
{
thread->semaphore.acquire();
}
void releaseThread()
{
thread->testSemaphore.release();
}
private slots:
void scopeTest();
void unlockAndRelockTest();
void lockerStateTest();
};
void tst_QWriteLocker::scopeTest()
{
class ScopeTestThread : public tst_QWriteLockerThread
{
public:
void run() override
{
waitForTest();
{
QWriteLocker locker(&lock);
waitForTest();
}
waitForTest();
}
};
thread = new ScopeTestThread;
thread->start();
waitForThread();
// lock should be unlocked before entering the scope that creates the QWriteLocker
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
waitForThread();
// lock should be locked by the QWriteLocker
QVERIFY(!thread->lock.tryLockForWrite());
releaseThread();
waitForThread();
// lock should be unlocked when the QWriteLocker goes out of scope
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
void tst_QWriteLocker::unlockAndRelockTest()
{
class UnlockAndRelockThread : public tst_QWriteLockerThread
{
public:
void run() override
{
QWriteLocker locker(&lock);
waitForTest();
locker.unlock();
waitForTest();
locker.relock();
waitForTest();
}
};
thread = new UnlockAndRelockThread;
thread->start();
waitForThread();
// lock should be locked by the QWriteLocker
QVERIFY(!thread->lock.tryLockForWrite());
releaseThread();
waitForThread();
// lock has been explicitly unlocked via QWriteLocker
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
waitForThread();
// lock has been explicitly relocked via QWriteLocker
QVERIFY(!thread->lock.tryLockForWrite());
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
void tst_QWriteLocker::lockerStateTest()
{
class LockerStateThread : public tst_QWriteLockerThread
{
public:
void run() override
{
{
QWriteLocker locker(&lock);
locker.relock();
locker.unlock();
waitForTest();
}
waitForTest();
}
};
thread = new LockerStateThread;
thread->start();
waitForThread();
// even though we relock() after creating the QWriteLocker, it shouldn't lock the lock more than once
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
waitForThread();
// if we call QWriteLocker::unlock(), its destructor should do nothing
QVERIFY(thread->lock.tryLockForWrite());
thread->lock.unlock();
releaseThread();
QVERIFY(thread->wait());
delete thread;
thread = nullptr;
}
QTEST_MAIN(tst_QWriteLocker)
#include "tst_qwritelocker.moc"