mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-05 16:55:25 +08:00
qt 6.5.1 original
This commit is contained in:
37
tests/auto/corelib/thread/CMakeLists.txt
Normal file
37
tests/auto/corelib/thread/CMakeLists.txt
Normal 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()
|
11
tests/auto/corelib/thread/qatomicint/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qatomicint/CMakeLists.txt
Normal 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
|
||||
)
|
875
tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
Normal file
875
tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
Normal 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"
|
19
tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt
Normal file
19
tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt
Normal 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)
|
14
tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
Normal file
14
tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
14
tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
Normal file
14
tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
Normal 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
|
||||
)
|
14
tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
Normal file
14
tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
766
tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
Normal file
766
tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
Normal 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)
|
||||
|
@ -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
|
||||
)
|
14
tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
Normal file
14
tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
11
tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
Normal 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
|
||||
)
|
680
tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
Normal file
680
tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
Normal 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"
|
20
tests/auto/corelib/thread/qfuture/CMakeLists.txt
Normal file
20
tests/auto/corelib/thread/qfuture/CMakeLists.txt
Normal 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)
|
5046
tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
Normal file
5046
tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
Normal file
File diff suppressed because it is too large
Load Diff
11
tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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"
|
14
tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt
Normal file
14
tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt
Normal 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
|
||||
)
|
1222
tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
Normal file
1222
tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
Normal file
File diff suppressed because it is too large
Load Diff
13
tests/auto/corelib/thread/qmutex/CMakeLists.txt
Normal file
13
tests/auto/corelib/thread/qmutex/CMakeLists.txt
Normal 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
|
||||
)
|
1292
tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
Normal file
1292
tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
Normal file
File diff suppressed because it is too large
Load Diff
11
tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt
Normal 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
|
||||
)
|
257
tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
Normal file
257
tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
Normal 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"
|
13
tests/auto/corelib/thread/qpromise/CMakeLists.txt
Normal file
13
tests/auto/corelib/thread/qpromise/CMakeLists.txt
Normal 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
|
||||
)
|
158
tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
Normal file
158
tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
Normal 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
|
||||
}
|
667
tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
Normal file
667
tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
Normal 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"
|
11
tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
Normal 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
|
||||
)
|
178
tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
Normal file
178
tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
Normal 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"
|
14
tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt
Normal file
14
tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt
Normal 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
|
||||
)
|
1075
tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
Normal file
1075
tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
Normal file
File diff suppressed because it is too large
Load Diff
13
tests/auto/corelib/thread/qresultstore/CMakeLists.txt
Normal file
13
tests/auto/corelib/thread/qresultstore/CMakeLists.txt
Normal 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
|
||||
)
|
589
tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
Normal file
589
tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
Normal 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"
|
2
tests/auto/corelib/thread/qsemaphore/BLACKLIST
Normal file
2
tests/auto/corelib/thread/qsemaphore/BLACKLIST
Normal file
@ -0,0 +1,2 @@
|
||||
[tryAcquireWithTimeout]
|
||||
macos
|
11
tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
Normal 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
|
||||
)
|
612
tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
Normal file
612
tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
Normal 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"
|
5
tests/auto/corelib/thread/qthread/BLACKLIST
Normal file
5
tests/auto/corelib/thread/qthread/BLACKLIST
Normal file
@ -0,0 +1,5 @@
|
||||
[wait3_slowDestructor]
|
||||
windows-10
|
||||
[sleep]
|
||||
windows-7sp1
|
||||
|
16
tests/auto/corelib/thread/qthread/CMakeLists.txt
Normal file
16
tests/auto/corelib/thread/qthread/CMakeLists.txt
Normal 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:
|
||||
#####################################################################
|
1848
tests/auto/corelib/thread/qthread/tst_qthread.cpp
Normal file
1848
tests/auto/corelib/thread/qthread/tst_qthread.cpp
Normal file
File diff suppressed because it is too large
Load Diff
14
tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
Normal file
14
tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
Normal 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
|
||||
)
|
80
tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
Normal file
80
tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
Normal 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;
|
||||
}
|
67
tests/auto/corelib/thread/qthreadonce/qthreadonce.h
Normal file
67
tests/auto/corelib/thread/qthreadonce/qthreadonce.h
Normal 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
|
203
tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
Normal file
203
tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
Normal 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"
|
11
tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
Normal 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
|
||||
)
|
1448
tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
Normal file
1448
tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
Normal file
File diff suppressed because it is too large
Load Diff
21
tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
Normal file
21
tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
Normal 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()
|
@ -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
|
||||
)
|
@ -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);
|
||||
}
|
475
tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
Normal file
475
tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
Normal 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"
|
11
tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
Normal 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
|
||||
)
|
853
tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
Normal file
853
tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
Normal 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"
|
11
tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
Normal file
11
tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
Normal 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
|
||||
)
|
178
tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
Normal file
178
tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
Normal 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"
|
Reference in New Issue
Block a user