summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/thread
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/thread')
-rw-r--r--tests/auto/corelib/thread/.prev_CMakeLists.txt25
-rw-r--r--tests/auto/corelib/thread/CMakeLists.txt39
-rw-r--r--tests/auto/corelib/thread/qatomicint/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicint/qatomicint.pro4
-rw-r--r--tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp62
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt3
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char/char.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char16_t/char16_t.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char32_t/char32_t.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/int/int.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/long/long.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pri11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pro18
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qlonglong/qlonglong.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qptrdiff/qptrdiff.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/quintptr/quintptr.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qulonglong/qulonglong.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/schar/schar.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/short/short.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp197
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uchar/uchar.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uint/uint.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ulong/ulong.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ushort/ushort.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/wchar_t/wchar_t.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro4
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp49
-rw-r--r--tests/auto/corelib/thread/qfuture/.prev_CMakeLists.txt14
-rw-r--r--tests/auto/corelib/thread/qfuture/CMakeLists.txt23
-rw-r--r--tests/auto/corelib/thread/qfuture/qfuture.pro5
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp2566
-rw-r--r--tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qfuturesynchronizer/qfuturesynchronizer.pro4
-rw-r--r--tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp69
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt13
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/qfuturewatcher.pro4
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp289
-rw-r--r--tests/auto/corelib/thread/qmutex/CMakeLists.txt19
-rw-r--r--tests/auto/corelib/thread/qmutex/qmutex.pro5
-rw-r--r--tests/auto/corelib/thread/qmutex/tst_qmutex.cpp280
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro4
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp119
-rw-r--r--tests/auto/corelib/thread/qpromise/.prev_CMakeLists.txt12
-rw-r--r--tests/auto/corelib/thread/qpromise/CMakeLists.txt13
-rw-r--r--tests/auto/corelib/thread/qpromise/qpromise.pro4
-rw-r--r--tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp77
-rw-r--r--tests/auto/corelib/thread/qpromise/tst_qpromise.cpp351
-rw-r--r--tests/auto/corelib/thread/qreadlocker/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qreadlocker/qreadlocker.pro4
-rw-r--r--tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp39
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt14
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro4
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp287
-rw-r--r--tests/auto/corelib/thread/qresultstore/CMakeLists.txt14
-rw-r--r--tests/auto/corelib/thread/qresultstore/qresultstore.pro4
-rw-r--r--tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp241
-rw-r--r--tests/auto/corelib/thread/qsemaphore/BLACKLIST6
-rw-r--r--tests/auto/corelib/thread/qsemaphore/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qsemaphore/qsemaphore.pro4
-rw-r--r--tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp107
-rw-r--r--tests/auto/corelib/thread/qthread/.prev_CMakeLists.txt16
-rw-r--r--tests/auto/corelib/thread/qthread/BLACKLIST2
-rw-r--r--tests/auto/corelib/thread/qthread/CMakeLists.txt16
-rw-r--r--tests/auto/corelib/thread/qthread/qthread.pro9
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp530
-rw-r--r--tests/auto/corelib/thread/qthreadonce/CMakeLists.txt13
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp29
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.h29
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.pro12
-rw-r--r--tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp49
-rw-r--r--tests/auto/corelib/thread/qthreadpool/BLACKLIST4
-rw-r--r--tests/auto/corelib/thread/qthreadpool/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qthreadpool/qthreadpool.pro4
-rw-r--r--tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp518
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt15
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashonexit/.prev_CMakeLists.txt15
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt12
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp29
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro16
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro8
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/test/test.pro16
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp64
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro4
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp46
-rw-r--r--tests/auto/corelib/thread/qwritelocker/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qwritelocker/qwritelocker.pro4
-rw-r--r--tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp39
-rw-r--r--tests/auto/corelib/thread/thread.pro28
107 files changed, 4713 insertions, 2112 deletions
diff --git a/tests/auto/corelib/thread/.prev_CMakeLists.txt b/tests/auto/corelib/thread/.prev_CMakeLists.txt
deleted file mode 100644
index b73d9af059..0000000000
--- a/tests/auto/corelib/thread/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated from thread.pro.
-
-if(QT_FEATURE_thread)
- add_subdirectory(qatomicint)
- add_subdirectory(qatomicinteger)
- add_subdirectory(qatomicpointer)
- add_subdirectory(qresultstore)
- add_subdirectory(qfuture)
- add_subdirectory(qfuturesynchronizer)
- add_subdirectory(qmutex)
- add_subdirectory(qmutexlocker)
- add_subdirectory(qreadlocker)
- add_subdirectory(qreadwritelock)
- add_subdirectory(qsemaphore)
- add_subdirectory(qthread)
- add_subdirectory(qthreadonce)
- add_subdirectory(qthreadpool)
- add_subdirectory(qthreadstorage)
- add_subdirectory(qwaitcondition)
- add_subdirectory(qwritelocker)
- add_subdirectory(qpromise)
-endif()
-if(TARGET Qt::Concurrent)
- add_subdirectory(qfuturewatcher)
-endif()
diff --git a/tests/auto/corelib/thread/CMakeLists.txt b/tests/auto/corelib/thread/CMakeLists.txt
index 8c3e33d6dc..d25d0205f5 100644
--- a/tests/auto/corelib/thread/CMakeLists.txt
+++ b/tests/auto/corelib/thread/CMakeLists.txt
@@ -1,30 +1,49 @@
-# Generated from thread.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-if(QT_FEATURE_thread)
+if(QT_BUILD_WASM_BATCHED_TESTS) # not all tests currently work in WebAssembly
add_subdirectory(qatomicint)
add_subdirectory(qatomicinteger)
add_subdirectory(qatomicpointer)
- add_subdirectory(qresultstore)
- add_subdirectory(qfuture)
add_subdirectory(qfuturesynchronizer)
+ add_subdirectory(qmutexlocker)
+ add_subdirectory(qreadlocker)
+ add_subdirectory(qresultstore)
+ add_subdirectory(qwritelocker)
+ return()
+endif()
+
+if(QT_FEATURE_thread)
+ add_subdirectory(qatomicint)
+ add_subdirectory(qatomicinteger)
+ add_subdirectory(qatomicpointer)
+ if(QT_FEATURE_future)
+ if(QT_FEATURE_concurrent AND NOT INTEGRITY)
+ add_subdirectory(qfuture)
+ endif()
+ add_subdirectory(qresultstore)
+ add_subdirectory(qfuturesynchronizer)
+ if(NOT INTEGRITY)
+ add_subdirectory(qpromise)
+ endif()
+ # QTBUG-87431
+ if(TARGET Qt::Concurrent AND NOT INTEGRITY)
+ add_subdirectory(qfuturewatcher)
+ endif()
+ endif()
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)
- add_subdirectory(qpromise)
-endif()
-if(TARGET Qt::Concurrent)
- add_subdirectory(qfuturewatcher)
endif()
+
diff --git a/tests/auto/corelib/thread/qatomicint/CMakeLists.txt b/tests/auto/corelib/thread/qatomicint/CMakeLists.txt
index d3d30f5928..239f3cce87 100644
--- a/tests/auto/corelib/thread/qatomicint/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicint/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qatomicint.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicint Test:
#####################################################################
-qt_add_test(tst_qatomicint
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicint LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicint
SOURCES
tst_qatomicint.cpp
)
diff --git a/tests/auto/corelib/thread/qatomicint/qatomicint.pro b/tests/auto/corelib/thread/qatomicint/qatomicint.pro
deleted file mode 100644
index 89ac465e81..0000000000
--- a/tests/auto/corelib/thread/qatomicint/qatomicint.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qatomicint
-QT = core testlib
-SOURCES = tst_qatomicint.cpp
diff --git a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
index ef00be31c5..63cb494c11 100644
--- a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
+++ b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
@@ -1,33 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QAtomicInt>
#include <QCoreApplication>
@@ -85,7 +60,7 @@ private:
};
template <int I>
-static inline void assemblyMarker(void *ptr = 0)
+static inline void assemblyMarker(void *ptr = nullptr)
{
puts((char *)ptr + I);
}
@@ -154,17 +129,11 @@ template <bool> inline void booleanHelper()
template <typename Atomic>
static void constexprFunctionsHelperTemplate()
{
-#ifdef Q_COMPILER_CONSTEXPR
// this is a compile-time test only
- booleanHelper<Atomic::isReferenceCountingNative()>();
booleanHelper<Atomic::isReferenceCountingWaitFree()>();
- booleanHelper<Atomic::isTestAndSetNative()>();
booleanHelper<Atomic::isTestAndSetWaitFree()>();
- booleanHelper<Atomic::isFetchAndStoreNative()>();
booleanHelper<Atomic::isFetchAndStoreWaitFree()>();
- booleanHelper<Atomic::isFetchAndAddNative()>();
booleanHelper<Atomic::isFetchAndAddWaitFree()>();
-#endif
}
void tst_QAtomicInt::warningFreeHelper()
@@ -192,23 +161,19 @@ void tst_QAtomicInt::warningFreeHelper()
constexprFunctionsHelperTemplate<QBasicAtomicInteger<long int> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned long int> >();
-#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
warningFreeHelperTemplate<qint16, QBasicAtomicInteger<qint16> >();
warningFreeHelperTemplate<quint16, QBasicAtomicInteger<quint16> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<qint16> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<quint16> >();
warningFreeHelperTemplate<qint16, QBasicAtomicInteger<char16_t> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<char16_t> >();
-#endif
-#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
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> >();
-#endif
#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
#if !defined(__i386__) || (defined(Q_CC_GNU) && defined(__OPTIMIZE__))
@@ -236,19 +201,12 @@ void tst_QAtomicInt::alignment()
static_assert(alignof(QBasicAtomicInt) == alignof(TypeInStruct<int>));
static_assert(alignof(QBasicAtomicInt) == alignof(TypeInStruct<int>));
-#ifdef Q_ATOMIC_INT32_IS_SUPPORTED
QCOMPARE(alignof(QBasicAtomicInteger<int>), alignof(TypeInStruct<int>));
-#endif
-
-#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
QCOMPARE(alignof(QBasicAtomicInteger<short>), alignof(TypeInStruct<short>));
-#endif
-
-#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
QCOMPARE(alignof(QBasicAtomicInteger<char>), alignof(TypeInStruct<char>));
-#endif
-#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
+#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
}
@@ -523,7 +481,6 @@ void tst_QAtomicInt::testAndSet()
QTEST(atomic.testAndSetOrdered(expected, newval), "result");
}
-#ifdef Q_ATOMIC_INT32_IS_SUPPORTED
QFETCH(bool, result);
// the new implementation has the version that loads the current value
@@ -558,7 +515,6 @@ void tst_QAtomicInt::testAndSet()
if (!result)
QCOMPARE(currentval, value);
}
-#endif
}
void tst_QAtomicInt::isFetchAndStoreNative()
@@ -720,7 +676,6 @@ void tst_QAtomicInt::fetchAndAdd_data()
QTest::newRow("7272+2181") << 7272 << 2181;
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;
@@ -736,7 +691,6 @@ void tst_QAtomicInt::fetchAndAdd_data()
QTest::newRow("5451+-4362") << 5451 << -4362;
QTest::newRow("7272+-2181") << 7272 << -2181;
- QTest::newRow("0+1") << 0 << 1;
QTest::newRow("-1+0") << -1 << 0;
QTest::newRow("-1+2") << -1 << 2;
QTest::newRow("-2+1") << -2 << 1;
diff --git a/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt
index 8f0637a4d2..03a6323a1f 100644
--- a/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from qatomicinteger.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(char)
add_subdirectory(char16_t)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
index 527302cdd4..882a9298f6 100644
--- a/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from char.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_char
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_char LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_char
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/char/char.pro b/tests/auto/corelib/thread/qatomicinteger/char/char.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/char/char.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt
index 6fb1c8e975..8e53b59689 100644
--- a/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from char16_t.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char16_t Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_char16_t
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_char16_t LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_char16_t
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/char16_t/char16_t.pro b/tests/auto/corelib/thread/qatomicinteger/char16_t/char16_t.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/char16_t/char16_t.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt
index 9a42a2ef09..5881d475f4 100644
--- a/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from char32_t.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char32_t Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_char32_t
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_char32_t LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_char32_t
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/char32_t/char32_t.pro b/tests/auto/corelib/thread/qatomicinteger/char32_t/char32_t.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/char32_t/char32_t.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
index 32a529ea60..0915e77a8d 100644
--- a/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from int.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_int Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_int
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_int LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_int
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/int/int.pro b/tests/auto/corelib/thread/qatomicinteger/int/int.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/int/int.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
index 974cb2bb9d..adf6638bfa 100644
--- a/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from long.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_long Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_long
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_long LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_long
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/long/long.pro b/tests/auto/corelib/thread/qatomicinteger/long/long.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/long/long.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pri b/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pri
deleted file mode 100644
index f1030d41ef..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pri
+++ /dev/null
@@ -1,11 +0,0 @@
-# Get our build type from the directory name
-TYPE = $$basename(_PRO_FILE_PWD_)
-dn = $$dirname(_PRO_FILE_PWD_)
-FORCE = $$basename(dn)
-suffix = $$TYPE
-
-CONFIG += testcase
-QT = core testlib
-TARGET = tst_qatomicinteger_$$lower($$suffix)
-SOURCES = $$PWD/tst_qatomicinteger.cpp
-DEFINES += QATOMIC_TEST_TYPE=$$TYPE tst_QAtomicIntegerXX=tst_QAtomicInteger_$$suffix
diff --git a/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pro b/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pro
deleted file mode 100644
index 09458bd9c3..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-TEMPLATE=subdirs
-SUBDIRS=\
- char \
- char16_t \
- char32_t \
- int \
- long \
- qlonglong \
- qptrdiff \
- quintptr \
- qulonglong \
- schar \
- short \
- uchar \
- uint \
- ulong \
- ushort \
- wchar_t \
diff --git a/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt
index bbee7fcb85..2ec977d7cb 100644
--- a/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qlonglong.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qlonglong Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_qlonglong
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_qlonglong LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_qlonglong
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/qlonglong/qlonglong.pro b/tests/auto/corelib/thread/qatomicinteger/qlonglong/qlonglong.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qlonglong/qlonglong.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt
index 9dbff18228..a2450931d5 100644
--- a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qptrdiff.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qptrdiff Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_qptrdiff
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_qptrdiff LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_qptrdiff
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/qptrdiff.pro b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/qptrdiff.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/qptrdiff.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt
index f5ef36f407..98302b5d07 100644
--- a/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from quintptr.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_quintptr Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_quintptr
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_quintptr LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_quintptr
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/quintptr/quintptr.pro b/tests/auto/corelib/thread/qatomicinteger/quintptr/quintptr.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/quintptr/quintptr.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt
index 5a783b941e..13acfc3e2b 100644
--- a/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qulonglong.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qulonglong Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_qulonglong
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_qulonglong LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_qulonglong
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/qulonglong/qulonglong.pro b/tests/auto/corelib/thread/qatomicinteger/qulonglong/qulonglong.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qulonglong/qulonglong.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt
index 869473d0c9..127f752cc2 100644
--- a/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from schar.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_schar Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_schar
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_schar LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_schar
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/schar/schar.pro b/tests/auto/corelib/thread/qatomicinteger/schar/schar.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/schar/schar.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt
index a90621584e..df9d2af4c3 100644
--- a/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from short.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_short Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_short
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_short LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_short
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/short/short.pro b/tests/auto/corelib/thread/qatomicinteger/short/short.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/short/short.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
index a56a3f645f..d1a8a8f729 100644
--- a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
+++ b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
@@ -1,62 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include <QtTest>
+#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
@@ -65,31 +22,21 @@
#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
-#if (defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__-0) > 2) \
- || (defined(WCHAR_MAX) && (WCHAR_MAX-0 > 0x10000))
-# define TYPE_SUPPORTED_wchar_t 1
-#endif
-#define TYPE_SUPPORTED_char32_t 1
+#define TYPE_SUPPORTED_char32_t 1
-#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
-# define TYPE_SUPPORTED_char 1
-# define TYPE_SUPPORTED_uchar 1
-# define TYPE_SUPPORTED_schar 1
-#endif
-#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
-# define TYPE_SUPPORTED_short 1
-# define TYPE_SUPPORTED_ushort 1
-# define TYPE_SUPPORTED_char16_t 1
-# ifndef TYPE_SUPPORTED_wchar_t
-# define TYPE_SUPPORTED_wchar_t 1
-# endif
-#endif
#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
# define TYPE_SUPPORTED_qlonglong 1
# define TYPE_SUPPORTED_qulonglong 1
@@ -117,6 +64,7 @@ typedef signed char schar;
typedef TEST_TYPE Type;
typedef Type T; // shorthand
+using U = std::make_unsigned_t<T>;
enum {
TypeIsUnsigned = Type(-1) > Type(0),
TypeIsSigned = !TypeIsUnsigned
@@ -127,6 +75,8 @@ template <> struct LargeIntTemplate<true> { typedef quint64 Type; };
template <> struct LargeIntTemplate<false> { typedef qint64 Type; };
typedef LargeIntTemplate<TypeIsUnsigned>::Type LargeInt;
+namespace {
+
class tst_QAtomicIntegerXX : public QObject
{
Q_OBJECT
@@ -196,17 +146,11 @@ void tst_QAtomicIntegerXX::static_checks()
(void) QAtomicInteger<T>::isFetchAndAddNative();
(void) QAtomicInteger<T>::isFetchAndAddWaitFree();
-#ifdef Q_COMPILER_CONSTEXPR
// this is a compile-time test only
- booleanHelper<QAtomicInteger<T>::isReferenceCountingNative()>();
booleanHelper<QAtomicInteger<T>::isReferenceCountingWaitFree()>();
- booleanHelper<QAtomicInteger<T>::isTestAndSetNative()>();
booleanHelper<QAtomicInteger<T>::isTestAndSetWaitFree()>();
- booleanHelper<QAtomicInteger<T>::isFetchAndStoreNative()>();
booleanHelper<QAtomicInteger<T>::isFetchAndStoreWaitFree()>();
- booleanHelper<QAtomicInteger<T>::isFetchAndAddNative()>();
booleanHelper<QAtomicInteger<T>::isFetchAndAddWaitFree()>();
-#endif
}
void tst_QAtomicIntegerXX::addData()
@@ -348,46 +292,32 @@ void tst_QAtomicIntegerXX::loadAcquireStoreRelease()
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;
+
+ // We perform arithmetic using the unsigned type U to avoid signed
+ // integer overflows in the non-atomic portion (atomics have well-defined,
+ // two's complement overflow, even signed ones).
+ T nextValue = T(U(value) + 1);
+ T prevValue = T(U(value) - 1);
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));
}
@@ -490,80 +420,55 @@ void tst_QAtomicIntegerXX::fetchAndAdd()
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
+ // We perform the additions using the unsigned type U to avoid signed
+ // integer overflows in the non-atomic portion (atomics have well-defined,
+ // two's complement overflow, even signed ones).
T parcel1 = 42;
T parcel2 = T(0-parcel1);
+ T newValue1 = T(U(value) + parcel1);
+ T newValue2 = T(U(value) + parcel2);
- 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()
@@ -571,80 +476,55 @@ void tst_QAtomicIntegerXX::fetchAndSub()
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
+ // We perform the subtractions using the unsigned type U to avoid signed
+ // integer underrflows in the non-atomic portion (atomics have well-defined,
+ // two's complement underflow, even signed ones).
T parcel1 = 42;
T parcel2 = T(0-parcel1);
+ T newValue1 = T(U(value) - parcel1);
+ T newValue2 = T(U(value) - parcel2);
- 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()
@@ -794,8 +674,9 @@ void tst_QAtomicIntegerXX::fetchAndXor()
QCOMPARE(atomic ^= minusOne, T(~value));
QCOMPARE(atomic ^= minusOne, T(value));
}
-
-#include "tst_qatomicinteger.moc"
+}
QTEST_APPLESS_MAIN(tst_QAtomicIntegerXX)
+#include "tst_qatomicinteger.moc"
+
diff --git a/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt
index a2ee999970..95d88d31a6 100644
--- a/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from uchar.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_uchar Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_uchar
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_uchar LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_uchar
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/uchar/uchar.pro b/tests/auto/corelib/thread/qatomicinteger/uchar/uchar.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/uchar/uchar.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
index f1eec6140f..2ab977ef6a 100644
--- a/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from uint.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_uint Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_uint
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_uint LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_uint
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/uint/uint.pro b/tests/auto/corelib/thread/qatomicinteger/uint/uint.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/uint/uint.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt
index eba2944e44..7707bd53b5 100644
--- a/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from ulong.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_ulong Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_ulong
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_ulong LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_ulong
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/ulong/ulong.pro b/tests/auto/corelib/thread/qatomicinteger/ulong/ulong.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/ulong/ulong.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt
index fe19fc42fc..667e9eade6 100644
--- a/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from ushort.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_ushort Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_ushort
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_ushort LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_ushort
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/ushort/ushort.pro b/tests/auto/corelib/thread/qatomicinteger/ushort/ushort.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/ushort/ushort.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt
index dfc4e6ee37..0e2d084b58 100644
--- a/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from wchar_t.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_wchar_t Test:
#####################################################################
-qt_add_test(tst_qatomicinteger_wchar_t
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicinteger_wchar_t LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicinteger_wchar_t
SOURCES
../tst_qatomicinteger.cpp
DEFINES
diff --git a/tests/auto/corelib/thread/qatomicinteger/wchar_t/wchar_t.pro b/tests/auto/corelib/thread/qatomicinteger/wchar_t/wchar_t.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/wchar_t/wchar_t.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt b/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
index 09c8c448df..cd8df9db66 100644
--- a/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qatomicpointer.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicpointer Test:
#####################################################################
-qt_add_test(tst_qatomicpointer
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qatomicpointer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qatomicpointer
SOURCES
tst_qatomicpointer.cpp
)
diff --git a/tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro b/tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro
deleted file mode 100644
index cce822da6e..0000000000
--- a/tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qatomicpointer
-QT = core testlib
-SOURCES = tst_qatomicpointer.cpp
diff --git a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
index bcc34a7d96..347831819e 100644
--- a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
+++ b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
@@ -1,33 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <qatomic.h>
#include <limits.h>
@@ -73,12 +48,12 @@ 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(0);
+ QBasicAtomicPointer<WFHC> p = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
p.loadRelaxed()->bar();
- WFHC *expectedValue = 0;
- WFHC *newValue = 0;
+ WFHC *expectedValue = nullptr;
+ WFHC *newValue = nullptr;
qptrdiff valueToAdd = 0;
p.testAndSetRelaxed(expectedValue, newValue);
@@ -585,7 +560,7 @@ void tst_QAtomicPointer::fetchAndAdd()
template <typename T> void constAndVolatile_helper()
{
- T *one = 0;
+ T *one = nullptr;
T *two = &one;
T *three = &two;
@@ -645,8 +620,8 @@ void tst_QAtomicPointer::forwardDeclared()
// this is just a compilation test
QAtomicPointer<ForwardDeclared> ptr;
ContainsForwardDeclared cfd;
- Q_UNUSED(ptr);
- Q_UNUSED(cfd);
+ Q_UNUSED(ptr)
+ Q_UNUSED(cfd)
QVERIFY(true);
}
@@ -661,7 +636,7 @@ template <typename T> static void operators_helper()
{
// Test that QBasicAtomicPointer also has operator= and cast operators
// We've been using them for QAtomicPointer<T> elsewhere
- QBasicAtomicPointer<T> atomic = Q_BASIC_ATOMIC_INITIALIZER(0);
+ QBasicAtomicPointer<T> atomic = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
atomic = one;
QCOMPARE(Ptr(atomic), one);
}
diff --git a/tests/auto/corelib/thread/qfuture/.prev_CMakeLists.txt b/tests/auto/corelib/thread/qfuture/.prev_CMakeLists.txt
deleted file mode 100644
index 6908bd678c..0000000000
--- a/tests/auto/corelib/thread/qfuture/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-# Generated from qfuture.pro.
-
-#####################################################################
-## tst_qfuture Test:
-#####################################################################
-
-qt_add_test(tst_qfuture
- SOURCES
- tst_qfuture.cpp
- DEFINES
- -QT_NO_JAVA_STYLE_ITERATORS
- PUBLIC_LIBRARIES
- Qt::CorePrivate
-)
diff --git a/tests/auto/corelib/thread/qfuture/CMakeLists.txt b/tests/auto/corelib/thread/qfuture/CMakeLists.txt
index 6fdfc977e9..ba5730d5cf 100644
--- a/tests/auto/corelib/thread/qfuture/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qfuture/CMakeLists.txt
@@ -1,14 +1,27 @@
-# Generated from qfuture.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfuture Test:
#####################################################################
-qt_add_test(tst_qfuture
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qfuture LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qfuture
SOURCES
tst_qfuture.cpp
-# DEFINES
-# -QT_NO_JAVA_STYLE_ITERATORS
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
+ Qt::TestPrivate
+)
+
+qt_internal_extend_target(tst_qfuture CONDITION MSVC
+ COMPILE_OPTIONS
+ /bigobj
)
+
+qt_internal_undefine_global_definition(tst_qfuture QT_NO_JAVA_STYLE_ITERATORS)
diff --git a/tests/auto/corelib/thread/qfuture/qfuture.pro b/tests/auto/corelib/thread/qfuture/qfuture.pro
deleted file mode 100644
index fe097edf20..0000000000
--- a/tests/auto/corelib/thread/qfuture/qfuture.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qfuture
-QT = core core-private testlib
-SOURCES = tst_qfuture.cpp
-DEFINES -= QT_NO_JAVA_STYLE_ITERATORS
diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
index e6f6827f3f..4cb29c514a 100644
--- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
+++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
@@ -1,36 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <QCoreApplication>
-#include <QDebug>
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#define QFUTURE_TEST
-#include <QtTest/QtTest>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QSemaphore>
+#include <QTestEventLoop>
+#include <QTimer>
+#include <QSignalSpy>
+#include <QVarLengthArray>
+#include <QSet>
+#include <QList>
+#include <private/qobject_p.h>
+
+#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <qfuture.h>
#include <qfuturewatcher.h>
#include <qresultstore.h>
@@ -40,18 +27,21 @@
#include <QtConcurrent/qtconcurrentrun.h>
#include <private/qfutureinterface_p.h>
+#include <forward_list>
+#include <list>
#include <vector>
#include <memory>
+#include <set>
// COM interface macro.
#if defined(Q_OS_WIN) && defined(interface)
# undef interface
#endif
-struct ResultStoreInt : QtPrivate::ResultStoreBase
-{
- ~ResultStoreInt() { clear<int>(); }
-};
+using namespace std::chrono_literals;
+static constexpr auto DefaultWaitTime = 2s;
+
+using namespace Qt::StringLiterals;
class SenderObject : public QObject
{
@@ -65,12 +55,75 @@ public:
{
emit multipleArgs(value1, value2, value3);
}
+ void emitTupleArgSignal(const std::tuple<int, double, QString> &t) { emit tupleArgSignal(t); }
+ void emitMultiArgsWithTupleSignal1(int value, const std::tuple<int, double, QString> &t)
+ {
+ emit multiArgsWithTupleSignal1(value, t);
+ }
+ void emitMultiArgsWithTupleSignal2(const std::tuple<int, double, QString> &t, int value)
+ {
+ emit multiArgsWithTupleSignal2(t, value);
+ }
+ void emitMultiArgsWithPairSignal1(int value, const std::pair<int, double> &p)
+ {
+ emit multiArgsWithPairSignal1(value, p);
+ }
+ void emitMultiArgsWithPairSignal2(const std::pair<int, double> &p, int value)
+ {
+ emit multiArgsWithPairSignal2(p, value);
+ }
+
+ void emitNoArgPrivateSignal() { emit noArgPrivateSignal(QPrivateSignal()); }
+ void emitIntArgPrivateSignal(int value) { emit intArgPrivateSignal(value, QPrivateSignal()); }
+ void emitMultiArgsPrivateSignal(int value1, double value2, const QString &value3)
+ {
+ emit multiArgsPrivateSignal(value1, value2, value3, QPrivateSignal());
+ }
+ void emitTupleArgPrivateSignal(const std::tuple<int, double, QString> &t)
+ {
+ emit tupleArgPrivateSignal(t, QPrivateSignal());
+ }
+ void emitMultiArgsWithTuplePrivateSignal1(int value, const std::tuple<int, double, QString> &t)
+ {
+ emit multiArgsWithTuplePrivateSignal1(value, t, QPrivateSignal());
+ }
+ void emitMultiArgsWithTuplePrivateSignal2(const std::tuple<int, double, QString> &t, int value)
+ {
+ emit multiArgsWithTuplePrivateSignal2(t, value, QPrivateSignal());
+ }
+ void emitMultiArgsWithPairPrivateSignal1(int value, const std::pair<int, double> &p)
+ {
+ emit multiArgsWithPairPrivateSignal1(value, p, QPrivateSignal());
+ }
+ void emitMultiArgsWithPairPrivateSignal2(const std::pair<int, double> &p, int value)
+ {
+ emit multiArgsWithPairPrivateSignal2(p, value, QPrivateSignal());
+ }
signals:
void noArgSignal();
void intArgSignal(int value);
void constRefArg(const QString &value);
void multipleArgs(int value1, double value2, const QString &value3);
+ void tupleArgSignal(const std::tuple<int, double, QString> &t);
+ void multiArgsWithTupleSignal1(int value, const std::tuple<int, double, QString> &t);
+ void multiArgsWithTupleSignal2(const std::tuple<int, double, QString> &t, int value);
+ void multiArgsWithPairSignal1(int value, const std::pair<int, double> &p);
+ void multiArgsWithPairSignal2(const std::pair<int, double> &p, int value);
+
+ // Private signals
+ void noArgPrivateSignal(QPrivateSignal);
+ void intArgPrivateSignal(int value, QPrivateSignal);
+ void multiArgsPrivateSignal(int value1, double value2, const QString &value3, QPrivateSignal);
+ void tupleArgPrivateSignal(const std::tuple<int, double, QString> &t, QPrivateSignal);
+ void multiArgsWithTuplePrivateSignal1(int value, const std::tuple<int, double, QString> &t,
+ QPrivateSignal);
+ void multiArgsWithTuplePrivateSignal2(const std::tuple<int, double, QString> &t, int value,
+ QPrivateSignal);
+ void multiArgsWithPairPrivateSignal1(int value, const std::pair<int, double> &p,
+ QPrivateSignal);
+ void multiArgsWithPairPrivateSignal2(const std::pair<int, double> &p, int value,
+ QPrivateSignal);
};
class LambdaThread : public QThread
@@ -91,25 +144,41 @@ private:
std::function<void ()> m_fn;
};
+// Emulates QWidget behavior by deleting its children early in the destructor
+// instead of leaving it to ~QObject()
+class FakeQWidget : public QObject
+{
+ Q_OBJECT
+public:
+ ~FakeQWidget() override {
+ auto *d = QObjectPrivate::get(this);
+ d->deleteChildren();
+ }
+};
+
using UniquePtr = std::unique_ptr<int>;
class tst_QFuture: public QObject
{
Q_OBJECT
private slots:
+ void compareCompiles();
void resultStore();
void future();
+ void futureToVoid();
void futureInterface();
void refcounting();
void cancel();
+ void cancelAndFinish();
void statePropagation();
void multipleResults();
void indexedResults();
void progress();
+ void setProgressRange();
+ void progressWithRange();
void progressText();
void resultsAfterFinished();
void resultsAsList();
- void implicitConversions();
void iterators();
void iteratorsThread();
#if QT_DEPRECATED_SINCE(6, 0)
@@ -136,7 +205,14 @@ private slots:
void onFailedForMoveOnlyTypes();
#endif
void onCanceled();
+ void cancelContinuations();
+ void continuationsWithContext_data();
+ void continuationsWithContext();
+ void continuationsWithMoveOnlyLambda();
+#if 0
+ // TODO: enable when QFuture::takeResults() is enabled
void takeResults();
+#endif
void takeResult();
void runAndTake();
void resultsReadyAt_data();
@@ -146,6 +222,36 @@ private slots:
void signalConnect();
void waitForFinished();
+ void rejectResultOverwrite_data();
+ void rejectResultOverwrite();
+ void rejectPendingResultOverwrite_data() { rejectResultOverwrite_data(); }
+ void rejectPendingResultOverwrite();
+
+ void createReadyFutures();
+ void continuationsAfterReadyFutures();
+
+ void getFutureInterface();
+ void convertQMetaType();
+
+ void whenAllIterators();
+ void whenAllIteratorsWithCanceled();
+ void whenAllIteratorsWithFailed();
+ void whenAllDifferentTypes();
+ void whenAllDifferentTypesWithCanceled();
+ void whenAllDifferentTypesWithFailed();
+ void whenAnyIterators();
+ void whenAnyIteratorsWithCanceled();
+ void whenAnyIteratorsWithFailed();
+ void whenAnyDifferentTypes();
+ void whenAnyDifferentTypesWithCanceled();
+ void whenAnyDifferentTypesWithFailed();
+
+ void continuationOverride();
+ void continuationsDontLeak();
+ void cancelAfterFinishWithContinuations();
+
+ void unwrap();
+
private:
using size_type = std::vector<int>::size_type;
@@ -159,6 +265,22 @@ private:
static void testTakeResults(QFuture<T> future, size_type resultCount);
};
+class IntResultsCleaner
+{
+public:
+ IntResultsCleaner(QtPrivate::ResultStoreBase &s) : store(s) { }
+ ~IntResultsCleaner() { store.clear<int>(); }
+
+private:
+ QtPrivate::ResultStoreBase &store;
+};
+
+void tst_QFuture::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<QFuture<int>::const_iterator>();
+ QTestPrivate::testEqualityOperatorsCompile<QFuture<QString>::const_iterator>();
+}
+
void tst_QFuture::resultStore()
{
int int0 = 0;
@@ -166,7 +288,9 @@ void tst_QFuture::resultStore()
int int2 = 2;
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
QCOMPARE(store.begin(), store.end());
QCOMPARE(store.resultAt(0), store.end());
QCOMPARE(store.resultAt(1), store.end());
@@ -174,7 +298,9 @@ void tst_QFuture::resultStore()
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResult(1, &int1);
QtPrivate::ResultIteratorBase it = store.begin();
@@ -196,7 +322,9 @@ void tst_QFuture::resultStore()
QList<int> vec1 = QList<int>() << 4 << 5;
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(-1, &vec0, 2);
store.addResults(-1, &vec1, 2);
QtPrivate::ResultIteratorBase it = store.begin();
@@ -219,7 +347,9 @@ void tst_QFuture::resultStore()
QCOMPARE(it, store.end());
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec1, 2);
store.addResult(-1, &int1);
@@ -250,7 +380,9 @@ void tst_QFuture::resultStore()
QCOMPARE(store.resultAt(4), store.end());
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(-1, &int1);
@@ -280,7 +412,9 @@ void tst_QFuture::resultStore()
QCOMPARE(store.resultAt(3).value<int>(), int1);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(200, &int1);
@@ -292,7 +426,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(1, &int1);
store.addResult(0, &int0);
store.addResult(-1, &int2);
@@ -303,7 +439,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
QCOMPARE(store.contains(0), false);
QCOMPARE(store.contains(1), false);
QCOMPARE(store.contains(INT_MAX), false);
@@ -311,7 +449,9 @@ void tst_QFuture::resultStore()
{
// Test filter mode, where "gaps" in the result array aren't allowed.
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -345,7 +485,9 @@ void tst_QFuture::resultStore()
{
// test canceled results
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -382,7 +524,9 @@ void tst_QFuture::resultStore()
{
// test addResult return value
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -428,7 +572,9 @@ void tst_QFuture::resultStore()
{
// test resultCount in non-filtered mode. It should always be possible
// to iterate through the results 0 to resultCount.
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(0, &int0);
QCOMPARE(store.count(), 1);
@@ -442,7 +588,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(2, &int0);
QCOMPARE(store.count(), 0);
@@ -454,7 +602,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -466,7 +616,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -474,7 +626,9 @@ void tst_QFuture::resultStore()
QCOMPARE(store.count(), 4);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -486,7 +640,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -499,7 +655,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -509,7 +667,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -522,7 +682,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(1, &int0);
store.addResult(3, &int0);
store.addResults(6, &vec0);
@@ -537,7 +699,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(1, &int0);
store.addResult(3, &int0);
@@ -565,7 +729,9 @@ void tst_QFuture::resultStore()
QCOMPARE(store.contains(7), false);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addCanceledResult(0);
QCOMPARE(store.contains(0), false);
@@ -601,6 +767,19 @@ void tst_QFuture::future()
QCOMPARE(intFuture2.isFinished(), true);
}
+void tst_QFuture::futureToVoid()
+{
+ QPromise<int> p;
+ QFuture<int> future = p.future();
+
+ p.start();
+ p.setProgressValue(42);
+ p.finish();
+
+ QFuture<void> voidFuture = QFuture<void>(future);
+ QCOMPARE(voidFuture.progressValue(), 42);
+}
+
class IntResult : public QFutureInterface<int>
{
public:
@@ -645,7 +824,7 @@ void tst_QFuture::futureInterface()
{
QFutureInterface<int> i;
i.reportStarted();
- i.reportResult(10);
+ QVERIFY(i.reportResult(10));
future = i.future();
i.reportFinished();
}
@@ -666,7 +845,7 @@ void tst_QFuture::futureInterface()
QCOMPARE(intFuture.isStarted(), true);
QCOMPARE(intFuture.isFinished(), false);
- result.reportFinished(&value);
+ QVERIFY(result.reportFinished(&value));
QCOMPARE(intFuture.isStarted(), true);
QCOMPARE(intFuture.isFinished(), true);
@@ -693,13 +872,33 @@ void tst_QFuture::futureInterface()
{
QFutureInterface<int> i1;
- i1.reportResult(1);
+ QVERIFY(i1.reportResult(1));
QFutureInterface<int> i2;
- i2.reportResult(2);
+ QVERIFY(i2.reportResult(2));
swap(i1, i2); // ADL must resolve this
QCOMPARE(i1.resultReference(0), 2);
QCOMPARE(i2.resultReference(0), 1);
}
+
+ {
+ QFutureInterface<int> fi;
+ fi.reportStarted();
+ QVERIFY(!fi.reportResults(QList<int> {}));
+ fi.reportFinished();
+
+ QVERIFY(fi.results().empty());
+ }
+
+ {
+ QFutureInterface<int> fi;
+ fi.reportStarted();
+ QList<int> values = { 1, 2, 3 };
+ QVERIFY(fi.reportResults(values));
+ QVERIFY(!fi.reportResults(QList<int> {}));
+ fi.reportFinished();
+
+ QCOMPARE(fi.results(), values);
+ }
}
template <typename T>
@@ -812,6 +1011,39 @@ void tst_QFuture::cancel()
}
}
+void tst_QFuture::cancelAndFinish()
+{
+ {
+ QFutureInterface<void> fi;
+
+ fi.reportStarted();
+ fi.cancelAndFinish();
+
+ QVERIFY(fi.isStarted());
+ QVERIFY(!fi.isRunning());
+ QVERIFY(!fi.isSuspended());
+ QVERIFY(!fi.isSuspending());
+ QVERIFY(fi.isCanceled());
+ QVERIFY(fi.isFinished());
+ }
+
+ // The same with suspended state
+ {
+ QFutureInterface<void> fi;
+
+ fi.reportStarted();
+ fi.setSuspended(true);
+ fi.cancelAndFinish();
+
+ QVERIFY(fi.isStarted());
+ QVERIFY(!fi.isRunning());
+ QVERIFY(!fi.isSuspended());
+ QVERIFY(!fi.isSuspending());
+ QVERIFY(fi.isCanceled());
+ QVERIFY(fi.isFinished());
+ }
+}
+
void tst_QFuture::statePropagation()
{
QFuture<void> f1;
@@ -859,15 +1091,15 @@ void tst_QFuture::multipleResults()
int result;
result = 1;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
QCOMPARE(f.resultAt(0), 1);
result = 2;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
QCOMPARE(f.resultAt(1), 2);
result = 3;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
result = 4;
a.reportFinished(&result);
@@ -878,13 +1110,13 @@ void tst_QFuture::multipleResults()
QList<int> fasit = QList<int>() << 1 << 2 << 3 << 4;
{
QList<int> results;
- foreach(int result, f)
+ for (int result : std::as_const(f))
results.append(result);
QCOMPARE(results, fasit);
}
{
QList<int> results;
- foreach(int result, copy)
+ for (int result : std::as_const(copy))
results.append(result);
QCOMPARE(results, fasit);
}
@@ -908,16 +1140,16 @@ void tst_QFuture::indexedResults()
QChar result;
result = 'B';
- Interface.reportResult(&result, 1);
+ QVERIFY(Interface.reportResult(&result, 1));
QCOMPARE(f.resultAt(1), result);
result = 'A';
- Interface.reportResult(&result, 0);
+ QVERIFY(Interface.reportResult(&result, 0));
QCOMPARE(f.resultAt(0), result);
result = 'C';
- Interface.reportResult(&result); // no index
+ QVERIFY(Interface.reportResult(&result)); // no index
QCOMPARE(f.resultAt(2), result);
Interface.reportFinished();
@@ -933,22 +1165,22 @@ void tst_QFuture::indexedResults()
int result;
result = 0;
- Interface.reportResult(&result, 0);
+ QVERIFY(Interface.reportResult(&result, 0));
QVERIFY(f.isResultReadyAt(0));
QCOMPARE(f.resultAt(0), 0);
result = 3;
- Interface.reportResult(&result, 3);
+ QVERIFY(Interface.reportResult(&result, 3));
QVERIFY(f.isResultReadyAt(3));
QCOMPARE(f.resultAt(3), 3);
result = 2;
- Interface.reportResult(&result, 2);
+ QVERIFY(Interface.reportResult(&result, 2));
QVERIFY(f.isResultReadyAt(2));
QCOMPARE(f.resultAt(2), 2);
result = 4;
- Interface.reportResult(&result); // no index
+ QVERIFY(Interface.reportResult(&result)); // no index
QVERIFY(f.isResultReadyAt(4));
QCOMPARE(f.resultAt(4), 4);
@@ -979,6 +1211,55 @@ void tst_QFuture::progress()
QCOMPARE (f.progressValue(), 50);
}
+void tst_QFuture::setProgressRange()
+{
+ QFutureInterface<int> i;
+
+ QCOMPARE(i.progressMinimum(), 0);
+ QCOMPARE(i.progressMaximum(), 0);
+
+ i.setProgressRange(10, 5);
+
+ QCOMPARE(i.progressMinimum(), 10);
+ QCOMPARE(i.progressMaximum(), 10);
+
+ i.setProgressRange(5, 10);
+
+ QCOMPARE(i.progressMinimum(), 5);
+ QCOMPARE(i.progressMaximum(), 10);
+}
+
+void tst_QFuture::progressWithRange()
+{
+ QFutureInterface<int> i;
+ QFuture<int> f;
+
+ i.reportStarted();
+ f = i.future();
+
+ QCOMPARE(i.progressValue(), 0);
+
+ i.setProgressRange(5, 10);
+
+ QCOMPARE(i.progressValue(), 5);
+
+ i.setProgressValue(20);
+
+ QCOMPARE(i.progressValue(), 5);
+
+ i.setProgressValue(9);
+
+ QCOMPARE(i.progressValue(), 9);
+
+ i.setProgressRange(5, 7);
+
+ QCOMPARE(i.progressValue(), 5);
+
+ i.reportFinished();
+
+ QCOMPARE(f.progressValue(), 5);
+}
+
void tst_QFuture::progressText()
{
QFutureInterface<void> i;
@@ -1005,7 +1286,7 @@ void tst_QFuture::resultsAfterFinished()
QCOMPARE(f.resultCount(), 0);
result = 1;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
QCOMPARE(f.resultAt(0), 1);
a.reportFinished();
@@ -1013,7 +1294,7 @@ void tst_QFuture::resultsAfterFinished()
QCOMPARE(f.resultAt(0), 1);
QCOMPARE(f.resultCount(), 1);
result = 2;
- a.reportResult(&result);
+ QVERIFY(!a.reportResult(&result));
QCOMPARE(f.resultCount(), 1);
}
// cancel it
@@ -1026,7 +1307,7 @@ void tst_QFuture::resultsAfterFinished()
QCOMPARE(f.resultCount(), 0);
result = 1;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
QCOMPARE(f.resultAt(0), 1);
QCOMPARE(f.resultCount(), 1);
@@ -1036,7 +1317,7 @@ void tst_QFuture::resultsAfterFinished()
QCOMPARE(f.resultCount(), 1);
result = 2;
- a.reportResult(&result);
+ QVERIFY(!a.reportResult(&result));
a.reportFinished();
}
}
@@ -1049,9 +1330,9 @@ void tst_QFuture::resultsAsList()
int result;
result = 1;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
result = 2;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
a.reportFinished();
@@ -1059,25 +1340,6 @@ void tst_QFuture::resultsAsList()
QCOMPARE(results, QList<int>() << 1 << 2);
}
-/*
- Test that QFuture<T> can be implicitly converted to T
-*/
-void tst_QFuture::implicitConversions()
-{
- QFutureInterface<QString> iface;
- iface.reportStarted();
-
- QFuture<QString> f(&iface);
-
- const QString input("FooBar 2000");
- iface.reportFinished(&input);
-
- const QString result = f;
- QCOMPARE(result, input);
- QCOMPARE(QString(f), input);
- QCOMPARE(static_cast<QString>(f), input);
-}
-
void tst_QFuture::iterators()
{
{
@@ -1105,16 +1367,16 @@ void tst_QFuture::iterators()
QFuture<int>::const_iterator i1 = f.begin(), i2 = i1 + 1;
QFuture<int>::const_iterator c1 = i1, c2 = c1 + 1;
- QCOMPARE(i1, i1);
- QCOMPARE(i1, c1);
- QCOMPARE(c1, i1);
- QCOMPARE(c1, c1);
- QCOMPARE(i2, i2);
- QCOMPARE(i2, c2);
- QCOMPARE(c2, i2);
- QCOMPARE(c2, c2);
- QCOMPARE(1 + i1, i1 + 1);
- QCOMPARE(1 + c1, c1 + 1);
+ QT_TEST_EQUALITY_OPS(i1, i1, true);
+ QT_TEST_EQUALITY_OPS(i1, c1, true);
+ QT_TEST_EQUALITY_OPS(c1, i1, true);
+ QT_TEST_EQUALITY_OPS(c1, c1, true);
+ QT_TEST_EQUALITY_OPS(i2, i2, true);
+ QT_TEST_EQUALITY_OPS(i2, c2, true);
+ QT_TEST_EQUALITY_OPS(c2, i2, true);
+ QT_TEST_EQUALITY_OPS(c2, c2, true);
+ QT_TEST_EQUALITY_OPS(1 + i1, i1 + 1, true);
+ QT_TEST_EQUALITY_OPS(1 + c1, c1 + 1, true);
QVERIFY(i1 != i2);
QVERIFY(i1 != c2);
@@ -1126,13 +1388,13 @@ void tst_QFuture::iterators()
QVERIFY(c2 != c1);
int x1 = *i1;
- Q_UNUSED(x1);
+ Q_UNUSED(x1)
int x2 = *i2;
- Q_UNUSED(x2);
+ Q_UNUSED(x2)
int y1 = *c1;
- Q_UNUSED(y1);
+ Q_UNUSED(y1)
int y2 = *c2;
- Q_UNUSED(y2);
+ Q_UNUSED(y2)
}
{
@@ -1184,10 +1446,10 @@ void tst_QFuture::iterators()
QCOMPARE(x1, y1);
QCOMPARE(x2, y2);
- int i1Size = i1->size();
- int i2Size = i2->size();
- int c1Size = c1->size();
- int c2Size = c2->size();
+ auto i1Size = i1->size();
+ auto i2Size = i2->size();
+ auto c1Size = c1->size();
+ auto c2Size = c2->size();
QCOMPARE(i1Size, c1Size);
QCOMPARE(i2Size, c2Size);
@@ -1492,12 +1754,10 @@ void tst_QFuture::voidConversions()
QFuture<int> intFuture(&iface);
int value = 10;
- iface.reportFinished(&value);
+ QVERIFY(iface.reportFinished(&value));
QFuture<void> voidFuture(intFuture);
voidFuture = intFuture;
-
- QVERIFY(voidFuture == intFuture);
}
{
@@ -1507,7 +1767,7 @@ void tst_QFuture::voidConversions()
iface.reportStarted();
QFuture<QList<int> > listFuture(&iface);
- iface.reportResult(QList<int>() << 1 << 2 << 3);
+ QVERIFY(iface.reportResult(QList<int>() << 1 << 2 << 3));
voidFuture = listFuture;
}
QCOMPARE(voidFuture.resultCount(), 0);
@@ -1634,7 +1894,7 @@ void tst_QFuture::exceptions()
bool caught = false;
try {
foreach (int e, f.results()) {
- Q_UNUSED(e);
+ Q_UNUSED(e)
QFAIL("did not get exception");
}
} catch (QException &) {
@@ -1701,7 +1961,7 @@ void tst_QFuture::nestedExceptions()
{
try {
MyClass m;
- Q_UNUSED(m);
+ Q_UNUSED(m)
throw 0;
} catch (int) {}
@@ -1729,7 +1989,7 @@ void tst_QFuture::nonGlobalThreadPool()
void run() override
{
const int ms = 100 + (QRandomGenerator::global()->bounded(100) - 100/2);
- QThread::msleep(ms);
+ QThread::sleep(std::chrono::milliseconds{ms});
reportResult(Answer);
reportFinished();
}
@@ -2014,6 +2274,26 @@ void tst_QFuture::then()
QVERIFY(threadId1 != QThread::currentThreadId());
QVERIFY(threadId2 != QThread::currentThreadId());
}
+
+ // QTBUG-106083 & QTBUG-105182
+ {
+ QThread thread;
+ thread.start();
+
+ QObject context;
+ context.moveToThread(&thread);
+
+ auto future = QtConcurrent::run([] {
+ return 42;
+ }).then([] (int result) {
+ return result + 1;
+ }).then(&context, [] (int result) {
+ return result + 1;
+ });
+ QCOMPARE(future.result(), 44);
+ thread.quit();
+ thread.wait();
+ }
}
template<class Type, class Callable>
@@ -2767,6 +3047,396 @@ void tst_QFuture::onCanceled()
#endif // QT_NO_EXCEPTIONS
}
+void tst_QFuture::cancelContinuations()
+{
+ // The chain is cancelled in the middle of execution of continuations
+ {
+ QPromise<int> promise;
+
+ int checkpoint = 0;
+ auto future = promise.future().then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ promise.future().cancel();
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ promise.start();
+ promise.addResult(42);
+ promise.finish();
+
+ QCOMPARE(future.result(), -1);
+ QCOMPARE(checkpoint, 2);
+ }
+
+ // The chain is cancelled before the execution of continuations
+ {
+ auto f = QtFuture::makeReadyValueFuture(42);
+ f.cancel();
+
+ int checkpoint = 0;
+ auto future = f.then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ QCOMPARE(future.result(), -1);
+ QCOMPARE(checkpoint, 0);
+ }
+
+ // The chain is canceled partially, through an intermediate future
+ {
+ QPromise<int> promise;
+
+ int checkpoint = 0;
+ auto intermediate = promise.future().then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ });
+
+ auto future = intermediate.then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ promise.start();
+ promise.addResult(42);
+
+ // This should cancel only the chain starting from intermediate
+ intermediate.cancel();
+
+ promise.finish();
+
+ QCOMPARE(future.result(), -1);
+ QCOMPARE(checkpoint, 1);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // The chain is cancelled in the middle of execution of continuations,
+ // while there's an exception in the chain, which is handled inside
+ // the continuations.
+ {
+ QPromise<int> promise;
+
+ int checkpoint = 0;
+ auto future = promise.future().then([&](int value) {
+ ++checkpoint;
+ throw QException();
+ return value + 1;
+ }).then([&](QFuture<int> future) {
+ try {
+ auto res = future.result();
+ Q_UNUSED(res);
+ } catch (const QException &) {
+ ++checkpoint;
+ }
+ return 2;
+ }).then([&](int value) {
+ ++checkpoint;
+ promise.future().cancel();
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ promise.start();
+ promise.addResult(42);
+ promise.finish();
+
+ QCOMPARE(future.result(), -1);
+ QCOMPARE(checkpoint, 3);
+ }
+#endif // QT_NO_EXCEPTIONS
+
+ // Check notifications from QFutureWatcher
+ {
+ QPromise<void> p;
+ auto f = p.future();
+
+ auto f1 = f.then([] {});
+ auto f2 = f1.then([] {});
+
+ QFutureWatcher<void> watcher1, watcher2;
+ int state = 0;
+ QObject::connect(&watcher1, &QFutureWatcher<void>::started, [&] {
+ QCOMPARE(state, 0);
+ ++state;
+ });
+ QObject::connect(&watcher1, &QFutureWatcher<void>::canceled, [&] {
+ QCOMPARE(state, 1);
+ ++state;
+ });
+ QObject::connect(&watcher1, &QFutureWatcher<void>::finished, [&] {
+ QCOMPARE(state, 2);
+ ++state;
+ });
+ QObject::connect(&watcher2, &QFutureWatcher<void>::started, [&] {
+ QCOMPARE(state, 3);
+ ++state;
+ });
+ QObject::connect(&watcher2, &QFutureWatcher<void>::canceled, [&] {
+ QCOMPARE(state, 4);
+ ++state;
+ });
+ QObject::connect(&watcher2, &QFutureWatcher<int>::finished, [&] {
+ QCOMPARE(state, 5);
+ ++state;
+ });
+
+ watcher1.setFuture(f1);
+ watcher2.setFuture(f2);
+
+ p.start();
+ f.cancel();
+ p.finish();
+
+ qApp->processEvents();
+
+ QCOMPARE(state, 6);
+ QVERIFY(watcher1.isFinished());
+ QVERIFY(watcher1.isCanceled());
+ QVERIFY(watcher2.isFinished());
+ QVERIFY(watcher2.isCanceled());
+ }
+
+ // Cancel continuations with context (QTBUG-108790)
+ {
+ // This test should pass with ASan
+ auto future = QtConcurrent::run([] {});
+ future.then(this, [] {});
+ future.waitForFinished();
+ future.cancel();
+ }
+}
+
+void tst_QFuture::continuationsWithContext_data()
+{
+ QTest::addColumn<bool>("inOtherThread");
+ QTest::addRow("in-other-thread") << true;
+ QTest::addRow("in-main-thread-qtbug119406") << false;
+}
+
+void tst_QFuture::continuationsWithContext()
+{
+ QFETCH(bool, inOtherThread);
+
+ auto tstThread = QThread::currentThread();
+ QThread *thread = inOtherThread ? new QThread
+ : tstThread;
+ auto context = new QObject();
+
+ const auto cleanupGuard = qScopeGuard([&] {
+ context->deleteLater();
+ if (thread != tstThread) {
+ thread->quit();
+ thread->wait();
+ delete thread;
+ }
+ });
+
+ if (inOtherThread) {
+ thread->start();
+ context->moveToThread(thread);
+ }
+
+ // .then()
+ {
+ QPromise<int> promise;
+ auto future = promise.future()
+ .then([&](int val) {
+ if (QThread::currentThread() != tstThread)
+ return 0;
+ return val + 1;
+ })
+ .then(context,
+ [&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1;
+ });
+ promise.start();
+ promise.addResult(0);
+ promise.finish();
+ QCOMPARE(future.result(), 3);
+ }
+
+ // .onCanceled
+ {
+ QPromise<int> promise;
+ auto future = promise.future()
+ .onCanceled(context,
+ [&] {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1;
+ });
+ promise.start();
+ promise.future().cancel();
+ promise.finish();
+ QCOMPARE(future.result(), 2);
+ }
+
+ // Cancellation when the context object is destroyed
+ {
+ // Use something like QWidget which deletes its children early, i.e.
+ // before ~QObject() runs. This behavior can lead to side-effects
+ // like QPointers to the parent not being set to nullptr during child
+ // object destruction.
+ QPointer shortLivedContext = new FakeQWidget();
+ shortLivedContext->moveToThread(thread);
+
+ QPromise<int> promise;
+ auto future = promise.future()
+ .then(shortLivedContext, [&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1000;
+ })
+ .onCanceled([&, ptr=QPointer(shortLivedContext)] {
+ if (QThread::currentThread() != thread)
+ return 0;
+ if (ptr)
+ return 1;
+ return 2;
+ });
+ promise.start();
+
+ QMetaObject::invokeMethod(shortLivedContext, [&]() {
+ delete shortLivedContext;
+ }, inOtherThread ? Qt::BlockingQueuedConnection
+ : Qt::DirectConnection);
+
+ promise.finish();
+ QCOMPARE(future.result(), 2);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // .onFaled()
+ {
+ QPromise<void> promise;
+ auto future = promise.future()
+ .then([&] {
+ if (QThread::currentThread() != tstThread)
+ return 0;
+ throw std::runtime_error("error");
+ })
+ .onFailed(context,
+ [&] {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1;
+ });
+ promise.start();
+ promise.finish();
+ QCOMPARE(future.result(), 2);
+ }
+#endif // QT_NO_EXCEPTIONS
+}
+
+void tst_QFuture::continuationsWithMoveOnlyLambda()
+{
+ // .then()
+ {
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeReadyVoidFuture()
+ .then([p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+ // .then() with thread pool
+ {
+ QThreadPool pool;
+
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeReadyVoidFuture()
+ .then(&pool, [p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+ // .then() with context
+ {
+ QObject object;
+
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeReadyVoidFuture()
+ .then(&object, [p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+
+ // .onCanceled()
+ {
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future =
+ createCanceledFuture<int>().onCanceled([p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+
+ // .onCanceled() with context
+ {
+ QObject object;
+
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = createCanceledFuture<int>().onCanceled(
+ &object, [p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // .onFailed()
+ {
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeExceptionalFuture<int>(QException())
+ .onFailed([p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+ // .onFailed() with context
+ {
+ QObject object;
+
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeExceptionalFuture<int>(QException())
+ .onFailed(&object, [p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+#endif // QT_NO_EXCEPTIONS
+}
+
void tst_QFuture::testSingleResult(const UniquePtr &p)
{
QVERIFY(p.get() != nullptr);
@@ -2780,7 +3450,7 @@ void tst_QFuture::testSingleResult(const std::vector<int> &v)
template<class T>
void tst_QFuture::testSingleResult(const T &unknown)
{
- Q_UNUSED(unknown);
+ Q_UNUSED(unknown)
}
@@ -2789,17 +3459,6 @@ void tst_QFuture::testFutureTaken(QFuture<T> &noMoreFuture)
{
QCOMPARE(noMoreFuture.isValid(), false);
QCOMPARE(noMoreFuture.resultCount(), 0);
- QCOMPARE(noMoreFuture.isStarted(), false);
- QCOMPARE(noMoreFuture.isRunning(), false);
- QCOMPARE(noMoreFuture.isSuspending(), false);
- QCOMPARE(noMoreFuture.isSuspended(), false);
-#if QT_DEPRECATED_SINCE(6, 0)
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_DEPRECATED
- QCOMPARE(noMoreFuture.isPaused(), false);
-QT_WARNING_POP
-#endif
- QCOMPARE(noMoreFuture.isFinished(), false);
QCOMPARE(noMoreFuture.progressValue(), 0);
}
@@ -2829,6 +3488,7 @@ void tst_QFuture::testTakeResults(QFuture<T> future, size_type resultCount)
testFutureTaken(copy);
}
+#if 0
void tst_QFuture::takeResults()
{
// Test takeResults() for movable types (whether or not copyable).
@@ -2844,8 +3504,8 @@ void tst_QFuture::takeResults()
const int expectedCount = 10;
for (int i = 0; i < expectedCount; ++i) {
- moveIface.reportAndMoveResult(UniquePtr{new int(0b101010)}, i);
- copyIface.reportAndMoveResult(std::vector<int>{1,2,3,4,5}, i);
+ QVERIFY(moveIface.reportAndMoveResult(UniquePtr{new int(0b101010)}, i));
+ QVERIFY(copyIface.reportAndMoveResult(std::vector<int>{1,2,3,4,5}, i));
}
moveIface.reportFinished();
@@ -2857,12 +3517,13 @@ void tst_QFuture::takeResults()
testTakeResults(copyIface.future(), size_type(expectedCount));
}
+#endif
void tst_QFuture::takeResult()
{
QFutureInterface<UniquePtr> iface;
iface.reportStarted();
- iface.reportAndMoveResult(UniquePtr{new int(0b101010)}, 0);
+ QVERIFY(iface.reportAndMoveResult(UniquePtr{new int(0b101010)}, 0));
iface.reportFinished();
auto future = iface.future();
@@ -2885,7 +3546,7 @@ void tst_QFuture::runAndTake()
auto rabbit = [](){
// Let's wait a bit to give the test below some time
// to sync up with us with its watcher.
- QThread::currentThread()->msleep(100);
+ QThread::currentThread()->sleep(std::chrono::milliseconds{100});
return UniquePtr(new int(10));
};
@@ -2898,12 +3559,15 @@ void tst_QFuture::runAndTake()
auto gotcha = QtConcurrent::run(rabbit);
watcha.setFuture(gotcha);
- loop.enterLoopMSecs(500);
+ loop.enterLoop(500ms);
if (loop.timeout())
QSKIP("Failed to run the task, nothing to test");
gotcha = watcha.future();
+#if 0
+ // TODO: enable when QFuture::takeResults() is enabled
testTakeResults(gotcha, size_type(1));
+#endif
}
void tst_QFuture::resultsReadyAt_data()
@@ -2943,9 +3607,9 @@ void tst_QFuture::resultsReadyAt()
{
int dummyResult = 0b101010;
if (testMove)
- iface.reportAndMoveResult(std::move(dummyResult), index);
+ QVERIFY(iface.reportAndMoveResult(std::move(dummyResult), index));
else
- iface.reportResult(&dummyResult, index);
+ QVERIFY(iface.reportResult(&dummyResult, index));
};
const QSignalSpy readyCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt);
@@ -2966,14 +3630,14 @@ void tst_QFuture::resultsReadyAt()
// Run event loop, QCoreApplication::postEvent is in use
// in QFutureInterface:
- eventProcessor.enterLoopMSecs(2000);
+ eventProcessor.enterLoop(DefaultWaitTime);
QVERIFY(!eventProcessor.timeout());
if (QTest::currentTestFailed()) // Failed in our lambda observing 'ready at'
return;
QCOMPARE(reported, nExpectedResults);
QCOMPARE(nExpectedResults, iface.future().resultCount());
- QCOMPARE(readyCounter.count(), 3);
+ QCOMPARE(readyCounter.size(), 3);
QCOMPARE(taken, 0b1111);
}
@@ -3014,11 +3678,21 @@ void tst_QFuture::canceledFutureIsNotValid()
void tst_QFuture::signalConnect()
{
+ const int intValue = 42;
+ const double doubleValue = 42.5;
+ const QString stringValue = "42";
+
+ using TupleType = std::tuple<int, double, QString>;
+ const TupleType tuple(intValue, doubleValue, stringValue);
+
+ using PairType = std::pair<int, double>;
+ const PairType pair(intValue, doubleValue);
+
// No arg
{
SenderObject sender;
auto future =
- QtFuture::connect(&sender, &SenderObject::noArgSignal).then([&] { return true; });
+ QtFuture::connect(&sender, &SenderObject::noArgSignal).then([] { return true; });
sender.emitNoArg();
QCOMPARE(future.result(), true);
}
@@ -3047,16 +3721,151 @@ void tst_QFuture::signalConnect()
// Multiple args
{
SenderObject sender;
- using TupleArgs = std::tuple<int, double, QString>;
auto future =
- QtFuture::connect(&sender, &SenderObject::multipleArgs).then([](TupleArgs values) {
+ QtFuture::connect(&sender, &SenderObject::multipleArgs).then([](TupleType values) {
return values;
});
- sender.emitMultipleArgs(42, 42.5, "42");
+ sender.emitMultipleArgs(intValue, doubleValue, stringValue);
+ auto result = future.result();
+ QCOMPARE(result, tuple);
+ }
+
+ // Single std::tuple arg
+ {
+ SenderObject sender;
+ QFuture<TupleType> future = QtFuture::connect(&sender, &SenderObject::tupleArgSignal);
+ sender.emitTupleArgSignal(tuple);
+ auto result = future.result();
+ QCOMPARE(result, tuple);
+ }
+
+ // Multi-args signal(int, std::tuple)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<int, TupleType>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithTupleSignal1);
+ sender.emitMultiArgsWithTupleSignal1(142, tuple);
+ const auto [v, t] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(t, tuple);
+ }
+
+ // Multi-args signal(std::tuple, int)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<TupleType, int>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithTupleSignal2);
+ sender.emitMultiArgsWithTupleSignal2(tuple, 142);
+ const auto [t, v] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(t, tuple);
+ }
+
+ // Multi-args signal(int, std::pair)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<int, PairType>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithPairSignal1);
+ sender.emitMultiArgsWithPairSignal1(142, pair);
+ const auto [v, p] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(p, pair);
+ }
+
+ // Multi-args signal(std::pair, int)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<PairType, int>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithPairSignal2);
+ sender.emitMultiArgsWithPairSignal2(pair, 142);
+ const auto [p, v] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(p, pair);
+ }
+
+ // No arg private signal
+ {
+ SenderObject sender;
+ auto future = QtFuture::connect(&sender, &SenderObject::noArgPrivateSignal).then([] {
+ return true;
+ });
+ sender.emitNoArgPrivateSignal();
+ QCOMPARE(future.result(), true);
+ }
+
+ // One arg private signal
+ {
+ SenderObject sender;
+ auto future =
+ QtFuture::connect(&sender, &SenderObject::intArgPrivateSignal).then([](int value) {
+ return value;
+ });
+ sender.emitIntArgPrivateSignal(42);
+ QCOMPARE(future.result(), 42);
+ }
+
+ // Multi-args private signal
+ {
+ SenderObject sender;
+ auto future = QtFuture::connect(&sender, &SenderObject::multiArgsPrivateSignal)
+ .then([](TupleType values) { return values; });
+ sender.emitMultiArgsPrivateSignal(intValue, doubleValue, stringValue);
auto result = future.result();
- QCOMPARE(std::get<0>(result), 42);
- QCOMPARE(std::get<1>(result), 42.5);
- QCOMPARE(std::get<2>(result), "42");
+ QCOMPARE(result, tuple);
+ }
+
+ // Single std::tuple arg private signal
+ {
+ SenderObject sender;
+ QFuture<TupleType> future =
+ QtFuture::connect(&sender, &SenderObject::tupleArgPrivateSignal);
+ sender.emitTupleArgPrivateSignal(tuple);
+ auto result = future.result();
+ QCOMPARE(result, tuple);
+ }
+
+ // Multi-args private signal(int, std::tuple)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<int, TupleType>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithTuplePrivateSignal1);
+ sender.emitMultiArgsWithTuplePrivateSignal1(142, tuple);
+ const auto [v, t] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(t, tuple);
+ }
+
+ // Multi-args private signal(std::tuple, int)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<TupleType, int>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithTuplePrivateSignal2);
+ sender.emitMultiArgsWithTuplePrivateSignal2(tuple, 142);
+ const auto [t, v] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(t, tuple);
+ }
+
+ // Multi-args private signal(int, std::pair)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<int, PairType>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithPairPrivateSignal1);
+ sender.emitMultiArgsWithPairPrivateSignal1(142, pair);
+ const auto [v, p] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(p, pair);
+ }
+
+ // Multi-args private signal(std::pair, int)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<PairType, int>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithPairPrivateSignal2);
+ sender.emitMultiArgsWithPairPrivateSignal2(pair, 142);
+ const auto [p, v] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(p, pair);
}
// Sender destroyed
@@ -3068,13 +3877,60 @@ void tst_QFuture::signalConnect()
QSignalSpy spy(sender, &QObject::destroyed);
sender->deleteLater();
- // emit the signal when sender is being destroyed
- QObject::connect(sender, &QObject::destroyed, [sender] { sender->emitIntArg(42); });
spy.wait();
QVERIFY(future.isCanceled());
QVERIFY(!future.isValid());
}
+
+ // Signal emitted, causing Sender to be destroyed
+ {
+ SenderObject *sender = new SenderObject();
+
+ auto future = QtFuture::connect(sender, &SenderObject::intArgSignal);
+ future.then([sender](int) {
+ // Scenario: Sender no longer needed, so it's deleted
+ delete sender;
+ });
+
+ QSignalSpy spy(sender, &SenderObject::destroyed);
+ emit sender->intArgSignal(5);
+ spy.wait();
+
+ QVERIFY(future.isFinished());
+ QVERIFY(!future.isCanceled());
+ QVERIFY(future.isValid());
+ }
+
+ // Connect to nullptr
+ {
+ SenderObject *sender = nullptr;
+ auto future = QtFuture::connect(sender, &SenderObject::intArgSignal);
+ QVERIFY(future.isFinished());
+ QVERIFY(future.isCanceled());
+ QVERIFY(!future.isValid());
+ }
+
+ // Connect to non-signal
+ {
+ SenderObject sender;
+
+#if defined(Q_CC_MSVC_ONLY) && (Q_CC_MSVC < 1940 || __cplusplus < 202002L)
+#define EXPECT_FUTURE_CONNECT_FAIL() QEXPECT_FAIL("", "QTBUG-101761, test fails on Windows/MSVC", Continue)
+#else
+ QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in SenderObject");
+#define EXPECT_FUTURE_CONNECT_FAIL()
+#endif
+
+ auto future = QtFuture::connect(&sender, &SenderObject::emitNoArg);
+ EXPECT_FUTURE_CONNECT_FAIL();
+ QVERIFY(future.isFinished());
+ EXPECT_FUTURE_CONNECT_FAIL();
+ QVERIFY(future.isCanceled());
+ EXPECT_FUTURE_CONNECT_FAIL();
+ QVERIFY(!future.isValid());
+#undef EXPECT_FUTURE_CONNECT_FAIL
+ }
}
void tst_QFuture::waitForFinished()
@@ -3101,5 +3957,1409 @@ void tst_QFuture::waitForFinished()
QVERIFY(waitingThread->isFinished());
}
+void tst_QFuture::rejectResultOverwrite_data()
+{
+ QTest::addColumn<bool>("filterMode");
+ QTest::addColumn<QList<int>>("initResults");
+
+ QTest::addRow("filter-mode-on-1-result") << true << QList<int>({ 456 });
+ QTest::addRow("filter-mode-on-N-results") << true << QList<int>({ 456, 789 });
+ QTest::addRow("filter-mode-off-1-result") << false << QList<int>({ 456 });
+ QTest::addRow("filter-mode-off-N-results") << false << QList<int>({ 456, 789 });
+}
+
+void tst_QFuture::rejectResultOverwrite()
+{
+ QFETCH(bool, filterMode);
+ QFETCH(QList<int>, initResults);
+
+ QFutureInterface<int> iface;
+ iface.setFilterMode(filterMode);
+ auto f = iface.future();
+ QFutureWatcher<int> watcher;
+ watcher.setFuture(f);
+
+ QTestEventLoop eventProcessor;
+ // control the loop by suspend
+ connect(&watcher, &QFutureWatcher<int>::suspending, &eventProcessor, &QTestEventLoop::exitLoop);
+ // internal machinery always emits resultsReadyAt
+ QSignalSpy resultCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt);
+
+ // init
+ if (initResults.size() == 1)
+ QVERIFY(iface.reportResult(initResults[0]));
+ else
+ QVERIFY(iface.reportResults(initResults));
+ QCOMPARE(f.resultCount(), initResults.size());
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ QCOMPARE(f.results(), initResults);
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ // Run event loop, QCoreApplication::postEvent is in use
+ // in QFutureInterface:
+ eventProcessor.enterLoop(DefaultWaitTime);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.size(), 1);
+ f.resume();
+
+ // overwrite with lvalue
+ {
+ int result = -1;
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(result, 0));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+ // overwrite with rvalue
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(-1, 0));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+ // overwrite with array
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResults(QList<int> { -1, -2, -3 }, 0));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+
+ // special case: add result by different index, overlapping with the vector
+ if (initResults.size() > 1) {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(-1, 1));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(1), initResults[1]);
+ }
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ eventProcessor.enterLoop(DefaultWaitTime);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.size(), 1);
+ f.resume();
+ QCOMPARE(f.results(), initResults);
+}
+
+void tst_QFuture::rejectPendingResultOverwrite()
+{
+ QFETCH(bool, filterMode);
+ QFETCH(QList<int>, initResults);
+
+ QFutureInterface<int> iface;
+ iface.setFilterMode(filterMode);
+ auto f = iface.future();
+ QFutureWatcher<int> watcher;
+ watcher.setFuture(f);
+
+ QTestEventLoop eventProcessor;
+ // control the loop by suspend
+ connect(&watcher, &QFutureWatcher<int>::suspending, &eventProcessor, &QTestEventLoop::exitLoop);
+ // internal machinery always emits resultsReadyAt
+ QSignalSpy resultCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt);
+
+ // init
+ if (initResults.size() == 1)
+ QVERIFY(iface.reportResult(initResults[0], 1));
+ else
+ QVERIFY(iface.reportResults(initResults, 1));
+ QCOMPARE(f.resultCount(), 0); // not visible yet
+ if (!filterMode) {
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ QCOMPARE(f.results(), initResults);
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ // Run event loop, QCoreApplication::postEvent is in use
+ // in QFutureInterface:
+ eventProcessor.enterLoop(DefaultWaitTime);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.size(), 1);
+ f.resume();
+ }
+
+ // overwrite with lvalue
+ {
+ int result = -1;
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(result, 1));
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // overwrite with rvalue
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(-1, 1));
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // overwrite with array
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResults(QList<int> { -1, -2 }, 1));
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // special case: add result by different index, overlapping with the vector
+ if (initResults.size() > 1) {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(-1, 2));
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(2), initResults[1]);
+ }
+
+ if (!filterMode) {
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ eventProcessor.enterLoop(DefaultWaitTime);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.size(), 1);
+ f.resume();
+ }
+
+ QVERIFY(iface.reportResult(123, 0)); // make results at 0 and 1 accessible
+ QCOMPARE(f.resultCount(), initResults.size() + 1);
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ initResults.prepend(123);
+ QCOMPARE(f.results(), initResults);
+}
+
+void tst_QFuture::createReadyFutures()
+{
+#if QT_DEPRECATED_SINCE(6, 10)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ // using const T &
+ {
+ const int val = 42;
+ QFuture<int> f = QtFuture::makeReadyFuture(val);
+ QCOMPARE(f.result(), val);
+ }
+
+ // using T
+ {
+ int val = 42;
+ QFuture<int> f = QtFuture::makeReadyFuture(val);
+ QCOMPARE(f.result(), val);
+ }
+
+ // using T &&
+ {
+ auto f = QtFuture::makeReadyFuture(std::make_unique<int>(42));
+ QCOMPARE(*f.takeResult(), 42);
+ }
+
+ // using void
+ {
+ auto f = QtFuture::makeReadyFuture();
+ QVERIFY(f.isStarted());
+ QVERIFY(!f.isRunning());
+ QVERIFY(f.isFinished());
+ }
+
+ // using const QList<T> &
+ {
+ const QList<int> values { 1, 2, 3 };
+ auto f = QtFuture::makeReadyFuture(values);
+ QCOMPARE(f.resultCount(), 3);
+ QCOMPARE(f.results(), values);
+ }
+QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 10)
+
+ // test makeReadyValueFuture<T>()
+ {
+ const int val = 42;
+ auto f = QtFuture::makeReadyValueFuture(val);
+ QCOMPARE_EQ(f.result(), val);
+
+ int otherVal = 42;
+ f = QtFuture::makeReadyValueFuture(otherVal);
+ QCOMPARE_EQ(f.result(), otherVal);
+ }
+ {
+ auto f = QtFuture::makeReadyValueFuture(std::make_unique<int>(42));
+ QCOMPARE(*f.takeResult(), 42);
+ }
+ // test makeReadyVoidFuture()
+ {
+ auto f = QtFuture::makeReadyVoidFuture();
+ QVERIFY(f.isStarted());
+ QVERIFY(!f.isRunning());
+ QVERIFY(f.isFinished());
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // using QException
+ {
+ QException e;
+ auto f = QtFuture::makeExceptionalFuture<int>(e);
+ bool caught = false;
+ try {
+ f.result();
+ } catch (QException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ }
+
+ // using std::exception_ptr and QFuture<void>
+ {
+ auto exception = std::make_exception_ptr(TestException());
+ auto f = QtFuture::makeExceptionalFuture(exception);
+ bool caught = false;
+ try {
+ f.waitForFinished();
+ } catch (TestException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ }
+#endif
+
+ // testing makeReadyRangeFuture with various containers
+ {
+ const QList<int> expectedResult{1, 2, 3};
+
+ const QList<int> list{1, 2, 3};
+ auto f = QtFuture::makeReadyRangeFuture(list);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ QVarLengthArray<int> varArray{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(varArray);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ std::vector<int> vec{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(std::move(vec));
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ f = QtFuture::makeReadyRangeFuture(std::array<int, 3>{1, 2, 3});
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ f = QtFuture::makeReadyRangeFuture(std::list<int>{1, 2, 3});
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ std::forward_list<int> fwdlist{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(fwdlist);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ const QSet<int> qset{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(qset);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ auto result = f.results();
+ std::sort(result.begin(), result.end());
+ QCOMPARE_EQ(result, expectedResult);
+
+ const QMap<QString, int> qmap{
+ {"one", 1},
+ {"two", 2},
+ {"three", 3}
+ };
+ f = QtFuture::makeReadyRangeFuture(qmap);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ result = f.results();
+ std::sort(result.begin(), result.end());
+ QCOMPARE_EQ(result, expectedResult);
+
+ std::set<int> stdset{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(stdset);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ result = f.results();
+ std::sort(result.begin(), result.end());
+ QCOMPARE_EQ(result, expectedResult);
+
+ // testing ValueType[N] overload
+ const int c_array[] = {1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(c_array);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ f = QtFuture::makeReadyRangeFuture({1, 2, 3});
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+ }
+ // testing makeReadyRangeFuture with a more complex underlying type
+ {
+ QObject obj1;
+ QObject obj2;
+ QObject obj3;
+
+ const QList<QObject*> expectedResult{&obj1, &obj2, &obj3};
+
+ const QList<QObject*> list{&obj1, &obj2, &obj3};
+ auto f = QtFuture::makeReadyRangeFuture(list);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ std::list<QObject*> stdlist{&obj1, &obj2, &obj3};
+ f = QtFuture::makeReadyRangeFuture(std::move(stdlist));
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ QObject* const c_array[] = {&obj1, &obj2, &obj3};
+ f = QtFuture::makeReadyRangeFuture(c_array);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+ }
+}
+
+void tst_QFuture::continuationsAfterReadyFutures()
+{
+ // continuations without a context
+ {
+ QFuture<int> f = QtFuture::makeReadyValueFuture(42)
+ .then([](int val) {
+ return val + 10;
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 52);
+ }
+ {
+ auto rangeF = QtFuture::makeReadyRangeFuture({1, 2, 3});
+ QFuture<int> f = rangeF
+ .then([vals = rangeF.results()](auto) {
+ return vals.last();
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 3);
+ }
+ {
+ QFuture<int> f = QtFuture::makeReadyVoidFuture()
+ .then([]() {
+ return 1;
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 1);
+ }
+#ifndef QT_NO_EXCEPTIONS
+ {
+ QException e;
+ QFuture<int> f = QtFuture::makeExceptionalFuture<int>(e)
+ .then([](int) {
+ return 1;
+ })
+ .onCanceled([]() {
+ return -1;
+ })
+ .onFailed([](const QException &) {
+ return -2;
+ });
+ QCOMPARE(f.result(), -2);
+ }
+#endif
+
+ // continuations with a context
+ QObject context;
+ {
+ QFuture<int> f = QtFuture::makeReadyValueFuture(42)
+ .then(&context, [](int val) {
+ return val + 10;
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 52);
+ }
+ {
+ auto rangeF = QtFuture::makeReadyRangeFuture({1, 2, 3});
+ QFuture<int> f = rangeF
+ .then(&context, [vals = rangeF.results()](auto) {
+ return vals.last();
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 3);
+ }
+ {
+ QFuture<int> f = QtFuture::makeReadyVoidFuture()
+ .then(&context, []() {
+ return 1;
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 1);
+ }
+#ifndef QT_NO_EXCEPTIONS
+ {
+ QException e;
+ QFuture<int> f = QtFuture::makeExceptionalFuture<int>(e)
+ .then(&context, [](int) {
+ return 1;
+ })
+ .onCanceled([]() {
+ return -1;
+ })
+ .onFailed([](const QException &) {
+ return -2;
+ });
+ QCOMPARE(f.result(), -2);
+ }
+#endif
+}
+
+void tst_QFuture::getFutureInterface()
+{
+ const int val = 42;
+ QFuture<int> f = QtFuture::makeReadyValueFuture(val);
+
+ auto interface = QFutureInterfaceBase::get(f);
+ QCOMPARE(interface.resultCount(), 1);
+}
+
+void tst_QFuture::convertQMetaType()
+{
+ const auto intType = QMetaType::fromType<QFuture<int>>();
+ const auto voidType = QMetaType::fromType<QFuture<void>>();
+
+ QVERIFY(QMetaType::canConvert(intType, voidType));
+
+ const int val = 42;
+ QFuture<int> f = QtFuture::makeReadyValueFuture(val);
+ auto variant = QVariant::fromValue(f);
+ QVERIFY(variant.convert(voidType));
+
+ const auto voidFuture = variant.value<QFuture<void>>();
+ QVERIFY(voidFuture.isValid());
+ QVERIFY(voidFuture.isFinished());
+}
+
+template<class OutputContainer>
+void testWhenAllIterators()
+{
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QPromise<int> p2;
+ QList<QFuture<int>> futures = { p0.future(), p1.future(), p2.future() };
+
+ bool finished = false;
+ QFuture<OutputContainer> whenAll;
+ if constexpr (std::is_same_v<QList<QFuture<int>>, OutputContainer>)
+ whenAll = QtFuture::whenAll(futures.begin(), futures.end());
+ else
+ whenAll = QtFuture::whenAll<OutputContainer>(futures.begin(), futures.end());
+ whenAll.then([&](const OutputContainer &output) {
+ QCOMPARE(output.size(), 3u);
+ QCOMPARE(output[0].result(), 0);
+ QCOMPARE(output[1].result(), 1);
+ QCOMPARE(output[2].result(), 2);
+ finished = true;
+ });
+ QVERIFY(whenAll.isRunning());
+
+ p0.start();
+ p0.addResult(0);
+ p0.finish();
+ QVERIFY(whenAll.isRunning());
+
+ p2.start();
+ p2.addResult(2);
+ p2.finish();
+ QVERIFY(whenAll.isRunning());
+
+ p1.start();
+ p1.addResult(1);
+ p1.finish();
+ QVERIFY(!whenAll.isRunning());
+ QVERIFY(finished);
+
+ // Try with empty sequence
+ QFuture<OutputContainer> whenAllEmpty;
+ if constexpr (std::is_same_v<QList<QFuture<int>>, OutputContainer>)
+ whenAllEmpty = QtFuture::whenAll(futures.end(), futures.end());
+ else
+ whenAllEmpty = QtFuture::whenAll<OutputContainer>(futures.end(), futures.end());
+ QVERIFY(whenAllEmpty.isStarted());
+ QVERIFY(whenAllEmpty.isFinished());
+ QVERIFY(whenAllEmpty.result().empty());
+}
+
+void tst_QFuture::whenAllIterators()
+{
+ // Try with different output containers
+ testWhenAllIterators<QList<QFuture<int>>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllIterators() with QList failed!");
+
+ testWhenAllIterators<std::vector<QFuture<int>>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllIterators() with std::vector failed!");
+
+ testWhenAllIterators<QVarLengthArray<QFuture<int>>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllIterators() with QVarLengthArray failed!");
+}
+
+void tst_QFuture::whenAllIteratorsWithCanceled()
+{
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QList<QFuture<int>> futures = { p0.future(), p1.future() };
+ bool finished = false;
+ auto whenAll = QtFuture::whenAll(futures.begin(), futures.end())
+ .then([&](const QList<QFuture<int>> &results) {
+ QCOMPARE(results.size(), 2);
+ QVERIFY(results[0].isCanceled());
+ QVERIFY(!results[1].isCanceled());
+ QCOMPARE(results[1].result(), 1);
+ finished = true;
+ });
+
+ p0.start();
+ p0.future().cancel();
+ p0.finish();
+ QVERIFY(!finished);
+
+ p1.start();
+ p1.addResult(1);
+ p1.finish();
+ QVERIFY(finished);
+}
+
+void tst_QFuture::whenAllIteratorsWithFailed()
+{
+#ifndef QT_NO_EXCEPTIONS
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QList<QFuture<int>> futures = { p0.future(), p1.future() };
+ bool finished = false;
+ auto whenAll = QtFuture::whenAll(futures.begin(), futures.end())
+ .then([&](QList<QFuture<int>> results) {
+ QCOMPARE(results.size(), 2);
+ QCOMPARE(results[1].result(), 1);
+ // A shorter way of handling the exception
+ results[0].onFailed([&](const QException &) {
+ finished = true;
+ return 0;
+ });
+ });
+
+ p0.start();
+ p0.setException(QException());
+ p0.finish();
+ QVERIFY(!finished);
+
+ p1.start();
+ p1.addResult(1);
+ p1.finish();
+ QVERIFY(finished);
+#else
+ QSKIP("Exceptions are disabled, skipping the test");
+#endif
+}
+
+// A helper for std::visit, see https://en.cppreference.com/w/cpp/utility/variant/visit
+template<class... Ts>
+struct overloaded : public Ts...
+{
+ using Ts::operator()...;
+};
+
+// explicit deduction guide
+template<class... Ts>
+overloaded(Ts...)->overloaded<Ts...>;
+
+template<class OutputContainer>
+void testWhenAllDifferentTypes()
+{
+ QPromise<int> pInt1;
+ QPromise<int> pInt2;
+ QPromise<void> pVoid;
+
+ using Futures = std::variant<QFuture<int>, QFuture<int>, QFuture<void>>;
+
+ QFuture<OutputContainer> whenAll;
+ if constexpr (std::is_same_v<QList<Futures>, OutputContainer>) {
+ whenAll = QtFuture::whenAll(pInt1.future(), pInt2.future(), pVoid.future());
+ } else {
+ whenAll =
+ QtFuture::whenAll<OutputContainer>(pInt1.future(), pInt2.future(), pVoid.future());
+ }
+
+ int sumOfInts = 0;
+ whenAll.then([&](const OutputContainer &results) {
+ for (auto future : results) {
+ std::visit(overloaded {
+ [&](const QFuture<int> &f) {
+ QVERIFY(f.isFinished());
+ sumOfInts += f.result();
+ },
+ [](const QFuture<void> &f) { QVERIFY(f.isFinished()); },
+ },
+ future);
+ }
+ });
+
+ pVoid.start();
+ pVoid.finish();
+ QVERIFY(whenAll.isRunning());
+
+ pInt2.start();
+ pInt2.addResult(2);
+ pInt2.finish();
+ QVERIFY(whenAll.isRunning());
+ QCOMPARE(sumOfInts, 0);
+
+ pInt1.start();
+ pInt1.addResult(1);
+ pInt1.finish();
+ QVERIFY(!whenAll.isRunning());
+ QCOMPARE(sumOfInts, 3);
+}
+
+void tst_QFuture::whenAllDifferentTypes()
+{
+ using Futures = std::variant<QFuture<int>, QFuture<int>, QFuture<void>>;
+ testWhenAllDifferentTypes<QList<Futures>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllDifferentTypes() with QList failed!");
+
+ testWhenAllDifferentTypes<std::vector<Futures>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllDifferentTypes() with std::vector failed!");
+
+ testWhenAllDifferentTypes<QVarLengthArray<Futures>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllDifferentTypes() with QVarLengthArray failed!");
+}
+
+void tst_QFuture::whenAllDifferentTypesWithCanceled()
+{
+ QPromise<int> pInt;
+ QPromise<QString> pString;
+
+ const QString someValue = u"some value"_s;
+
+ bool finished = false;
+ using Futures = std::variant<QFuture<int>, QFuture<QString>>;
+ auto whenAll = QtFuture::whenAll(pInt.future(), pString.future())
+ .then([&](const QList<Futures> &results) {
+ finished = true;
+ for (auto future : results) {
+ std::visit(overloaded {
+ [](const QFuture<int> &f) {
+ QVERIFY(f.isFinished());
+ QVERIFY(f.isCanceled());
+ },
+ [&](const QFuture<QString> &f) {
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), someValue);
+ },
+ },
+ future);
+ }
+ });
+
+ pString.start();
+ pString.addResult(someValue);
+ pString.finish();
+ QVERIFY(!finished);
+
+ pInt.start();
+ pInt.future().cancel();
+ pInt.finish();
+ QVERIFY(finished);
+}
+
+void tst_QFuture::whenAllDifferentTypesWithFailed()
+{
+#ifndef QT_NO_EXCEPTIONS
+ QPromise<int> pInt;
+ QPromise<QString> pString;
+
+ const QString someValue = u"some value"_s;
+
+ bool finished = false;
+ using Futures = std::variant<QFuture<int>, QFuture<QString>>;
+ auto whenAll = QtFuture::whenAll(pInt.future(), pString.future())
+ .then([&](const QList<Futures> &results) {
+ finished = true;
+ for (auto future : results) {
+ std::visit(overloaded {
+ [](QFuture<int> f) {
+ QVERIFY(f.isFinished());
+ bool failed = false;
+ // A shorter way of handling the exception
+ f.onFailed([&](const QException &) {
+ failed = true;
+ return -1;
+ });
+ QVERIFY(failed);
+ },
+ [&](const QFuture<QString> &f) {
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), someValue);
+ },
+ },
+ future);
+ }
+ });
+
+ pInt.start();
+ pInt.setException(QException());
+ pInt.finish();
+ QVERIFY(!finished);
+
+ pString.start();
+ pString.addResult(someValue);
+ pString.finish();
+ QVERIFY(finished);
+#else
+ QSKIP("Exceptions are disabled, skipping the test")
+#endif
+}
+
+void tst_QFuture::whenAnyIterators()
+{
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QPromise<int> p2;
+ QList<QFuture<int>> futures = { p0.future(), p1.future(), p2.future() };
+
+ auto whenAny = QtFuture::whenAny(futures.begin(), futures.end());
+ int count = 0;
+ whenAny.then([&](const QtFuture::WhenAnyResult<int> &result) {
+ QCOMPARE(result.index, 1);
+ QCOMPARE(result.future.result(), 1);
+ QVERIFY(!futures[0].isFinished());
+ QVERIFY(futures[1].isFinished());
+ QVERIFY(!futures[2].isFinished());
+ ++count;
+ });
+
+ p0.start();
+ p1.start();
+ p2.start();
+ p0.addResult(0);
+ p1.addResult(1);
+ p2.addResult(2);
+ QVERIFY(!whenAny.isFinished());
+ QCOMPARE(count, 0);
+
+ p1.finish();
+ QVERIFY(whenAny.isFinished());
+ QCOMPARE(count, 1);
+
+ p0.finish();
+ QCOMPARE(count, 1);
+
+ p2.finish();
+ QCOMPARE(count, 1);
+
+ auto whenAnyEmpty = QtFuture::whenAny(futures.end(), futures.end());
+ QVERIFY(whenAnyEmpty.isStarted());
+ QVERIFY(whenAnyEmpty.isFinished());
+ QCOMPARE(whenAnyEmpty.result().index, -1);
+ auto whenAnyEmptyResult = whenAnyEmpty.result().future;
+ QVERIFY(whenAnyEmptyResult.isStarted());
+ QVERIFY(whenAnyEmptyResult.isFinished());
+ QVERIFY(whenAnyEmptyResult.isCanceled());
+}
+
+void tst_QFuture::whenAnyIteratorsWithCanceled()
+{
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QList<QFuture<int>> futures = { p0.future(), p1.future() };
+ int count = 0;
+ auto whenAny = QtFuture::whenAny(futures.begin(), futures.end())
+ .then([&](const QtFuture::WhenAnyResult<int> &result) {
+ QCOMPARE(result.index, 1);
+ QVERIFY(result.future.isCanceled());
+ QVERIFY(!futures[0].isFinished());
+ QVERIFY(futures[1].isFinished());
+ ++count;
+ });
+
+ p1.start();
+ p1.future().cancel();
+ p1.finish();
+ QVERIFY(whenAny.isFinished());
+ QCOMPARE(count, 1);
+
+ p0.start();
+ p0.addResult(0);
+ p0.finish();
+ QCOMPARE(count, 1);
+}
+
+void tst_QFuture::whenAnyIteratorsWithFailed()
+{
+#ifndef QT_NO_EXCEPTIONS
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QList<QFuture<int>> futures = { p0.future(), p1.future() };
+ int count = 0;
+ auto whenAny = QtFuture::whenAny(futures.begin(), futures.end())
+ .then([&](QtFuture::WhenAnyResult<int> result) {
+ QCOMPARE(result.index, 1);
+ QVERIFY(p1.future().isFinished());
+ QVERIFY(!p0.future().isFinished());
+ // A shorter way of handling the exception
+ result.future.onFailed([&](const QException &) {
+ ++count;
+ return 0;
+ });
+ });
+
+ p1.start();
+ p1.setException(QException());
+ p1.finish();
+ QCOMPARE(count, 1);
+
+ p0.start();
+ p0.addResult(0);
+ p0.finish();
+ QCOMPARE(count, 1);
+#else
+ QSKIP("Exceptions are disabled, skipping the test")
+#endif
+}
+
+void tst_QFuture::whenAnyDifferentTypes()
+{
+ QPromise<int> pInt1;
+ QPromise<int> pInt2;
+ QPromise<void> pVoid;
+
+ auto whenAny = QtFuture::whenAny(pInt1.future(), pInt2.future(), pVoid.future());
+ int count = 0;
+ whenAny.then([&](const std::variant<QFuture<int>, QFuture<int>, QFuture<void>> &result) {
+ QCOMPARE(result.index(), 1u);
+ std::visit(overloaded { [&](const QFuture<int> &future) {
+ QVERIFY(future.isFinished());
+ QCOMPARE(future.result(), 2);
+ ++count;
+ },
+ [](auto) { QFAIL("The wrong future completed."); }
+ },
+ result);
+ });
+
+ pInt2.start();
+ pInt1.start();
+ pVoid.start();
+ pInt1.addResult(1);
+ pInt2.addResult(2);
+
+ QVERIFY(!whenAny.isFinished());
+ QCOMPARE(count, 0);
+
+ pInt2.finish();
+ QVERIFY(whenAny.isFinished());
+ QCOMPARE(count, 1);
+
+ pInt1.finish();
+ QCOMPARE(count, 1);
+
+ pVoid.finish();
+ QCOMPARE(count, 1);
+}
+
+void tst_QFuture::whenAnyDifferentTypesWithCanceled()
+{
+ QPromise<int> pInt;
+ QPromise<void> pVoid;
+
+ int count = 0;
+ auto whenAny = QtFuture::whenAny(pInt.future(), pVoid.future())
+ .then([&](const std::variant<QFuture<int>, QFuture<void>> &result) {
+ QCOMPARE(result.index(), 0u);
+ std::visit(overloaded { [&](const QFuture<int> &future) {
+ QVERIFY(future.isFinished());
+ QVERIFY(future.isCanceled());
+ ++count;
+ },
+ [](auto) {
+ QFAIL("The wrong future completed.");
+ }
+ },
+ result);
+ });
+
+ pInt.start();
+ pInt.future().cancel();
+ pInt.finish();
+ QCOMPARE(count, 1);
+
+ pVoid.start();
+ pVoid.finish();
+ QCOMPARE(count, 1);
+}
+
+void tst_QFuture::whenAnyDifferentTypesWithFailed()
+{
+#ifndef QT_NO_EXCEPTIONS
+ QPromise<int> pInt;
+ QPromise<void> pVoid;
+
+ int count = 0;
+ auto whenAny = QtFuture::whenAny(pInt.future(), pVoid.future())
+ .then([&](const std::variant<QFuture<int>, QFuture<void>> &result) {
+ QCOMPARE(result.index(), 0u);
+ std::visit(overloaded { [&](QFuture<int> future) {
+ QVERIFY(future.isFinished());
+ // A shorter way of handling the exception
+ future.onFailed([&](const QException &) {
+ ++count;
+ return -1;
+ });
+ },
+ [](auto) {
+ QFAIL("The wrong future completed.");
+ }
+ },
+ result);
+ });
+
+ pInt.start();
+ pInt.setException(QException());
+ pInt.finish();
+ QCOMPARE(count, 1);
+
+ pVoid.start();
+ pVoid.finish();
+ QCOMPARE(count, 1);
+#else
+ QSKIP("Exceptions are disabled, skipping the test")
+#endif
+}
+
+void tst_QFuture::continuationOverride()
+{
+ QPromise<int> p;
+ bool firstExecuted = false;
+ bool secondExecuted = false;
+
+ QTest::ignoreMessage(QtWarningMsg,
+ "Adding a continuation to a future which already has a continuation. "
+ "The existing continuation is overwritten.");
+
+ QFuture<int> f1 = p.future();
+ f1.then([&firstExecuted](int) {
+ firstExecuted = true;
+ });
+
+ QFuture<int> f2 = p.future();
+ f2.then([&secondExecuted](int) {
+ secondExecuted = true;
+ });
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ QVERIFY(p.future().isFinished());
+ QVERIFY(!firstExecuted);
+ QVERIFY(secondExecuted);
+}
+
+struct InstanceCounter
+{
+ InstanceCounter() { ++count; }
+ InstanceCounter(const InstanceCounter &) { ++count; }
+ ~InstanceCounter() { --count; }
+ static int count;
+};
+int InstanceCounter::count = 0;
+
+void tst_QFuture::continuationsDontLeak()
+{
+ {
+ // QFuture isn't started and isn't finished (has no state)
+ QPromise<InstanceCounter> promise;
+ auto future = promise.future();
+
+ bool continuationIsRun = false;
+ future.then([future, &continuationIsRun](InstanceCounter) { continuationIsRun = true; });
+
+ promise.addResult(InstanceCounter {});
+
+ QVERIFY(!continuationIsRun);
+ }
+ QCOMPARE(InstanceCounter::count, 0);
+
+ {
+ // QFuture is started, but not finished
+ QPromise<InstanceCounter> promise;
+ auto future = promise.future();
+
+ bool continuationIsRun = false;
+ future.then([future, &continuationIsRun](InstanceCounter) { continuationIsRun = true; });
+
+ promise.start();
+ promise.addResult(InstanceCounter {});
+
+ QVERIFY(!continuationIsRun);
+ }
+ QCOMPARE(InstanceCounter::count, 0);
+
+ {
+ // QFuture is started and finished, the continuation is run
+ QPromise<InstanceCounter> promise;
+ auto future = promise.future();
+
+ bool continuationIsRun = false;
+ future.then([future, &continuationIsRun](InstanceCounter) {
+ QVERIFY(future.isFinished());
+ continuationIsRun = true;
+ });
+
+ promise.start();
+ promise.addResult(InstanceCounter {});
+ promise.finish();
+
+ QVERIFY(continuationIsRun);
+ }
+ QCOMPARE(InstanceCounter::count, 0);
+
+ {
+ // QTBUG-116731: Must pass with ASan enabled
+ bool continuationIsRun = false;
+ auto f = QtFuture::makeReadyValueFuture(42);
+ QtFuture::whenAll(f).then([&](auto) { continuationIsRun = true; });
+ QVERIFY(continuationIsRun);
+ }
+
+ {
+ // QTBUG-116731: Must pass with ASan enabled
+ bool continuationIsRun = false;
+ auto f = QtFuture::makeReadyValueFuture(42);
+ QList fs{f};
+ QtFuture::whenAll(fs.begin(), fs.end()).then([&](auto) { continuationIsRun = true; });
+ QVERIFY(continuationIsRun);
+ }
+
+ {
+ // QTBUG-116731: Must pass with ASan enabled
+ bool continuationIsRun = false;
+ auto f = QtFuture::makeReadyValueFuture(42);
+ QtFuture::whenAny(f).then([&](auto) { continuationIsRun = true; });
+ QVERIFY(continuationIsRun);
+ }
+
+ {
+ // QTBUG-116731: Must pass with ASan enabled
+ bool continuationIsRun = false;
+ auto f = QtFuture::makeReadyValueFuture(42);
+ QList fs{f};
+ QtFuture::whenAny(fs.begin(), fs.end()).then([&](auto) { continuationIsRun = true; });
+ QVERIFY(continuationIsRun);
+ }
+}
+
+// This test checks that we do not get use-after-free
+void tst_QFuture::cancelAfterFinishWithContinuations()
+{
+ QFuture<void> future;
+ bool continuationIsRun = false;
+ bool cancelCalled = false;
+ {
+ QPromise<void> promise;
+ future = promise.future();
+
+ future.then([&continuationIsRun]() {
+ continuationIsRun = true;
+ }).onCanceled([&cancelCalled]() {
+ cancelCalled = true;
+ });
+
+ promise.start();
+ promise.finish();
+ }
+
+ QVERIFY(continuationIsRun);
+ future.cancel();
+ QVERIFY(!cancelCalled);
+}
+
+void tst_QFuture::unwrap()
+{
+ // The nested future succeeds
+ {
+ QPromise<int> p;
+ QFuture<QFuture<int>> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ return nested;
+ });
+
+ QFuture<int> unwrapped = f.unwrap();
+ QVERIFY(!unwrapped.isStarted());
+ QVERIFY(!unwrapped.isFinished());
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ unwrapped.waitForFinished();
+
+ QVERIFY(unwrapped.isStarted());
+ QVERIFY(unwrapped.isFinished());
+ QCOMPARE(unwrapped.result(), 43);
+ }
+
+ // The nested future succeeds with multiple results
+ {
+ QPromise<int> p;
+ QFuture<QFuture<int>> f = p.future().then([] (int value) {
+ QPromise<int> nested;
+ nested.start();
+ nested.addResult(++value);
+ nested.addResult(++value);
+ nested.addResult(++value);
+ nested.finish();
+ return nested.future();
+ });
+
+ QFuture<int> unwrapped = f.unwrap();
+ QVERIFY(!unwrapped.isStarted());
+ QVERIFY(!unwrapped.isFinished());
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(unwrapped.isStarted());
+ QVERIFY(unwrapped.isFinished());
+ QCOMPARE(unwrapped.results(), QList<int>() << 43 << 44 << 45);
+ }
+
+ // The chain is canceled, check that unwrap() propagates the cancellation.
+ {
+ QPromise<int> p;
+ QFuture<int> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ return nested;
+ }).unwrap().then([] (int result) {
+ return result;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ p.start();
+ p.future().cancel();
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(f.isStarted());
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), -1);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // The chain has an exception, check that unwrap() propagates it.
+ {
+ QPromise<int> p;
+ QFuture<int> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ return nested;
+ }).unwrap().then([] (int result) {
+ return result;
+ }).onFailed([] (QException &) {
+ return -1;
+ });
+
+ p.start();
+ p.setException(QException());
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(f.isStarted());
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), -1);
+ }
+
+#endif // QT_NO_EXCEPTIONS
+
+ // The nested future is canceled
+ {
+ QPromise<int> p;
+ QFuture<int> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ nested.cancel();
+ return nested;
+ }).unwrap().then([] (int result) {
+ return result;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(f.isStarted());
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), -1);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // The nested future fails with an exception
+ {
+ QPromise<int> p;
+ QFuture<int> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ throw QException();
+ return value + 1;
+ });
+ return nested;
+ }).unwrap().then([] (int result) {
+ return result;
+ }).onFailed([] (QException &) {
+ return -1;
+ });
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(f.isStarted());
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), -1);
+ }
+#endif // QT_NO_EXCEPTIONS
+
+ // Check that continuations are called in the right order
+ {
+ QPromise<void> p;
+
+ std::atomic<bool> firstThenInvoked = false;
+ std::atomic<bool> secondThenInvoked = false;
+ std::atomic<bool> nestedThenInvoked = false;
+ auto f = p.future().then([&] {
+ if (!firstThenInvoked && !secondThenInvoked && !nestedThenInvoked)
+ firstThenInvoked = true;
+ QFuture<void> nested = QtConcurrent::run([&] {
+ QVERIFY(firstThenInvoked);
+ QVERIFY(!nestedThenInvoked);
+ QVERIFY(!secondThenInvoked);
+ nestedThenInvoked = true;
+ });
+ return nested;
+ }).unwrap().then([&] {
+ QVERIFY(firstThenInvoked);
+ QVERIFY(nestedThenInvoked);
+ QVERIFY(!secondThenInvoked);
+ secondThenInvoked = true;
+ });
+
+ QVERIFY(!firstThenInvoked);
+ QVERIFY(!nestedThenInvoked);
+ QVERIFY(!secondThenInvoked);
+
+ p.start();
+ p.finish();
+
+ f.waitForFinished();
+
+ if (QTest::currentTestFailed())
+ return;
+
+ QVERIFY(firstThenInvoked);
+ QVERIFY(nestedThenInvoked);
+ QVERIFY(secondThenInvoked);
+ }
+
+ // Unwrap multiple nested futures
+ {
+ QPromise<int> p;
+ QFuture<QFuture<QFuture<int>>> f = p.future().then([] (int value) {
+ QFuture<QFuture<int>> nested = QtConcurrent::run([value] {
+ QFuture<int> doubleNested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ return doubleNested;
+ });
+ return nested;
+ });
+
+ QFuture<int> unwrapped = f.unwrap();
+ QVERIFY(!unwrapped.isStarted());
+ QVERIFY(!unwrapped.isFinished());
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ unwrapped.waitForFinished();
+
+ QVERIFY(unwrapped.isStarted());
+ QVERIFY(unwrapped.isFinished());
+ QCOMPARE(unwrapped.result(), 43);
+ }
+
+ // Unwrap multiple nested void futures
+ {
+ QPromise<void> p;
+ std::atomic<bool> nestedInvoked = false;
+ std::atomic<bool> doubleNestedInvoked = false;
+ QFuture<QFuture<QFuture<void>>> f = p.future().then([&] {
+ QFuture<QFuture<void>> nested = QtConcurrent::run([&] {
+ QFuture<void> doubleNested = QtConcurrent::run([&] {
+ doubleNestedInvoked = true;
+ });
+ nestedInvoked = true;
+ return doubleNested;
+ });
+ return nested;
+ });
+
+ QFuture<void> unwrapped = f.unwrap();
+ QVERIFY(!nestedInvoked);
+ QVERIFY(!doubleNestedInvoked);
+ QVERIFY(!unwrapped.isStarted());
+ QVERIFY(!unwrapped.isFinished());
+
+ p.start();
+ p.finish();
+
+ unwrapped.waitForFinished();
+
+ QVERIFY(unwrapped.isStarted());
+ QVERIFY(unwrapped.isFinished());
+ QVERIFY(nestedInvoked);
+ QVERIFY(doubleNestedInvoked);
+ }
+}
+
QTEST_MAIN(tst_QFuture)
#include "tst_qfuture.moc"
diff --git a/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt b/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
index 3ebd266084..c0f4561c51 100644
--- a/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qfuturesynchronizer.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfuturesynchronizer Test:
#####################################################################
-qt_add_test(tst_qfuturesynchronizer
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qfuturesynchronizer LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qfuturesynchronizer
SOURCES
tst_qfuturesynchronizer.cpp
)
diff --git a/tests/auto/corelib/thread/qfuturesynchronizer/qfuturesynchronizer.pro b/tests/auto/corelib/thread/qfuturesynchronizer/qfuturesynchronizer.pro
deleted file mode 100644
index 0d20117ed0..0000000000
--- a/tests/auto/corelib/thread/qfuturesynchronizer/qfuturesynchronizer.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qfuturesynchronizer
-QT = core testlib
-SOURCES = tst_qfuturesynchronizer.cpp
diff --git a/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp b/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp
index 2a8a340925..62ad4f872a 100644
--- a/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp
+++ b/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QtCore/qfuturesynchronizer.h>
#include <QtCore/qfuture.h>
@@ -38,6 +13,7 @@ class tst_QFutureSynchronizer : public QObject
private Q_SLOTS:
void construction();
+ void setFutureAliasingExistingMember();
void addFuture();
void cancelOnWait();
void clearFutures();
@@ -58,6 +34,38 @@ void tst_QFutureSynchronizer::construction()
QCOMPARE(synchronizerWithFuture.futures().size(), 1);
}
+void tst_QFutureSynchronizer::setFutureAliasingExistingMember()
+{
+ //
+ // GIVEN: a QFutureSynchronizer with one QFuture:
+ //
+ QFutureSynchronizer synchronizer(QtFuture::makeReadyValueFuture(42));
+
+ //
+ // WHEN: calling setFuture() with an alias of the QFuture already in `synchronizer`:
+ //
+ for (int i = 0; i < 2; ++i) {
+ // The next line triggers -Wdangling-reference, but it's a FP because
+ // of implicit sharing. We cannot keep a copy of synchronizer.futures()
+ // around to avoid the warning, as the extra copy would cause a detach()
+ // of m_futures inside setFuture() with the consequence that `f` no longer
+ // aliases an element in m_futures, which is the goal of this test.
+QT_WARNING_PUSH
+#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 1301
+QT_WARNING_DISABLE_GCC("-Wdangling-reference")
+#endif
+ const auto &f = synchronizer.futures().constFirst();
+QT_WARNING_POP
+ synchronizer.setFuture(f);
+ }
+
+ //
+ // THEN: it didn't crash
+ //
+ QCOMPARE(synchronizer.futures().size(), 1);
+ QCOMPARE(synchronizer.futures().constFirst().result(), 42);
+}
+
void tst_QFutureSynchronizer::addFuture()
{
QFutureSynchronizer<void> synchronizer;
@@ -107,7 +115,7 @@ void tst_QFutureSynchronizer::futures()
synchronizer.addFuture(future);
}
- QCOMPARE(futures, synchronizer.futures());
+ QCOMPARE(futures.size(), synchronizer.futures().size());
}
void tst_QFutureSynchronizer::setFuture()
@@ -122,7 +130,6 @@ void tst_QFutureSynchronizer::setFuture()
QFuture<void> future;
synchronizer.setFuture(future);
QCOMPARE(synchronizer.futures().size(), 1);
- QCOMPARE(synchronizer.futures().first(), future);
}
void tst_QFutureSynchronizer::waitForFinished()
diff --git a/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt b/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt
index a6771e538e..65417199a3 100644
--- a/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt
@@ -1,13 +1,20 @@
-# Generated from qfuturewatcher.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfuturewatcher Test:
#####################################################################
-qt_add_test(tst_qfuturewatcher
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qfuturewatcher LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qfuturewatcher
SOURCES
tst_qfuturewatcher.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Concurrent
Qt::CorePrivate
)
diff --git a/tests/auto/corelib/thread/qfuturewatcher/qfuturewatcher.pro b/tests/auto/corelib/thread/qfuturewatcher/qfuturewatcher.pro
deleted file mode 100644
index d0e8b4c982..0000000000
--- a/tests/auto/corelib/thread/qfuturewatcher/qfuturewatcher.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qfuturewatcher
-QT = core core-private testlib concurrent
-SOURCES = tst_qfuturewatcher.cpp
diff --git a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
index 027bd1313e..40aa89ded4 100644
--- a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
+++ b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
@@ -1,41 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QDebug>
#include <QElapsedTimer>
-#include <QtTest/QtTest>
+#include <QSignalSpy>
#include <QtConcurrent>
#include <private/qfutureinterface_p.h>
using namespace QtConcurrent;
+using namespace std::chrono_literals;
-#include <QtTest/QtTest>
+#include <QTest>
//#define PRINT
@@ -46,8 +22,11 @@ private slots:
void startFinish();
void progressValueChanged();
void canceled();
+ void cancelAndFinish_data();
+ void cancelAndFinish();
void resultAt();
void resultReadyAt();
+ void orderedResultReadyAt();
void futureSignals();
void watchFinishedFuture();
void watchCanceledFuture();
@@ -64,13 +43,13 @@ private slots:
void suspendEvents();
void suspended();
void suspendedEventsOrder();
- void finishedState();
void throttling();
void incrementalMapResults();
void incrementalFilterResults();
void qfutureSynchronizer();
void warnRace();
void matchFlags();
+ void checkStateConsistency();
};
void sleeper()
@@ -114,9 +93,9 @@ void mapSleeper(int &)
QTest::qSleep(100);
}
-QSet<int> progressValues;
-QSet<QString> progressTexts;
-QMutex mutex;
+static QSet<int> progressValues;
+static QSet<QString> progressTexts;
+static QMutex mutex;
class ProgressObject : public QObject
{
Q_OBJECT
@@ -191,7 +170,7 @@ class CancelObject : public QObject
Q_OBJECT
public:
bool wasCanceled;
- CancelObject() : wasCanceled(false) {};
+ CancelObject() : wasCanceled(false) {}
public slots:
void cancel();
};
@@ -229,12 +208,48 @@ void tst_QFutureWatcher::canceled()
future.waitForFinished();
}
-class IntTask : public RunFunctionTask<int>
+void tst_QFutureWatcher::cancelAndFinish_data()
+{
+ QTest::addColumn<bool>("isCanceled");
+ QTest::addColumn<bool>("isFinished");
+
+ QTest::addRow("running") << false << false;
+ QTest::addRow("canceled") << true << false;
+ QTest::addRow("finished") << false << true;
+ QTest::addRow("canceledAndFinished") << true << true;
+}
+
+void tst_QFutureWatcher::cancelAndFinish()
+{
+ QFETCH(bool, isCanceled);
+ QFETCH(bool, isFinished);
+
+ QFutureInterface<void> fi;
+ QFutureWatcher<void> futureWatcher;
+ QSignalSpy finishedSpy(&futureWatcher, &QFutureWatcher<void>::finished);
+ QSignalSpy canceledSpy(&futureWatcher, &QFutureWatcher<void>::canceled);
+ futureWatcher.setFuture(fi.future());
+
+ fi.reportStarted();
+
+ if (isCanceled)
+ fi.cancel();
+ if (isFinished)
+ fi.reportFinished();
+
+ fi.cancelAndFinish();
+
+ // The signals should be emitted only once
+ QTRY_COMPARE(canceledSpy.size(), 1);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+}
+
+class IntTask : public RunFunctionTaskBase<int>
{
public:
void runFunctor() override
{
- result = 10;
+ promise.reportResult(10);
}
};
@@ -266,6 +281,28 @@ void tst_QFutureWatcher::resultReadyAt()
QVERIFY(resultSpy.wait());
}
+void tst_QFutureWatcher::orderedResultReadyAt()
+{
+ for (int i = 0; i < 1000; ++i) {
+ QObject context;
+ QFuture<QString> f = run([](QPromise<QString> &fi) {
+ fi.addResult("First");
+ fi.addResult("Second");
+ });
+ QList<int> actualIndices;
+
+ QFutureWatcher<QString> watcher;
+ connect(&watcher, &QFutureWatcherBase::resultReadyAt, &context,
+ [&actualIndices](int index) { actualIndices.append(index); });
+ watcher.setFuture(f);
+ f.waitForFinished();
+ QCoreApplication::processEvents();
+ const QList<int> expectedIndices{0, 1};
+ QCOMPARE(actualIndices.size(), expectedIndices.size());
+ QCOMPARE(actualIndices, expectedIndices);
+ }
+}
+
class SignalSlotObject : public QObject
{
Q_OBJECT
@@ -336,21 +373,21 @@ void tst_QFutureWatcher::futureSignals()
const int progress = 1;
a.setProgressValue(progress);
- QTRY_COMPARE(progressSpy.count(), 2);
+ QTRY_COMPARE(progressSpy.size(), 2);
QCOMPARE(progressSpy.takeFirst().at(0).toInt(), 0);
QCOMPARE(progressSpy.takeFirst().at(0).toInt(), 1);
const int result = 10;
a.reportResult(&result);
QVERIFY(resultReadySpy.wait());
- QCOMPARE(resultReadySpy.count(), 1);
+ QCOMPARE(resultReadySpy.size(), 1);
a.reportFinished(&result);
- QTRY_COMPARE(resultReadySpy.count(), 2);
+ QTRY_COMPARE(resultReadySpy.size(), 2);
QCOMPARE(resultReadySpy.takeFirst().at(0).toInt(), 0); // check the index
QCOMPARE(resultReadySpy.takeFirst().at(0).toInt(), 1);
- QCOMPARE(finishedSpy.count(), 1);
+ QCOMPARE(finishedSpy.size(), 1);
}
}
@@ -389,10 +426,10 @@ void tst_QFutureWatcher::watchFinishedFuture()
watcher.setFuture(f);
QVERIFY(finishedSpy.wait());
- QCOMPARE(startedSpy.count(), 1);
- QCOMPARE(finishedSpy.count(), 1);
- QCOMPARE(resultReadySpy.count(), 1);
- QCOMPARE(canceledSpy.count(), 0);
+ QCOMPARE(startedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 1);
+ QCOMPARE(resultReadySpy.size(), 1);
+ QCOMPARE(canceledSpy.size(), 0);
}
void tst_QFutureWatcher::watchCanceledFuture()
@@ -423,10 +460,10 @@ void tst_QFutureWatcher::watchCanceledFuture()
watcher.setFuture(f);
QVERIFY(finishedSpy.wait());
- QCOMPARE(startedSpy.count(), 1);
- QCOMPARE(finishedSpy.count(), 1);
- QCOMPARE(resultReadySpy.count(), 0);
- QCOMPARE(canceledSpy.count(), 1);
+ QCOMPARE(startedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 1);
+ QCOMPARE(resultReadySpy.size(), 0);
+ QCOMPARE(canceledSpy.size(), 1);
}
void tst_QFutureWatcher::disconnectRunningFuture()
@@ -449,21 +486,21 @@ void tst_QFutureWatcher::disconnectRunningFuture()
const int result = 10;
a.reportResult(&result);
QVERIFY(resultReadySpy.wait());
- QCOMPARE(resultReadySpy.count(), 1);
+ QCOMPARE(resultReadySpy.size(), 1);
delete watcher;
a.reportResult(&result);
QTest::qWait(10);
- QCOMPARE(resultReadySpy.count(), 1);
+ QCOMPARE(resultReadySpy.size(), 1);
a.reportFinished(&result);
QTest::qWait(10);
- QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
}
const int maxProgress = 100000;
-class ProgressEmitterTask : public RunFunctionTask<void>
+class ProgressEmitterTask : public RunFunctionTaskBase<void>
{
public:
void runFunctor() override
@@ -487,13 +524,14 @@ void tst_QFutureWatcher::tooMuchProgress()
QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(registerProgress(int)));
f.setFuture((new ProgressEmitterTask())->start());
- QTestEventLoop::instance().enterLoop(5);
+ // Android reports ca. 10k progressValueChanged per second
+ QTestEventLoop::instance().enterLoop(15);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(progressValues.contains(maxProgress));
}
template <typename T>
-class ProgressTextTask : public RunFunctionTask<T>
+class ProgressTextTask : public RunFunctionTaskBase<T>
{
public:
void runFunctor() override
@@ -658,14 +696,14 @@ void tst_QFutureWatcher::changeFuture()
watcher.setFuture(b); // But oh no! we're switching to another future
QTest::qWait(10); // before the event gets delivered.
- QCOMPARE(resultReadySpy.count(), 0);
+ QCOMPARE(resultReadySpy.size(), 0);
watcher.setFuture(a);
watcher.setFuture(b);
watcher.setFuture(a); // setting it back gets us one event, not two.
QVERIFY(resultReadySpy.wait());
- QCOMPARE(resultReadySpy.count(), 1);
+ QCOMPARE(resultReadySpy.size(), 1);
}
// Test that events aren't delivered from canceled futures
@@ -693,7 +731,7 @@ void tst_QFutureWatcher::cancelEvents()
QVERIFY(finishedSpy.wait());
- QCOMPARE(resultReadySpy.count(), 0);
+ QCOMPARE(resultReadySpy.size(), 0);
}
#if QT_DEPRECATED_SINCE(6, 0)
@@ -720,14 +758,14 @@ void tst_QFutureWatcher::pauseEvents()
watcher.setFuture(iface.future());
watcher.pause();
- QTRY_COMPARE(pauseSpy.count(), 1);
+ QTRY_COMPARE(pauseSpy.size(), 1);
int value = 0;
iface.reportFinished(&value);
// A result is reported, although the watcher is paused.
// The corresponding event should be also reported.
- QTRY_COMPARE(resultReadySpy.count(), 1);
+ QTRY_COMPARE(resultReadySpy.size(), 1);
watcher.resume();
}
@@ -755,7 +793,7 @@ void tst_QFutureWatcher::pauseEvents()
a.resume(); // should give us no results.
QTest::qWait(10);
- QCOMPARE(resultReadySpy.count(), 0);
+ QCOMPARE(resultReadySpy.size(), 0);
}
}
@@ -775,23 +813,23 @@ void tst_QFutureWatcher::pausedSuspendedOrder()
bool pausedBeforeSuspended = false;
bool notSuspendedBeforePaused = false;
connect(&watcher, &QFutureWatcher<void>::paused,
- [&] { notSuspendedBeforePaused = (suspendedSpy.count() == 0); });
+ [&] { notSuspendedBeforePaused = (suspendedSpy.size() == 0); });
connect(&watcher, &QFutureWatcher<void>::suspended,
- [&] { pausedBeforeSuspended = (pausedSpy.count() == 1); });
+ [&] { pausedBeforeSuspended = (pausedSpy.size() == 1); });
watcher.setFuture(iface.future());
iface.reportSuspended();
// Make sure reportPaused() is ignored if the state is not paused
pausedSpy.wait(100);
- QCOMPARE(pausedSpy.count(), 0);
- QCOMPARE(suspendedSpy.count(), 0);
+ QCOMPARE(pausedSpy.size(), 0);
+ QCOMPARE(suspendedSpy.size(), 0);
iface.setPaused(true);
iface.reportSuspended();
- QTRY_COMPARE(suspendedSpy.count(), 1);
- QCOMPARE(pausedSpy.count(), 1);
+ QTRY_COMPARE(suspendedSpy.size(), 1);
+ QCOMPARE(pausedSpy.size(), 1);
QVERIFY(notSuspendedBeforePaused);
QVERIFY(pausedBeforeSuspended);
@@ -822,14 +860,14 @@ void tst_QFutureWatcher::suspendEvents()
watcher.setFuture(iface.future());
watcher.suspend();
- QTRY_COMPARE(suspendingSpy.count(), 1);
+ QTRY_COMPARE(suspendingSpy.size(), 1);
int value = 0;
iface.reportFinished(&value);
// A result is reported, although the watcher is paused.
// The corresponding event should be also reported.
- QTRY_COMPARE(resultReadySpy.count(), 1);
+ QTRY_COMPARE(resultReadySpy.size(), 1);
watcher.resume();
}
@@ -858,13 +896,13 @@ void tst_QFutureWatcher::suspendEvents()
a.resume(); // should give us no results.
QTest::qWait(10);
- QCOMPARE(resultReadySpy.count(), 0);
+ QCOMPARE(resultReadySpy.size(), 0);
}
}
void tst_QFutureWatcher::suspended()
{
- QFutureWatcher<void> watcher;
+ QFutureWatcher<int> watcher;
QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
#if QT_DEPRECATED_SINCE(6, 0)
QT_WARNING_PUSH
@@ -886,39 +924,39 @@ QT_WARNING_POP
QFuture<int> future = QtConcurrent::mapped(&pool, values, [&](int value) {
++count;
// Sleep, to make sure not all threads will start at once.
- QThread::msleep(50);
+ QThread::sleep(50ms);
return value;
});
watcher.setFuture(future);
// Allow some threads to start before suspending.
- QThread::msleep(200);
+ QThread::sleep(200ms);
watcher.suspend();
watcher.suspend();
- QTRY_COMPARE(suspendedSpy.count(), 1); // suspended() should be emitted only once
- QCOMPARE(suspendingSpy.count(), 2); // suspending() is emitted as many times as requested
+ QTRY_COMPARE(suspendedSpy.size(), 1); // suspended() should be emitted only once
+ QCOMPARE(suspendingSpy.size(), 2); // suspending() is emitted as many times as requested
#if QT_DEPRECATED_SINCE(6, 0)
- QCOMPARE(pausedSpy.count(), 2); // paused() is emitted as many times as requested
+ QCOMPARE(pausedSpy.size(), 2); // paused() is emitted as many times as requested
#endif
// Make sure QFutureWatcher::resultReadyAt() is emitted only for already started threads.
- const auto resultReadyAfterPaused = resultReadySpy.count();
+ const auto resultReadyAfterPaused = resultReadySpy.size();
QCOMPARE(resultReadyAfterPaused, count);
// Make sure no more results are reported before resuming.
- QThread::msleep(200);
- QCOMPARE(resultReadyAfterPaused, resultReadySpy.count());
+ QThread::sleep(200ms);
+ QCOMPARE(resultReadyAfterPaused, resultReadySpy.size());
resultReadySpy.clear();
watcher.resume();
- QTRY_COMPARE(finishedSpy.count(), 1);
+ QTRY_COMPARE(finishedSpy.size(), 1);
// Make sure that no more suspended() signals have been emitted.
- QCOMPARE(suspendedSpy.count(), 1);
+ QCOMPARE(suspendedSpy.size(), 1);
// Make sure the rest of results were reported after resume.
- QCOMPARE(resultReadySpy.count(), numValues - resultReadyAfterPaused);
+ QCOMPARE(resultReadySpy.size(), numValues - resultReadyAfterPaused);
}
void tst_QFutureWatcher::suspendedEventsOrder()
@@ -937,53 +975,29 @@ void tst_QFutureWatcher::suspendedEventsOrder()
bool suspendingBeforeSuspended = false;
bool notSuspendedBeforeSuspending = false;
connect(&watcher, &QFutureWatcher<void>::suspending,
- [&] { notSuspendedBeforeSuspending = (suspendedSpy.count() == 0); });
+ [&] { notSuspendedBeforeSuspending = (suspendedSpy.size() == 0); });
connect(&watcher, &QFutureWatcher<void>::suspended,
- [&] { suspendingBeforeSuspended = (suspendingSpy.count() == 1); });
+ [&] { suspendingBeforeSuspended = (suspendingSpy.size() == 1); });
watcher.setFuture(iface.future());
iface.reportSuspended();
// Make sure reportPaused() is ignored if the state is not paused
suspendingSpy.wait(100);
- QCOMPARE(suspendingSpy.count(), 0);
- QCOMPARE(suspendedSpy.count(), 0);
+ QCOMPARE(suspendingSpy.size(), 0);
+ QCOMPARE(suspendedSpy.size(), 0);
iface.setSuspended(true);
iface.reportSuspended();
- QTRY_COMPARE(suspendedSpy.count(), 1);
- QCOMPARE(suspendingSpy.count(), 1);
+ QTRY_COMPARE(suspendedSpy.size(), 1);
+ QCOMPARE(suspendingSpy.size(), 1);
QVERIFY(notSuspendedBeforeSuspending);
QVERIFY(suspendingBeforeSuspended);
iface.reportFinished();
}
-// Test that the finished state for the watcher gets
-// set when the finished event is delivered.
-// This means it will lag the finished state for the future,
-// but makes it more useful.
-void tst_QFutureWatcher::finishedState()
-{
- QFutureInterface<int> iface;
- iface.reportStarted();
- QFuture<int> future = iface.future();
- QFutureWatcher<int> watcher;
- QSignalSpy startedSpy(&watcher, &QFutureWatcher<int>::started);
- QSignalSpy finishedSpy(&watcher, &QFutureWatcher<int>::finished);
-
- watcher.setFuture(future);
- QVERIFY(startedSpy.wait());
-
- iface.reportFinished();
- QVERIFY(future.isFinished());
- QVERIFY(!watcher.isFinished());
-
- QVERIFY(finishedSpy.wait());
- QVERIFY(watcher.isFinished());
-}
-
/*
Verify that throttling kicks in if you report a lot of results,
and that it clears when the result events are processed.
@@ -1007,7 +1021,7 @@ void tst_QFutureWatcher::throttling()
QVERIFY(iface.isThrottled());
- QTRY_COMPARE(resultSpy.count(), resultCount); // Process the results
+ QTRY_COMPARE(resultSpy.size(), resultCount); // Process the results
QVERIFY(!iface.isThrottled());
@@ -1149,7 +1163,7 @@ public:
void tst_QFutureWatcher::warnRace()
{
-#ifndef Q_OS_MAC //I don't know why it is not working on mac
+#ifndef Q_OS_DARWIN // I don't know why it is not working on mac
#ifndef QT_NO_DEBUG
QTest::ignoreMessage(QtWarningMsg, "QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race");
#endif
@@ -1178,6 +1192,55 @@ void tst_QFutureWatcher::matchFlags()
QCOMPARE(watcher.isFinished(), future.isFinished());
}
+void tst_QFutureWatcher::checkStateConsistency()
+{
+#define CHECK_FAIL(state) \
+ do { \
+ if (QTest::currentTestFailed()) \
+ QFAIL("checkState() failed, QFutureWatcher has inconistent state after " state "!"); \
+ } while (false)
+
+ QFutureWatcher<void> futureWatcher;
+
+ auto checkState = [&futureWatcher] {
+ QCOMPARE(futureWatcher.isStarted(), futureWatcher.future().isStarted());
+ QCOMPARE(futureWatcher.isRunning(), futureWatcher.future().isRunning());
+ QCOMPARE(futureWatcher.isCanceled(), futureWatcher.future().isCanceled());
+ QCOMPARE(futureWatcher.isSuspended(), futureWatcher.future().isSuspended());
+ QCOMPARE(futureWatcher.isSuspending(), futureWatcher.future().isSuspending());
+ QCOMPARE(futureWatcher.isFinished(), futureWatcher.future().isFinished());
+ };
+
+ checkState();
+ CHECK_FAIL("default-constructing");
+
+ QFutureInterface<void> fi;
+ futureWatcher.setFuture(fi.future());
+ checkState();
+ CHECK_FAIL("setting future");
+
+ fi.reportStarted();
+ checkState();
+ CHECK_FAIL("starting");
+
+ fi.future().suspend();
+ checkState();
+ CHECK_FAIL("suspending");
+
+ fi.reportSuspended();
+ checkState();
+ CHECK_FAIL("suspended");
+
+ fi.reportCanceled();
+ checkState();
+ CHECK_FAIL("canceling");
+
+ fi.reportFinished();
+ checkState();
+ CHECK_FAIL("finishing");
+
+#undef CHECK_FAIL
+}
QTEST_MAIN(tst_QFutureWatcher)
#include "tst_qfuturewatcher.moc"
diff --git a/tests/auto/corelib/thread/qmutex/CMakeLists.txt b/tests/auto/corelib/thread/qmutex/CMakeLists.txt
index ee7bcde85f..5a92a2bffd 100644
--- a/tests/auto/corelib/thread/qmutex/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qmutex/CMakeLists.txt
@@ -1,18 +1,19 @@
-# Generated from qmutex.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmutex Test:
#####################################################################
-qt_add_test(tst_qmutex
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmutex LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qmutex
SOURCES
tst_qmutex.cpp
-)
-
-## Scopes:
-#####################################################################
-
-qt_extend_target(tst_qmutex CONDITION WIN32
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
)
diff --git a/tests/auto/corelib/thread/qmutex/qmutex.pro b/tests/auto/corelib/thread/qmutex/qmutex.pro
deleted file mode 100644
index cb9d364b71..0000000000
--- a/tests/auto/corelib/thread/qmutex/qmutex.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qmutex
-QT = core testlib
-SOURCES = tst_qmutex.cpp
-win32:QT += core-private
diff --git a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
index 51e46f87ea..4753444ab9 100644
--- a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
+++ b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
@@ -1,40 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QSemaphore>
#include <qatomic.h>
#include <qcoreapplication.h>
#include <qelapsedtimer.h>
#include <qmutex.h>
#include <qthread.h>
+#include <qvarlengtharray.h>
#include <qwaitcondition.h>
+#include <private/qvolatile_p.h>
+
+using namespace std::chrono_literals;
class tst_QMutex : public QObject
{
@@ -46,11 +26,9 @@ public:
Milliseconds,
Seconds,
};
- Q_ENUM(TimeUnit);
+ Q_ENUM(TimeUnit)
private slots:
- void convertToMilliseconds_data();
- void convertToMilliseconds();
void tryLock_non_recursive();
void try_lock_for_non_recursive();
void try_lock_until_non_recursive();
@@ -68,11 +46,11 @@ private slots:
static const int iterations = 100;
-QAtomicInt lockCount(0);
-QMutex normalMutex;
-QRecursiveMutex recursiveMutex;
-QSemaphore testsTurn;
-QSemaphore threadsTurn;
+static QAtomicInt lockCount(0);
+static QMutex normalMutex;
+static QRecursiveMutex recursiveMutex;
+static QSemaphore testsTurn;
+static QSemaphore threadsTurn;
/*
Depending on the OS, tryWaits may return early than expected because of the
@@ -83,121 +61,15 @@ QSemaphore threadsTurn;
enum {
#ifdef Q_OS_WIN
systemTimersResolution = 16,
+#elif defined(Q_OS_QNX)
+ systemTimersResolution = 10,
#else
systemTimersResolution = 1,
#endif
waitTime = 100
};
-#if __has_include(<chrono>)
static constexpr std::chrono::milliseconds waitTimeAsDuration(waitTime);
-#endif
-
-void tst_QMutex::convertToMilliseconds_data()
-{
- QTest::addColumn<TimeUnit>("unit");
- QTest::addColumn<double>("doubleValue");
- QTest::addColumn<qint64>("intValue");
- QTest::addColumn<qint64>("expected");
-
-#if !__has_include(<chrono>)
- QSKIP("This test requires <chrono>");
-#endif
-
- auto add = [](TimeUnit unit, double d, long long i, qint64 expected) {
- const QScopedArrayPointer<char> enumName(QTest::toString(unit));
- QTest::addRow("%s:%f:%lld", enumName.data(), d, i)
- << unit << d << qint64(i) << expected;
- };
-
- auto forAllUnitsAdd = [=](double d, long long i, qint64 expected) {
- for (auto unit : {TimeUnit::Nanoseconds, TimeUnit::Microseconds, TimeUnit::Milliseconds, TimeUnit::Seconds})
- add(unit, d, i, expected);
- };
-
- forAllUnitsAdd(-0.5, -1, 0); // all negative values result in 0
-
- forAllUnitsAdd(0, 0, 0);
-
- add(TimeUnit::Nanoseconds, 1, 1, 1);
- add(TimeUnit::Nanoseconds, 1000 * 1000, 1000 * 1000, 1);
- add(TimeUnit::Nanoseconds, 1000 * 1000 + 0.5, 1000 * 1000 + 1, 2);
-
- add(TimeUnit::Microseconds, 1, 1, 1);
- add(TimeUnit::Microseconds, 1000, 1000, 1);
- add(TimeUnit::Microseconds, 1000 + 0.5, 1000 + 1, 2);
-
- add(TimeUnit::Milliseconds, 1, 1, 1);
- add(TimeUnit::Milliseconds, 1.5, 2, 2);
-
- add(TimeUnit::Seconds, 0.9991, 1, 1000);
-
- //
- // overflowing int results in INT_MAX (equivalent to a spurious wakeup after ~24 days); check it:
- //
-
- // spot on:
- add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000, INT_MAX * Q_INT64_C(1000) * 1000, INT_MAX);
- add(TimeUnit::Microseconds, INT_MAX * 1000., INT_MAX * Q_INT64_C(1000), INT_MAX);
- add(TimeUnit::Milliseconds, INT_MAX, INT_MAX, INT_MAX);
-
- // minimally above:
- add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 + 1, INT_MAX * Q_INT64_C(1000) * 1000 + 1, INT_MAX);
- add(TimeUnit::Microseconds, INT_MAX * 1000. + 1, INT_MAX * Q_INT64_C(1000) + 1, INT_MAX);
- add(TimeUnit::Milliseconds, INT_MAX + 1., INT_MAX + Q_INT64_C(1), INT_MAX);
- add(TimeUnit::Seconds, INT_MAX / 1000. + 1, INT_MAX / 1000 + 1, INT_MAX);
-
- // minimally below:
- add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 - 1, INT_MAX * Q_INT64_C(1000) * 1000 - 1, INT_MAX);
- add(TimeUnit::Microseconds, INT_MAX * 1000. - 1, INT_MAX * Q_INT64_C(1000) - 1, INT_MAX);
- add(TimeUnit::Milliseconds, INT_MAX - 0.1, INT_MAX , INT_MAX);
-
-}
-
-void tst_QMutex::convertToMilliseconds()
-{
-#if !__has_include(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
- QFETCH(TimeUnit, unit);
- QFETCH(double, doubleValue);
- QFETCH(qint64, intValue);
- QFETCH(qint64, expected);
-
- constexpr qint64 maxShort = std::numeric_limits<short>::max();
- constexpr qint64 maxInt = std::numeric_limits<int>::max();
- constexpr qint64 maxUInt = std::numeric_limits<uint>::max();
-
- switch (unit) {
-#define CASE(Unit, Period) \
- case TimeUnit::Unit: \
- DO(double, Period, doubleValue); \
- if (intValue < maxShort) \
- DO(short, Period, short(intValue)); \
- if (intValue < maxInt) \
- DO(int, Period, int(intValue)); \
- DO(qint64, Period, intValue); \
- if (intValue >= 0) { \
- if (intValue < maxUInt) \
- DO(uint, Period, uint(intValue)); \
- DO(quint64, Period, quint64(intValue)); \
- } \
- break
-#define DO(Rep, Period, val) \
- do { \
- const std::chrono::duration<Rep, Period> wait((val)); \
- QCOMPARE(QMutex::convertToMilliseconds(wait), expected); \
- } while (0)
-
- CASE(Nanoseconds, std::nano);
- CASE(Microseconds, std::micro);
- CASE(Milliseconds, std::milli);
- CASE(Seconds, std::ratio<1>);
-#undef DO
-#undef CASE
- }
-#endif
-}
void tst_QMutex::tryLock_non_recursive()
{
@@ -227,19 +99,19 @@ void tst_QMutex::tryLock_non_recursive()
QElapsedTimer timer;
timer.start();
QVERIFY(!normalMutex.tryLock(waitTime));
- QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
+ QCOMPARE_GE(timer.elapsed(), waitTime - systemTimersResolution);
testsTurn.release();
// TEST 4: thread can acquire lock, timeout = waitTime
threadsTurn.acquire();
timer.start();
QVERIFY(normalMutex.tryLock(waitTime));
- QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution);
+ QCOMPARE_LE(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
timer.start();
// it's non-recursive, so the following lock needs to fail
QVERIFY(!normalMutex.tryLock(waitTime));
- QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
+ QCOMPARE_GE(timer.elapsed(), waitTime - systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
normalMutex.unlock();
testsTurn.release();
@@ -253,7 +125,7 @@ void tst_QMutex::tryLock_non_recursive()
threadsTurn.acquire();
timer.start();
QVERIFY(normalMutex.tryLock(0));
- QVERIFY(timer.elapsed() < waitTime + systemTimersResolution);
+ QCOMPARE_LT(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(!normalMutex.tryLock(0));
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
@@ -264,7 +136,7 @@ void tst_QMutex::tryLock_non_recursive()
threadsTurn.acquire();
timer.start();
QVERIFY(normalMutex.tryLock(3000));
- QVERIFY(timer.elapsed() < 3000 + systemTimersResolution);
+ QCOMPARE_LT(timer.elapsed(), 3000 + systemTimersResolution);
normalMutex.unlock();
testsTurn.release();
@@ -275,47 +147,47 @@ void tst_QMutex::tryLock_non_recursive()
Thread thread;
thread.start();
- // TEST 1: thread can't acquire lock
+ qDebug("TEST 1: thread can't acquire lock");
testsTurn.acquire();
normalMutex.lock();
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
threadsTurn.release();
- // TEST 2: thread can acquire lock
+ qDebug("TEST 2: thread can acquire lock");
testsTurn.acquire();
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
normalMutex.unlock();
threadsTurn.release();
- // TEST 3: thread can't acquire lock, timeout = waitTime
+ qDebug("TEST 3: thread can't acquire lock, timeout = waitTime");
testsTurn.acquire();
normalMutex.lock();
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
threadsTurn.release();
- // TEST 4: thread can acquire lock, timeout = waitTime
+ qDebug("TEST 4: thread can acquire lock, timeout = waitTime");
testsTurn.acquire();
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
normalMutex.unlock();
threadsTurn.release();
- // TEST 5: thread can't acquire lock, timeout = 0
+ qDebug("TEST 5: thread can't acquire lock, timeout = 0");
testsTurn.acquire();
normalMutex.lock();
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
threadsTurn.release();
- // TEST 6: thread can acquire lock, timeout = 0
+ qDebug("TEST 6: thread can acquire lock, timeout = 0");
testsTurn.acquire();
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
normalMutex.unlock();
threadsTurn.release();
- // TEST 7: thread can acquire lock, timeout = 3000 (QTBUG-24795)
+ qDebug("TEST 7: thread can acquire lock, timeout = 3000 (QTBUG-24795)");
testsTurn.acquire();
normalMutex.lock();
threadsTurn.release();
- QThread::msleep(100);
+ QThread::sleep(100ms);
normalMutex.unlock();
// wait for thread to finish
@@ -324,10 +196,8 @@ void tst_QMutex::tryLock_non_recursive()
thread.wait();
}
-void tst_QMutex::try_lock_for_non_recursive() {
-#if !__has_include(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
+void tst_QMutex::try_lock_for_non_recursive()
+{
class Thread : public QThread
{
public:
@@ -354,19 +224,19 @@ void tst_QMutex::try_lock_for_non_recursive() {
QElapsedTimer timer;
timer.start();
QVERIFY(!normalMutex.try_lock_for(waitTimeAsDuration));
- QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
+ QCOMPARE_GE(timer.elapsed(), waitTime - systemTimersResolution);
testsTurn.release();
// TEST 4: thread can acquire lock, timeout = waitTime
threadsTurn.acquire();
timer.start();
QVERIFY(normalMutex.try_lock_for(waitTimeAsDuration));
- QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution);
+ QCOMPARE_LE(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
timer.start();
// it's non-recursive, so the following lock needs to fail
QVERIFY(!normalMutex.try_lock_for(waitTimeAsDuration));
- QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
+ QCOMPARE_GE(timer.elapsed(), waitTime - systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
normalMutex.unlock();
testsTurn.release();
@@ -380,7 +250,7 @@ void tst_QMutex::try_lock_for_non_recursive() {
threadsTurn.acquire();
timer.start();
QVERIFY(normalMutex.try_lock_for(std::chrono::milliseconds::zero()));
- QVERIFY(timer.elapsed() < waitTime + systemTimersResolution);
+ QCOMPARE_LT(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(!normalMutex.try_lock_for(std::chrono::milliseconds::zero()));
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
@@ -391,7 +261,7 @@ void tst_QMutex::try_lock_for_non_recursive() {
threadsTurn.acquire();
timer.start();
QVERIFY(normalMutex.try_lock_for(std::chrono::milliseconds(3000)));
- QVERIFY(timer.elapsed() < 3000 + systemTimersResolution);
+ QCOMPARE_LT(timer.elapsed(), 3000 + systemTimersResolution);
normalMutex.unlock();
testsTurn.release();
@@ -442,21 +312,17 @@ void tst_QMutex::try_lock_for_non_recursive() {
testsTurn.acquire();
normalMutex.lock();
threadsTurn.release();
- QThread::msleep(100);
+ QThread::sleep(100ms);
normalMutex.unlock();
// wait for thread to finish
testsTurn.acquire();
threadsTurn.release();
thread.wait();
-#endif
}
void tst_QMutex::try_lock_until_non_recursive()
{
-#if !__has_include(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
class Thread : public QThread
{
public:
@@ -483,19 +349,19 @@ void tst_QMutex::try_lock_until_non_recursive()
threadsTurn.acquire();
auto endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(!normalMutex.try_lock_until(endTimePoint));
- QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration);
+ QCOMPARE_GE(std::chrono::steady_clock::now(), endTimePoint - systemTimersResolutionAsDuration);
testsTurn.release();
// TEST 4: thread can acquire lock, timeout = waitTime
threadsTurn.acquire();
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(normalMutex.try_lock_until(endTimePoint));
- QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration);
+ QCOMPARE_LE(std::chrono::steady_clock::now(), endTimePoint + systemTimersResolutionAsDuration);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
// it's non-recursive, so the following lock needs to fail
QVERIFY(!normalMutex.try_lock_until(endTimePoint));
- QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration);
+ QCOMPARE_GE(std::chrono::steady_clock::now(), endTimePoint - systemTimersResolutionAsDuration);
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
normalMutex.unlock();
testsTurn.release();
@@ -509,7 +375,7 @@ void tst_QMutex::try_lock_until_non_recursive()
threadsTurn.acquire();
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(normalMutex.try_lock_until(std::chrono::steady_clock::now()));
- QVERIFY(std::chrono::steady_clock::now() < endTimePoint + systemTimersResolutionAsDuration);
+ QCOMPARE_LT(std::chrono::steady_clock::now(), endTimePoint + systemTimersResolutionAsDuration);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(!normalMutex.try_lock_until(std::chrono::steady_clock::now()));
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
@@ -520,7 +386,7 @@ void tst_QMutex::try_lock_until_non_recursive()
threadsTurn.acquire();
endTimePoint = std::chrono::steady_clock::now() + std::chrono::milliseconds(3000);
QVERIFY(normalMutex.try_lock_until(endTimePoint));
- QVERIFY(std::chrono::steady_clock::now() < endTimePoint + systemTimersResolutionAsDuration);
+ QCOMPARE_LT(std::chrono::steady_clock::now(), endTimePoint + systemTimersResolutionAsDuration);
normalMutex.unlock();
testsTurn.release();
@@ -571,14 +437,13 @@ void tst_QMutex::try_lock_until_non_recursive()
testsTurn.acquire();
normalMutex.lock();
threadsTurn.release();
- QThread::msleep(100);
+ QThread::sleep(100ms);
normalMutex.unlock();
// wait for thread to finish
testsTurn.acquire();
threadsTurn.release();
thread.wait();
-#endif
}
void tst_QMutex::tryLock_recursive()
@@ -609,14 +474,14 @@ void tst_QMutex::tryLock_recursive()
QElapsedTimer timer;
timer.start();
QVERIFY(!recursiveMutex.tryLock(waitTime));
- QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
+ QCOMPARE_GE(timer.elapsed(), waitTime - systemTimersResolution);
QVERIFY(!recursiveMutex.tryLock(0));
testsTurn.release();
threadsTurn.acquire();
timer.start();
QVERIFY(recursiveMutex.tryLock(waitTime));
- QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution);
+ QCOMPARE_LE(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(recursiveMutex.tryLock(waitTime));
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
@@ -634,7 +499,7 @@ void tst_QMutex::tryLock_recursive()
threadsTurn.acquire();
timer.start();
QVERIFY(recursiveMutex.tryLock(0));
- QVERIFY(timer.elapsed() < waitTime + systemTimersResolution);
+ QCOMPARE_LT(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(recursiveMutex.tryLock(0));
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
@@ -707,9 +572,6 @@ void tst_QMutex::tryLock_recursive()
void tst_QMutex::try_lock_for_recursive()
{
-#if !__has_include(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
class Thread : public QThread
{
public:
@@ -736,14 +598,14 @@ void tst_QMutex::try_lock_for_recursive()
QElapsedTimer timer;
timer.start();
QVERIFY(!recursiveMutex.try_lock_for(waitTimeAsDuration));
- QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
+ QCOMPARE_GE(timer.elapsed(), waitTime - systemTimersResolution);
QVERIFY(!recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
testsTurn.release();
threadsTurn.acquire();
timer.start();
QVERIFY(recursiveMutex.try_lock_for(waitTimeAsDuration));
- QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution);
+ QCOMPARE_LE(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(recursiveMutex.try_lock_for(waitTimeAsDuration));
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
@@ -761,7 +623,7 @@ void tst_QMutex::try_lock_for_recursive()
threadsTurn.acquire();
timer.start();
QVERIFY(recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
- QVERIFY(timer.elapsed() < waitTime + systemTimersResolution);
+ QCOMPARE_LT(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
@@ -830,14 +692,10 @@ void tst_QMutex::try_lock_for_recursive()
testsTurn.acquire();
threadsTurn.release();
thread.wait();
-#endif
}
void tst_QMutex::try_lock_until_recursive()
{
-#if !__has_include(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
class Thread : public QThread
{
public:
@@ -864,14 +722,14 @@ void tst_QMutex::try_lock_until_recursive()
threadsTurn.acquire();
auto endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(!recursiveMutex.try_lock_until(endTimePoint));
- QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration);
+ QCOMPARE_GE(std::chrono::steady_clock::now(), endTimePoint - systemTimersResolutionAsDuration);
QVERIFY(!recursiveMutex.try_lock());
testsTurn.release();
threadsTurn.acquire();
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(recursiveMutex.try_lock_until(endTimePoint));
- QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration);
+ QCOMPARE_LE(std::chrono::steady_clock::now(), endTimePoint + systemTimersResolutionAsDuration);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(recursiveMutex.try_lock_until(endTimePoint));
@@ -890,7 +748,7 @@ void tst_QMutex::try_lock_until_recursive()
threadsTurn.acquire();
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(recursiveMutex.try_lock_until(std::chrono::steady_clock::now()));
- QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration);
+ QCOMPARE_LE(std::chrono::steady_clock::now(), endTimePoint + systemTimersResolutionAsDuration);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(recursiveMutex.try_lock_until(std::chrono::steady_clock::now()));
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
@@ -959,7 +817,6 @@ void tst_QMutex::try_lock_until_recursive()
testsTurn.acquire();
threadsTurn.release();
thread.wait();
-#endif
}
class mutex_Thread : public QThread
@@ -1085,8 +942,8 @@ void tst_QMutex::lock_unlock_locked_tryLock()
}
}
-enum { one_minute = 6 * 1000, //not really one minute, but else it is too long.
- threadCount = 10 };
+constexpr int one_minute = 6 * 1000; // not really one minute, but else it is too long.
+constexpr int threadCount = 10;
class StressTestThread : public QThread
{
@@ -1190,9 +1047,9 @@ void tst_QMutex::tryLockDeadlock()
{
for (int i = 0; i < 100000; ++i) {
if (mut.tryLock(0)) {
- if ((++tryLockDeadlockCounter) != 1)
+ if (QtPrivate::volatilePreIncrement(tryLockDeadlockCounter) != 1)
++tryLockDeadlockFailureCount;
- if ((--tryLockDeadlockCounter) != 0)
+ if (QtPrivate::volatilePreDecrement(tryLockDeadlockCounter) != 0)
++tryLockDeadlockFailureCount;
mut.unlock();
}
@@ -1209,9 +1066,9 @@ void tst_QMutex::tryLockDeadlock()
for (int i = 0; i < 100000; ++i) {
mut.lock();
- if ((++tryLockDeadlockCounter) != 1)
+ if (QtPrivate::volatilePreIncrement(tryLockDeadlockCounter) != 1)
++tryLockDeadlockFailureCount;
- if ((--tryLockDeadlockCounter) != 0)
+ if (QtPrivate::volatilePreDecrement(tryLockDeadlockCounter) != 0)
++tryLockDeadlockFailureCount;
mut.unlock();
}
@@ -1288,14 +1145,14 @@ public:
quint64 i = 0;
while (t.elapsed() < one_minute) {
i++;
- uint nb = (i * 9 + lockCount.loadRelaxed() * 13) % threadCount;
+ uint nb = (i * 9 + uint(lockCount.loadRelaxed()) * 13) % threadCount;
QMutexLocker locker(&mutex[nb]);
if (sentinel[nb].loadRelaxed()) errorCount.ref();
if (sentinel[nb].fetchAndAddRelaxed(5)) errorCount.ref();
if (!sentinel[nb].testAndSetRelaxed(5, 0)) errorCount.ref();
if (sentinel[nb].loadRelaxed()) errorCount.ref();
lockCount.ref();
- nb = (nb * 17 + i * 5 + lockCount.loadRelaxed() * 3) % threadCount;
+ nb = (nb * 17 + i * 5 + uint(lockCount.loadRelaxed()) * 3) % threadCount;
if (mutex[nb].tryLock()) {
if (sentinel[nb].loadRelaxed()) errorCount.ref();
if (sentinel[nb].fetchAndAddRelaxed(16)) errorCount.ref();
@@ -1304,7 +1161,7 @@ public:
lockCount.ref();
mutex[nb].unlock();
}
- nb = (nb * 15 + i * 47 + lockCount.loadRelaxed() * 31) % threadCount;
+ nb = (nb * 15 + i * 47 + uint(lockCount.loadRelaxed()) * 31) % threadCount;
if (mutex[nb].tryLock(2)) {
if (sentinel[nb].loadRelaxed()) errorCount.ref();
if (sentinel[nb].fetchAndAddRelaxed(53)) errorCount.ref();
@@ -1323,12 +1180,13 @@ QAtomicInt MoreStressTestThread::errorCount = 0;
void tst_QMutex::moreStress()
{
- MoreStressTestThread threads[threadCount];
- for (int i = 0; i < threadCount; ++i)
- threads[i].start();
+ QVarLengthArray<MoreStressTestThread, threadCount> threads(qMin(QThread::idealThreadCount(),
+ int(threadCount)));
+ for (auto &thread : threads)
+ thread.start();
QVERIFY(threads[0].wait(one_minute + 10000));
- for (int i = 1; i < threadCount; ++i)
- QVERIFY(threads[i].wait(10000));
+ for (auto &thread : threads)
+ QVERIFY(thread.wait(10000));
qDebug("locked %d times", MoreStressTestThread::lockCount.loadRelaxed());
QCOMPARE(MoreStressTestThread::errorCount.loadRelaxed(), 0);
}
diff --git a/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt b/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt
index 2f85dd512f..7b2d6dc1c2 100644
--- a/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qmutexlocker.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmutexlocker Test:
#####################################################################
-qt_add_test(tst_qmutexlocker
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qmutexlocker LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qmutexlocker
SOURCES
tst_qmutexlocker.cpp
)
diff --git a/tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro b/tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro
deleted file mode 100644
index 76ec0471ca..0000000000
--- a/tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qmutexlocker
-QT = core testlib
-SOURCES = tst_qmutexlocker.cpp
diff --git a/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
index 46cdd8073e..6ccab04c27 100644
--- a/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
+++ b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QCoreApplication>
#include <QMutexLocker>
@@ -67,6 +42,7 @@ private slots:
void scopeTest();
void unlockAndRelockTest();
void lockerStateTest();
+ void moveSemantics();
};
void tst_QMutexLocker::scopeTest()
@@ -80,6 +56,7 @@ void tst_QMutexLocker::scopeTest()
{
QMutexLocker locker(&mutex);
+ QVERIFY(locker.isLocked());
waitForTest();
}
@@ -110,7 +87,7 @@ void tst_QMutexLocker::scopeTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
@@ -122,16 +99,23 @@ void tst_QMutexLocker::unlockAndRelockTest()
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());
}
};
@@ -157,7 +141,7 @@ void tst_QMutexLocker::unlockAndRelockTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
void tst_QMutexLocker::lockerStateTest()
@@ -169,10 +153,13 @@ void tst_QMutexLocker::lockerStateTest()
{
{
QMutexLocker locker(&mutex);
- locker.relock();
+ QVERIFY(locker.isLocked());
+
locker.unlock();
+ QVERIFY(!locker.isLocked());
waitForTest();
+ QVERIFY(!locker.isLocked());
}
waitForTest();
@@ -197,7 +184,73 @@ void tst_QMutexLocker::lockerStateTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ 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)
diff --git a/tests/auto/corelib/thread/qpromise/.prev_CMakeLists.txt b/tests/auto/corelib/thread/qpromise/.prev_CMakeLists.txt
deleted file mode 100644
index dc25a38fea..0000000000
--- a/tests/auto/corelib/thread/qpromise/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-# Generated from qpromise.pro.
-
-#####################################################################
-## tst_qpromise Test:
-#####################################################################
-
-qt_add_test(tst_qpromise
- SOURCES
- tst_qpromise.cpp
- PUBLIC_LIBRARIES
- Qt::CorePrivate
-)
diff --git a/tests/auto/corelib/thread/qpromise/CMakeLists.txt b/tests/auto/corelib/thread/qpromise/CMakeLists.txt
index dc25a38fea..c1ca30b34a 100644
--- a/tests/auto/corelib/thread/qpromise/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qpromise/CMakeLists.txt
@@ -1,12 +1,19 @@
-# Generated from qpromise.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qpromise Test:
#####################################################################
-qt_add_test(tst_qpromise
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qpromise LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qpromise
SOURCES
tst_qpromise.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
)
diff --git a/tests/auto/corelib/thread/qpromise/qpromise.pro b/tests/auto/corelib/thread/qpromise/qpromise.pro
deleted file mode 100644
index 9ea1aaacdf..0000000000
--- a/tests/auto/corelib/thread/qpromise/qpromise.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qpromise
-QT = core core-private testlib
-SOURCES = tst_qpromise.cpp
diff --git a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
index 7180baccca..2cef0dca95 100644
--- a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
+++ b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
@@ -1,59 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// 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 <QtTest/QtTest>
+#include <QTest>
#include <qfuture.h>
#include <qfuturewatcher.h>
@@ -71,35 +24,38 @@ public:
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.reportStarted(); // notifies QFuture that the computation is started
+ promise.start(); // notifies QFuture that the computation is started
promise.addResult(42);
- promise.reportFinished(); // notifies QFuture that the computation is finished
+ promise.finish(); // notifies QFuture that the computation is finished
}, std::move(promise)));
thread->start();
- future.waitForFinished(); // blocks until QPromise::reportFinished is called
+ 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();
// ...
-//! [multithread_init]
- sharedPromise->reportStarted();
+ sharedPromise->start();
+//! [multithread_init]
//! [multithread_main]
// here, QPromise is shared between threads via a smart pointer
@@ -132,17 +88,21 @@ void snippet_QPromise::multithreadExample()
for (auto& t : threads)
t->wait();
- sharedPromise->reportFinished();
+//! [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.reportStarted();
+ 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) {
@@ -151,7 +111,7 @@ void snippet_QPromise::suspendExample()
if (promise.isCanceled()) // support cancellation
break;
}
- promise.reportFinished();
+ promise.finish();
}, std::move(promise)));
thread->start();
//! [suspend_start]
@@ -194,4 +154,5 @@ void snippet_QPromise::suspendExample()
QList<int> expected(100);
std::iota(expected.begin(), expected.end(), 0);
QCOMPARE(future.results(), expected);
+#endif
}
diff --git a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
index 2b8853ccd7..2c12e41c93 100644
--- a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
+++ b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
@@ -1,36 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QDebug>
#define QPROMISE_TEST
-#include <QtTest/QtTest>
+#include <QTest>
#include <qfuture.h>
#include <qfuturewatcher.h>
#include <qpromise.h>
@@ -39,6 +14,8 @@
#include <memory>
#include <chrono>
+using namespace std::chrono_literals;
+
class tst_QPromise : public QObject
{
Q_OBJECT
@@ -47,6 +24,7 @@ private slots:
void promise();
void futureFromPromise();
void addResult();
+ void addResultWithBracedInitializer();
void addResultOutOfOrder();
#ifndef QT_NO_EXCEPTIONS
void setException();
@@ -64,6 +42,10 @@ private slots:
void cancelWhenDestroyed();
#endif
void cancelWhenReassigned();
+ void cancelWhenDestroyedWithoutStarting();
+ void cancelWhenDestroyedRunsContinuations();
+ void cancelWhenDestroyedWithFailureHandler(); // QTBUG-114606
+ void continuationsRunWhenFinished();
void finishWhenSwapped();
void cancelWhenMoved();
void waitUntilResumed();
@@ -77,22 +59,21 @@ private slots:
struct TrivialType { int field = 0; };
struct CopyOnlyType {
- Q_DISABLE_MOVE(CopyOnlyType);
- CopyOnlyType() = default;
+ constexpr CopyOnlyType(int field = 0) noexcept : field(field) {}
CopyOnlyType(const CopyOnlyType &) = default;
CopyOnlyType& operator=(const CopyOnlyType &) = default;
~CopyOnlyType() = default;
- int field = 0;
+ int field;
};
struct MoveOnlyType {
- Q_DISABLE_COPY(MoveOnlyType);
- MoveOnlyType() = default;
+ Q_DISABLE_COPY(MoveOnlyType)
+ constexpr MoveOnlyType(int field = 0) noexcept : field(field) {}
MoveOnlyType(MoveOnlyType &&) = default;
MoveOnlyType& operator=(MoveOnlyType &&) = default;
~MoveOnlyType() = default;
- int field = 0;
+ 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; }
@@ -105,6 +86,7 @@ do { \
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
@@ -121,13 +103,14 @@ struct ThreadWrapper
t->wait();
}
};
+#endif
void tst_QPromise::promise()
{
const auto testCanCreatePromise = [] (auto promise) {
- promise.reportStarted();
+ promise.start();
promise.suspendIfRequested(); // should not block on its own
- promise.reportFinished();
+ promise.finish();
};
RUN_TEST_FUNC(testCanCreatePromise, QPromise<void>());
@@ -144,11 +127,11 @@ void tst_QPromise::futureFromPromise()
auto future = promise.future();
QVERIFY(!future.isValid());
- promise.reportStarted();
+ promise.start();
QCOMPARE(future.isStarted(), true);
QVERIFY(future.isValid());
- promise.reportFinished();
+ promise.finish();
QCOMPARE(future.isFinished(), true);
QVERIFY(future.isValid());
@@ -169,34 +152,72 @@ void tst_QPromise::addResult()
auto f = promise.future();
// add as lvalue
+ int resultAt0 = 456;
{
- int result = 456;
- promise.addResult(result);
+ QVERIFY(promise.addResult(resultAt0));
QCOMPARE(f.resultCount(), 1);
- QCOMPARE(f.result(), result);
- QCOMPARE(f.resultAt(0), result);
+ QCOMPARE(f.result(), resultAt0);
+ QCOMPARE(f.resultAt(0), resultAt0);
}
// add as rvalue
{
int result = 789;
- promise.addResult(789);
+ QVERIFY(promise.addResult(789));
QCOMPARE(f.resultCount(), 2);
QCOMPARE(f.resultAt(1), result);
}
// add at position
{
int result = 56238;
- promise.addResult(result, 2);
+ QVERIFY(promise.addResult(result, 2));
QCOMPARE(f.resultCount(), 3);
QCOMPARE(f.resultAt(2), result);
}
- // add at position and overwrite
+ // add multiple results in one go:
+ {
+ QList results = {42, 4242, 424242};
+ QVERIFY(promise.addResults(results));
+ QCOMPARE(f.resultCount(), 6);
+ QCOMPARE(f.resultAt(3), 42);
+ QCOMPARE(f.resultAt(4), 4242);
+ QCOMPARE(f.resultAt(5), 424242);
+ }
+ // add as lvalue at position and overwrite
{
int result = -1;
const auto originalCount = f.resultCount();
- promise.addResult(result, 0);
+ QVERIFY(!promise.addResult(result, 0));
QCOMPARE(f.resultCount(), originalCount);
- QCOMPARE(f.resultAt(0), result);
+ 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::addResultWithBracedInitializer() // QTBUG-111826
+{
+ struct MyClass
+ {
+ QString strValue;
+ int intValue = 0;
+#ifndef __cpp_aggregate_paren_init // make emplacement work with MyClass
+ MyClass(QString s, int i) : strValue(std::move(s)), intValue(i) {}
+#endif
+ };
+
+ {
+ QPromise<MyClass> myPromise;
+ myPromise.addResult({"bar", 1});
+ }
+
+ {
+ QPromise<MyClass> myPromise;
+ myPromise.emplaceResult("bar", 1);
}
}
@@ -216,9 +237,9 @@ void tst_QPromise::addResultOutOfOrder()
{
QPromise<int> promise;
auto f = promise.future();
- promise.addResult(456, 1);
+ QVERIFY(promise.addResult(456, 1));
QCOMPARE(f.resultCount(), 0);
- promise.addResult(123, 0);
+ QVERIFY(promise.addResult(123, 0));
QList<int> expected({123, 456});
RUN_TEST_FUNC(compareResults, f, expected);
@@ -229,16 +250,16 @@ void tst_QPromise::addResultOutOfOrder()
{
QPromise<int> promise;
auto f = promise.future();
- promise.addResult(0, 0);
- promise.addResult(1, 1);
- promise.addResult(3, 3); // intentional gap here
+ 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});
- promise.addResult(2, 2); // fill a gap with a value
+ QVERIFY(promise.addResult(2, 2)); // fill a gap with a value
RUN_TEST_FUNC(compareResults, f, expectedWhenNoGap);
QCOMPARE(f.results(), expectedWhenNoGap);
}
@@ -250,9 +271,9 @@ void tst_QPromise::setException()
struct TestException {}; // custom exception class
const auto testExceptionCaught = [] (auto promise, const auto& exception) {
auto f = promise.future();
- promise.reportStarted();
+ promise.start();
promise.setException(exception);
- promise.reportFinished();
+ promise.finish();
bool caught = false;
try {
@@ -271,6 +292,10 @@ void tst_QPromise::setException()
std::make_exception_ptr(TestException()));
RUN_TEST_FUNC(testExceptionCaught, QPromise<int>(),
std::make_exception_ptr(TestException()));
+ RUN_TEST_FUNC(testExceptionCaught, QPromise<CopyOnlyType>(),
+ std::make_exception_ptr(TestException()));
+ RUN_TEST_FUNC(testExceptionCaught, QPromise<MoveOnlyType>(),
+ std::make_exception_ptr(TestException()));
}
#endif
@@ -284,6 +309,8 @@ void tst_QPromise::cancel()
testCancel(QPromise<void>());
testCancel(QPromise<int>());
+ testCancel(QPromise<CopyOnlyType>());
+ testCancel(QPromise<MoveOnlyType>());
}
void tst_QPromise::progress()
@@ -301,7 +328,6 @@ void tst_QPromise::progress()
promise.setProgressValue(0); // decrement
QCOMPARE(f.progressValue(), 1);
promise.setProgressValue(10); // out of range
- QEXPECT_FAIL("", "Out of range value is set - QTBUG-84729", Continue);
QCOMPARE(f.progressValue(), 1);
promise.setProgressRange(0, 100);
@@ -312,12 +338,15 @@ void tst_QPromise::progress()
RUN_TEST_FUNC(testProgress, QPromise<void>());
RUN_TEST_FUNC(testProgress, QPromise<int>());
+ RUN_TEST_FUNC(testProgress, QPromise<CopyOnlyType>());
+ RUN_TEST_FUNC(testProgress, QPromise<MoveOnlyType>());
}
void tst_QPromise::addInThread()
{
+#if QT_CONFIG(cxx11_future)
const auto testAddResult = [] (auto promise, const auto &result) {
- promise.reportStarted();
+ promise.start();
auto f = promise.future();
// move construct QPromise
ThreadWrapper thr([p = std::move(promise), &result] () mutable {
@@ -331,12 +360,14 @@ void tst_QPromise::addInThread()
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)
QPromise<MoveOnlyType> promise;
- promise.reportStarted();
+ promise.start();
auto f = promise.future();
ThreadWrapper thr([p = std::move(promise)] () mutable {
@@ -346,13 +377,15 @@ void tst_QPromise::addInThreadMoveOnlyObject()
// Iterators wait for result first
for (auto& result : f)
QCOMPARE(result, MoveOnlyType{-11});
+#endif
}
void tst_QPromise::reportFromMultipleThreads()
{
+#if QT_CONFIG(cxx11_future)
QPromise<int> promise;
auto f = promise.future();
- promise.reportStarted();
+ promise.start();
ThreadWrapper threads[] = {
ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
@@ -361,17 +394,19 @@ void tst_QPromise::reportFromMultipleThreads()
};
for (auto& t : threads)
t.join();
- promise.reportFinished();
+ 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)
QPromise<int> initialPromise;
auto f = initialPromise.future();
{
@@ -379,7 +414,7 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise()
// move-constructed) must be able to set results, QFuture must still
// hold correct references to results.
auto promise = std::move(initialPromise);
- promise.reportStarted();
+ promise.start();
ThreadWrapper threads[] = {
ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
ThreadWrapper([&promise] () mutable { promise.addResult(43); }),
@@ -387,7 +422,7 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise()
};
for (auto& t : threads)
t.join();
- promise.reportFinished();
+ promise.finish();
}
QCOMPARE(f.isFinished(), true);
@@ -398,16 +433,18 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise()
QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end());
expected.removeOne(actual);
}
+#endif
}
void tst_QPromise::doNotCancelWhenFinished()
{
+#if QT_CONFIG(cxx11_future)
const auto testFinishedPromise = [] (auto promise) {
auto f = promise.future();
- promise.reportStarted();
+ promise.start();
// Finish QPromise inside thread, destructor must not call cancel()
- ThreadWrapper([p = std::move(promise)] () mutable { p.reportFinished(); }).join();
+ ThreadWrapper([p = std::move(promise)] () mutable { p.finish(); }).join();
f.waitForFinished();
@@ -418,18 +455,22 @@ void tst_QPromise::doNotCancelWhenFinished()
RUN_TEST_FUNC(testFinishedPromise, QPromise<void>());
RUN_TEST_FUNC(testFinishedPromise, QPromise<int>());
RUN_TEST_FUNC(testFinishedPromise, QPromise<QString>());
+ RUN_TEST_FUNC(testFinishedPromise, QPromise<CopyOnlyType>());
+ RUN_TEST_FUNC(testFinishedPromise, QPromise<MoveOnlyType>());
+#endif
}
#ifndef QT_NO_EXCEPTIONS
void tst_QPromise::cancelWhenDestroyed()
{
+#if QT_CONFIG(cxx11_future)
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.reportStarted();
+ promise.start();
ThreadWrapper threads[] = {
ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
ThreadWrapper([&promise] () mutable { promise.addResult(43); }),
@@ -437,8 +478,8 @@ void tst_QPromise::cancelWhenDestroyed()
};
for (auto& t : threads)
t.join();
- throw "Throw in the middle, we lose our promise here, reportFinished() not called!";
- promise.reportFinished();
+ throw "Throw in the middle, we lose our promise here, finish() not called!";
+ promise.finish();
} catch (...) {}
QCOMPARE(f.isFinished(), true);
@@ -450,17 +491,19 @@ void tst_QPromise::cancelWhenDestroyed()
QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end());
expected.removeOne(actual);
}
+#endif
}
#endif
void tst_QPromise::cancelWhenReassigned()
{
+#if QT_CONFIG(cxx11_future)
QPromise<int> promise;
auto f = promise.future();
- promise.reportStarted();
+ promise.start();
ThreadWrapper thr([p = std::move(promise)] () mutable {
- QThread::msleep(100);
+ QThread::sleep(100ms);
p = QPromise<int>(); // assign new promise, old must be correctly destroyed
});
@@ -468,27 +511,143 @@ void tst_QPromise::cancelWhenReassigned()
QCOMPARE(f.isFinished(), true);
QCOMPARE(f.isCanceled(), true);
+#endif
+}
+
+template <typename T>
+static inline void testCancelWhenDestroyedWithoutStarting()
+{
+ QFuture<T> future;
+ {
+ QPromise<T> promise;
+ future = promise.future();
+ }
+ future.waitForFinished();
+ QVERIFY(!future.isStarted());
+ QVERIFY(future.isCanceled());
+ QVERIFY(future.isFinished());
+}
+
+void tst_QPromise::cancelWhenDestroyedWithoutStarting()
+{
+ testCancelWhenDestroyedWithoutStarting<void>();
+ testCancelWhenDestroyedWithoutStarting<int>();
+ testCancelWhenDestroyedWithoutStarting<CopyOnlyType>();
+ testCancelWhenDestroyedWithoutStarting<MoveOnlyType>();
+}
+
+template <typename T>
+static inline void testCancelWhenDestroyedRunsContinuations()
+{
+ QFuture<T> future;
+ bool onCanceledCalled = false;
+ bool thenCalled = false;
+ {
+ QPromise<T> promise;
+ future = promise.future();
+ future.then([&] (auto&&) {
+ thenCalled = true;
+ }).onCanceled([&] () {
+ onCanceledCalled = true;
+ });
+ }
+ QVERIFY(future.isFinished());
+ QVERIFY(!thenCalled);
+ QVERIFY(onCanceledCalled);
+}
+
+void tst_QPromise::cancelWhenDestroyedRunsContinuations()
+{
+ testCancelWhenDestroyedRunsContinuations<void>();
+ testCancelWhenDestroyedRunsContinuations<int>();
+ testCancelWhenDestroyedRunsContinuations<CopyOnlyType>();
+ testCancelWhenDestroyedRunsContinuations<MoveOnlyType>();
+}
+
+template <typename T>
+static inline void testCancelWhenDestroyedWithFailureHandler()
+{
+ QFuture<T> future;
+ bool onFailedCalled = false;
+ bool thenCalled = false;
+ {
+ QPromise<T> promise;
+ future = promise.future();
+ future
+ .onFailed([&] () {
+ onFailedCalled = true;
+ if constexpr (!std::is_same_v<void, T>)
+ return T{};
+ })
+ .then([&] (auto&&) {
+ thenCalled = true;
+ });
+ }
+ QVERIFY(future.isFinished());
+ QVERIFY(!onFailedCalled);
+ QVERIFY(!thenCalled);
+}
+
+void tst_QPromise::cancelWhenDestroyedWithFailureHandler()
+{
+#ifndef QT_NO_EXCEPTIONS
+ testCancelWhenDestroyedWithFailureHandler<void>();
+ testCancelWhenDestroyedWithFailureHandler<int>();
+ testCancelWhenDestroyedWithFailureHandler<CopyOnlyType>();
+ testCancelWhenDestroyedWithFailureHandler<MoveOnlyType>();
+#else
+ QSKIP("Exceptions are disabled, skipping the test");
+#endif
+}
+
+template <typename T>
+static inline void testContinuationsRunWhenFinished()
+{
+ QPromise<T> promise;
+ QFuture<T> future = promise.future();
+
+ bool thenCalled = false;
+ future.then([&] (auto&&) {
+ thenCalled = true;
+ });
+
+ promise.start();
+ if constexpr (!std::is_void_v<T>) {
+ promise.addResult(T{});
+ }
+ promise.finish();
+
+ QVERIFY(thenCalled);
+}
+
+void tst_QPromise::continuationsRunWhenFinished()
+{
+ testContinuationsRunWhenFinished<void>();
+ testContinuationsRunWhenFinished<int>();
+ testContinuationsRunWhenFinished<CopyOnlyType>();
+ testContinuationsRunWhenFinished<MoveOnlyType>();
}
void tst_QPromise::finishWhenSwapped()
{
+#if QT_CONFIG(cxx11_future)
QPromise<int> promise1;
auto f1 = promise1.future();
- promise1.reportStarted();
+ promise1.start();
QPromise<int> promise2;
auto f2 = promise2.future();
- promise2.reportStarted();
+ promise2.start();
ThreadWrapper thr([&promise1, &promise2] () mutable {
- QThread::msleep(100);
+ QThread::sleep(100ms);
promise1.addResult(0);
promise2.addResult(1);
swap(promise1, promise2); // ADL must resolve this
promise1.addResult(2);
promise2.addResult(3);
- promise1.reportFinished(); // this finish is for future #2
- promise2.reportFinished(); // this finish is for future #1
+ promise1.finish(); // this finish is for future #2
+ promise2.finish(); // this finish is for future #1
});
f1.waitForFinished();
@@ -506,23 +665,26 @@ void tst_QPromise::finishWhenSwapped()
QCOMPARE(f2.resultAt(0), 1);
QCOMPARE(f2.resultAt(1), 2);
+#endif
}
-void tst_QPromise::cancelWhenMoved()
+template <typename T>
+void testCancelWhenMoved()
{
- QPromise<int> promise1;
+#if QT_CONFIG(cxx11_future)
+ QPromise<T> promise1;
auto f1 = promise1.future();
- promise1.reportStarted();
+ promise1.start();
- QPromise<int> promise2;
+ QPromise<T> promise2;
auto f2 = promise2.future();
- promise2.reportStarted();
+ 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);
+ QThread::sleep(100ms);
p1 = std::move(p2);
- p1.reportFinished(); // this finish is for future #2
+ p1.finish(); // this finish is for future #2
});
f1.waitForFinished();
@@ -535,24 +697,36 @@ void tst_QPromise::cancelWhenMoved()
// Future #2 is explicitly finished inside thread
QCOMPARE(f2.isFinished(), true);
QCOMPARE(f2.isCanceled(), false);
+#endif
+}
+
+void tst_QPromise::cancelWhenMoved()
+{
+ testCancelWhenMoved<void>();
+ testCancelWhenMoved<int>();
+ testCancelWhenMoved<CopyOnlyType>();
+ testCancelWhenMoved<MoveOnlyType>();
}
void tst_QPromise::waitUntilResumed()
{
+#if !QT_CONFIG(cxx11_future)
+ QSKIP("This test requires QThread::create");
+#else
QPromise<int> promise;
- promise.reportStarted();
+ 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.reportFinished();
+ 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
+ QThread::sleep(50ms); // allow another thread to actually carry on
}
f.resume();
@@ -560,30 +734,33 @@ void tst_QPromise::waitUntilResumed()
QCOMPARE(f.resultCount(), 1);
QCOMPARE(f.result(), 42);
+#endif
}
void tst_QPromise::waitUntilCanceled()
{
+#if QT_CONFIG(cxx11_future)
QPromise<int> promise;
- promise.reportStarted();
+ 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.reportFinished();
+ 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
+ QThread::sleep(50ms); // 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
diff --git a/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt b/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
index 245fb52dfc..7e80a4df81 100644
--- a/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qreadlocker.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qreadlocker Test:
#####################################################################
-qt_add_test(tst_qreadlocker
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qreadlocker LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qreadlocker
SOURCES
tst_qreadlocker.cpp
)
diff --git a/tests/auto/corelib/thread/qreadlocker/qreadlocker.pro b/tests/auto/corelib/thread/qreadlocker/qreadlocker.pro
deleted file mode 100644
index ba46786750..0000000000
--- a/tests/auto/corelib/thread/qreadlocker/qreadlocker.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qreadlocker
-QT = core testlib
-SOURCES = tst_qreadlocker.cpp
diff --git a/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
index 241f064a21..e21f80c05d 100644
--- a/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
+++ b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QCoreApplication>
#include <QReadLocker>
@@ -109,7 +84,7 @@ void tst_QReadLocker::scopeTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
@@ -156,7 +131,7 @@ void tst_QReadLocker::unlockAndRelockTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
void tst_QReadLocker::lockerStateTest()
@@ -196,7 +171,7 @@ void tst_QReadLocker::lockerStateTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
QTEST_MAIN(tst_QReadLocker)
diff --git a/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt b/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt
index fbd8ebf408..5ed3012e04 100644
--- a/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt
@@ -1,10 +1,20 @@
-# Generated from qreadwritelock.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qreadwritelock Test:
#####################################################################
-qt_add_test(tst_qreadwritelock
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qreadwritelock LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qreadwritelock
SOURCES
tst_qreadwritelock.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::TestPrivate
)
diff --git a/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro b/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro
deleted file mode 100644
index c108c3d8af..0000000000
--- a/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qreadwritelock
-QT = core testlib
-SOURCES = tst_qreadwritelock.cpp
diff --git a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
index f984aec0e8..86dfa5faff 100644
--- a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
+++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
@@ -1,46 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QSemaphore>
#include <qcoreapplication.h>
#include <qreadwritelock.h>
#include <qelapsedtimer.h>
#include <qmutex.h>
#include <qthread.h>
#include <qwaitcondition.h>
+#include <private/qemulationdetector_p.h>
+#include <private/qvolatile_p.h>
#ifdef Q_OS_UNIX
#include <unistd.h>
#endif
-#if defined(Q_OS_WIN)
-# include <qt_windows.h>
-# define sleep(X) Sleep(X)
-#endif
//on solaris, threads that loop on the release bool variable
//needs to sleep more than 1 usec.
@@ -52,6 +26,8 @@
#include <stdio.h>
+using namespace std::chrono_literals;
+
class tst_QReadWriteLock : public QObject
{
Q_OBJECT
@@ -184,10 +160,10 @@ void tst_QReadWriteLock::readWriteLockUnlockLoop()
}
-QAtomicInt lockCount(0);
-QReadWriteLock readWriteLock;
-QSemaphore testsTurn;
-QSemaphore threadsTurn;
+static QAtomicInt lockCount(0);
+static QReadWriteLock readWriteLock;
+static QSemaphore testsTurn;
+static QSemaphore threadsTurn;
void tst_QReadWriteLock::tryReadLock()
@@ -403,8 +379,8 @@ void tst_QReadWriteLock::tryWriteLock()
}
}
-bool threadDone;
-QAtomicInt release;
+static bool threadDone;
+static QAtomicInt release;
/*
write-lock
@@ -494,8 +470,8 @@ class ReadLockLoopThread : public QThread
public:
QReadWriteLock &testRwlock;
int runTime;
- int holdTime;
- int waitTime;
+ std::chrono::milliseconds holdTime;
+ std::chrono::milliseconds waitTime;
bool print;
QElapsedTimer t;
inline ReadLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false)
@@ -511,9 +487,9 @@ public:
while (t.elapsed()<runTime) {
testRwlock.lockForRead();
if(print) printf("reading\n");
- if (holdTime) msleep(holdTime);
+ if (holdTime > 0ms) sleep(holdTime);
testRwlock.unlock();
- if (waitTime) msleep(waitTime);
+ if (waitTime > 0ms) sleep(waitTime);
}
}
};
@@ -530,8 +506,8 @@ class WriteLockLoopThread : public QThread
public:
QReadWriteLock &testRwlock;
int runTime;
- int holdTime;
- int waitTime;
+ std::chrono::milliseconds holdTime;
+ std::chrono::milliseconds waitTime;
bool print;
QElapsedTimer t;
inline WriteLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false)
@@ -547,14 +523,14 @@ public:
while (t.elapsed() < runTime) {
testRwlock.lockForWrite();
if (print) printf(".");
- if (holdTime) msleep(holdTime);
+ if (holdTime > 0ms) sleep(holdTime);
testRwlock.unlock();
- if (waitTime) msleep(waitTime);
+ if (waitTime > 0ms) sleep(waitTime);
}
}
};
-volatile int count=0;
+static volatile int count = 0;
/*
for(runTime msecs)
@@ -569,7 +545,7 @@ class WriteLockCountThread : public QThread
public:
QReadWriteLock &testRwlock;
int runTime;
- int waitTime;
+ std::chrono::milliseconds waitTime;
int maxval;
QElapsedTimer t;
inline WriteLockCountThread(QReadWriteLock &l, int runTime, int waitTime, int maxval)
@@ -586,15 +562,11 @@ public:
if(count)
qFatal("Non-zero count at start of write! (%d)",count );
// printf(".");
- int i;
- for(i=0; i<maxval; ++i) {
- volatile int lc=count;
- ++lc;
- count=lc;
- }
+ for (int i = 0; i < maxval; ++i)
+ QtPrivate::volatilePreIncrement(count);
count=0;
testRwlock.unlock();
- msleep(waitTime);
+ sleep(waitTime);
}
}
};
@@ -611,7 +583,7 @@ class ReadLockCountThread : public QThread
public:
QReadWriteLock &testRwlock;
int runTime;
- int waitTime;
+ std::chrono::milliseconds waitTime;
QElapsedTimer t;
inline ReadLockCountThread(QReadWriteLock &l, int runTime, int waitTime)
:testRwlock(l)
@@ -626,7 +598,7 @@ public:
if(count)
qFatal("Non-zero count at Read! (%d)",count );
testRwlock.unlock();
- msleep(waitTime);
+ sleep(waitTime);
}
}
};
@@ -643,7 +615,7 @@ void tst_QReadWriteLock::readLockBlockRelease()
threadDone=false;
ReadLockThread rlt(testLock);
rlt.start();
- sleep(1);
+ QThread::sleep(1s);
testLock.unlock();
rlt.wait();
QVERIFY(threadDone);
@@ -660,7 +632,7 @@ void tst_QReadWriteLock::writeLockBlockRelease()
threadDone=false;
WriteLockThread wlt(testLock);
wlt.start();
- sleep(1);
+ QThread::sleep(1s);
testLock.unlock();
wlt.wait();
QVERIFY(threadDone);
@@ -679,10 +651,10 @@ void tst_QReadWriteLock::multipleReadersBlockRelease()
ReadLockReleasableThread rlt2(testLock);
rlt1.start();
rlt2.start();
- sleep(1);
+ QThread::sleep(1s);
WriteLockThread wlt(testLock);
wlt.start();
- sleep(1);
+ QThread::sleep(1s);
release.storeRelaxed(true);
wlt.wait();
rlt1.wait();
@@ -695,27 +667,29 @@ void tst_QReadWriteLock::multipleReadersBlockRelease()
*/
void tst_QReadWriteLock::multipleReadersLoop()
{
- int time=500;
- int hold=250;
- int wait=0;
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Flaky on QEMU, QTBUG-96103");
+
+ constexpr int time = 500;
+ constexpr int hold = 250;
+ constexpr int wait = 0;
#if defined (Q_OS_HPUX)
- const int numthreads=50;
+ constexpr int NumThreads = 50;
#elif defined(Q_OS_VXWORKS)
- const int numthreads=40;
+ constexpr int NumThreads = 40;
#else
- const int numthreads=75;
+ constexpr int NumThreads = 75;
#endif
QReadWriteLock testLock;
- ReadLockLoopThread *threads[numthreads];
- int i;
- for (i=0; i<numthreads; ++i)
- threads[i] = new ReadLockLoopThread(testLock, time, hold, wait);
- for (i=0; i<numthreads; ++i)
- threads[i]->start();
- for (i=0; i<numthreads; ++i)
- threads[i]->wait();
- for (i=0; i<numthreads; ++i)
- delete threads[i];
+ ReadLockLoopThread *threads[NumThreads];
+ for (auto &thread : threads)
+ thread = new ReadLockLoopThread(testLock, time, hold, wait);
+ for (auto thread : threads)
+ thread->start();
+ for (auto thread : threads)
+ thread->wait();
+ for (auto thread : threads)
+ delete thread;
}
/*
@@ -723,21 +697,20 @@ void tst_QReadWriteLock::multipleReadersLoop()
*/
void tst_QReadWriteLock::multipleWritersLoop()
{
- int time=500;
- int wait=0;
- int hold=0;
- const int numthreads=50;
- QReadWriteLock testLock;
- WriteLockLoopThread *threads[numthreads];
- int i;
- for (i=0; i<numthreads; ++i)
- threads[i] = new WriteLockLoopThread(testLock, time, hold, wait);
- for (i=0; i<numthreads; ++i)
- threads[i]->start();
- for (i=0; i<numthreads; ++i)
- threads[i]->wait();
- for (i=0; i<numthreads; ++i)
- delete threads[i];
+ constexpr int time = 500;
+ constexpr int wait = 0;
+ constexpr int hold = 0;
+ constexpr int numthreads = 50;
+ QReadWriteLock testLock;
+ WriteLockLoopThread *threads[numthreads];
+ for (auto &thread : threads)
+ thread = new WriteLockLoopThread(testLock, time, hold, wait);
+ for (auto thread : threads)
+ thread->start();
+ for (auto thread : threads)
+ thread->wait();
+ for (auto thread : threads)
+ delete thread;
}
/*
@@ -745,40 +718,36 @@ void tst_QReadWriteLock::multipleWritersLoop()
*/
void tst_QReadWriteLock::multipleReadersWritersLoop()
{
- //int time=INT_MAX;
- int time=10000;
- int readerThreads=20;
- int readerWait=0;
- int readerHold=1;
-
- int writerThreads=2;
- int writerWait=500;
- int writerHold=50;
-
- QReadWriteLock testLock;
- ReadLockLoopThread *readers[1024];
- WriteLockLoopThread *writers[1024];
- int i;
-
- for (i=0; i<readerThreads; ++i)
- readers[i] = new ReadLockLoopThread(testLock, time, readerHold, readerWait, false);
- for (i=0; i<writerThreads; ++i)
- writers[i] = new WriteLockLoopThread(testLock, time, writerHold, writerWait, false);
-
- for (i=0; i<readerThreads; ++i)
- readers[i]->start(QThread::NormalPriority);
- for (i=0; i<writerThreads; ++i)
- writers[i]->start(QThread::IdlePriority);
-
- for (i=0; i<readerThreads; ++i)
- readers[i]->wait();
- for (i=0; i<writerThreads; ++i)
- writers[i]->wait();
-
- for (i=0; i<readerThreads; ++i)
- delete readers[i];
- for (i=0; i<writerThreads; ++i)
- delete writers[i];
+ constexpr int time = 10000; // INT_MAX
+ constexpr int readerThreads = 20;
+ constexpr int readerWait = 0;
+ constexpr int readerHold = 1;
+
+ constexpr int writerThreads = 2;
+ constexpr int writerWait = 500;
+ constexpr int writerHold = 50;
+
+ QReadWriteLock testLock;
+ ReadLockLoopThread *readers[readerThreads];
+ WriteLockLoopThread *writers[writerThreads];
+
+ for (auto &thread : readers)
+ thread = new ReadLockLoopThread(testLock, time, readerHold, readerWait, false);
+ for (auto &thread : writers)
+ thread = new WriteLockLoopThread(testLock, time, writerHold, writerWait, false);
+ for (auto thread : readers)
+ thread->start(QThread::NormalPriority);
+ for (auto thread : writers)
+ thread->start(QThread::IdlePriority);
+
+ for (auto thread : readers)
+ thread->wait();
+ for (auto thread : writers)
+ thread->wait();
+ for (auto thread : readers)
+ delete thread;
+ for (auto thread : writers)
+ delete thread;
}
/*
@@ -787,39 +756,35 @@ void tst_QReadWriteLock::multipleReadersWritersLoop()
*/
void tst_QReadWriteLock::countingTest()
{
- //int time=INT_MAX;
- int time=10000;
- int readerThreads=20;
- int readerWait=1;
-
- int writerThreads=3;
- int writerWait=150;
- int maxval=10000;
-
- QReadWriteLock testLock;
- ReadLockCountThread *readers[1024];
- WriteLockCountThread *writers[1024];
- int i;
-
- for (i=0; i<readerThreads; ++i)
- readers[i] = new ReadLockCountThread(testLock, time, readerWait);
- for (i=0; i<writerThreads; ++i)
- writers[i] = new WriteLockCountThread(testLock, time, writerWait, maxval);
-
- for (i=0; i<readerThreads; ++i)
- readers[i]->start(QThread::NormalPriority);
- for (i=0; i<writerThreads; ++i)
- writers[i]->start(QThread::LowestPriority);
-
- for (i=0; i<readerThreads; ++i)
- readers[i]->wait();
- for (i=0; i<writerThreads; ++i)
- writers[i]->wait();
-
- for (i=0; i<readerThreads; ++i)
- delete readers[i];
- for (i=0; i<writerThreads; ++i)
- delete writers[i];
+ constexpr int time = 10000; // INT_MAX
+ constexpr int readerThreads = 20;
+ constexpr int readerWait = 1;
+
+ constexpr int writerThreads = 3;
+ constexpr int writerWait = 150;
+ constexpr int maxval = 10000;
+
+ QReadWriteLock testLock;
+ ReadLockCountThread *readers[readerThreads];
+ WriteLockCountThread *writers[writerThreads];
+
+ for (auto &thread : readers)
+ thread = new ReadLockCountThread(testLock, time, readerWait);
+ for (auto &thread : writers)
+ thread = new WriteLockCountThread(testLock, time, writerWait, maxval);
+ for (auto thread : readers)
+ thread->start(QThread::NormalPriority);
+ for (auto thread : writers)
+ thread->start(QThread::LowestPriority);
+
+ for (auto thread : readers)
+ thread->wait();
+ for (auto thread : writers)
+ thread->wait();
+ for (auto thread : readers)
+ delete thread;
+ for (auto thread : writers)
+ delete thread;
}
void tst_QReadWriteLock::limitedReaders()
@@ -864,7 +829,7 @@ private:
void tst_QReadWriteLock::deleteOnUnlock()
{
- QReadWriteLock *lock = 0;
+ QReadWriteLock *lock = nullptr;
QWaitCondition startup;
QMutex waitMutex;
diff --git a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt
index c75794cb25..5abfc14ac6 100644
--- a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt
@@ -1,12 +1,20 @@
-# Generated from qresultstore.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qresultstore Test:
#####################################################################
-qt_add_test(tst_qresultstore
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qresultstore LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qresultstore
SOURCES
tst_qresultstore.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
+ Qt::TestPrivate
)
diff --git a/tests/auto/corelib/thread/qresultstore/qresultstore.pro b/tests/auto/corelib/thread/qresultstore/qresultstore.pro
deleted file mode 100644
index 80e79b1c1a..0000000000
--- a/tests/auto/corelib/thread/qresultstore/qresultstore.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qresultstore
-QT = core-private testlib
-SOURCES = tst_qresultstore.cpp
diff --git a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
index 5b545fe37a..722184a72a 100644
--- a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
+++ b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
@@ -1,40 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <qresultstore.h>
using namespace QtPrivate;
-struct ResultStoreInt : ResultStoreBase
+class IntResultsCleaner
{
- ~ResultStoreInt() { clear<int>(); }
+public:
+ IntResultsCleaner(QtPrivate::ResultStoreBase &s) : store(s) { }
+ ~IntResultsCleaner() { store.clear<int>(); }
+
+private:
+ QtPrivate::ResultStoreBase &store;
};
class tst_QtConcurrentResultStore : public QObject
@@ -43,6 +23,7 @@ class tst_QtConcurrentResultStore : public QObject
public slots:
void init();
private slots:
+ void compareCompiles();
void construction();
void iterators();
void addResult();
@@ -53,6 +34,8 @@ private slots:
void filterMode();
void addCanceledResult();
void count();
+ void pendingResultsDoNotLeak_data();
+ void pendingResultsDoNotLeak();
private:
int int0;
int int1;
@@ -70,6 +53,11 @@ void tst_QtConcurrentResultStore::init()
vec1 = QList<int> { 4, 5 };
}
+void tst_QtConcurrentResultStore::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<ResultIteratorBase>();
+}
+
void tst_QtConcurrentResultStore::construction()
{
ResultStoreBase store;
@@ -85,22 +73,27 @@ void tst_QtConcurrentResultStore::iterators()
QCOMPARE(store.resultAt(1), store.end());
}
{
- ResultStoreInt storebase;
+ 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());
+ QT_TEST_EQUALITY_OPS(it, storebase.begin(), true);
QVERIFY(it != storebase.end());
++it;
QCOMPARE(it.resultIndex(), 1);
QVERIFY(it != storebase.begin());
QVERIFY(it != storebase.end());
+ QT_TEST_EQUALITY_OPS(it, storebase.begin(), false);
+ QT_TEST_EQUALITY_OPS(it, storebase.end(), false);
++it;
QVERIFY(it != storebase.begin());
QCOMPARE(it, storebase.end());
+ QT_TEST_EQUALITY_OPS(it, storebase.end(), true);
}
}
@@ -108,7 +101,9 @@ void tst_QtConcurrentResultStore::addResult()
{
{
// test addResult return value
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
QCOMPARE(store.addResult(0, &int0), 0);
@@ -154,13 +149,15 @@ void tst_QtConcurrentResultStore::addResult()
void tst_QtConcurrentResultStore::addResults()
{
- ResultStoreInt store;
+ 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());
+ QT_TEST_EQUALITY_OPS(it, store.begin(), true);
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
QCOMPARE(it.resultIndex(), 1);
@@ -174,34 +171,44 @@ void tst_QtConcurrentResultStore::addResults()
QCOMPARE(it.resultIndex(), 3);
++it;
- QCOMPARE(it, store.end());
+ QT_TEST_EQUALITY_OPS(it, store.end(), true);
+
+ 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()
{
- ResultStoreInt store;
+ 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());
+ QT_TEST_EQUALITY_OPS(it, store.begin(), true);
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
QCOMPARE(it.resultIndex(), 1);
- QVERIFY(it != store.begin());
- QVERIFY(it != store.end());
+ QT_TEST_EQUALITY_OPS(it, store.begin(), false);
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
QCOMPARE(it.resultIndex(), 2);
- QVERIFY(it != store.end());
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
QCOMPARE(it.resultIndex(), 3);
- QVERIFY(it != store.end());
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
- QVERIFY(it == store.end());
+ QT_TEST_EQUALITY_OPS(it, store.end(), true);
QCOMPARE(store.resultAt(0).value<int>(), int0);
QCOMPARE(store.resultAt(1).value<int>(), vec0[0]);
@@ -212,7 +219,9 @@ void tst_QtConcurrentResultStore::resultIndex()
void tst_QtConcurrentResultStore::resultAt()
{
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(200, &int1);
@@ -223,7 +232,9 @@ void tst_QtConcurrentResultStore::resultAt()
QCOMPARE(store.resultAt(200).value<int>(), int1);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(1, &int1);
store.addResult(0, &int0);
store.addResult(-1, &int2);
@@ -237,7 +248,9 @@ void tst_QtConcurrentResultStore::resultAt()
void tst_QtConcurrentResultStore::contains()
{
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
QCOMPARE(store.contains(0), false);
QCOMPARE(store.contains(1), false);
QCOMPARE(store.contains(INT_MAX), false);
@@ -249,7 +262,9 @@ void tst_QtConcurrentResultStore::contains()
QVERIFY(store.contains(int2));
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(1, &int0);
store.addResult(3, &int0);
store.addResults(6, &vec0);
@@ -264,7 +279,9 @@ void tst_QtConcurrentResultStore::contains()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(1, &int0);
store.addResult(3, &int0);
@@ -292,7 +309,9 @@ void tst_QtConcurrentResultStore::contains()
QCOMPARE(store.contains(7), false);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addCanceledResult(0);
QCOMPARE(store.contains(0), false);
@@ -306,7 +325,9 @@ void tst_QtConcurrentResultStore::contains()
void tst_QtConcurrentResultStore::filterMode()
{
// Test filter mode, where "gaps" in the result array aren't allowed.
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
QCOMPARE(store.filterMode(), false);
store.setFilterMode(true);
QVERIFY(store.filterMode());
@@ -338,12 +359,22 @@ void tst_QtConcurrentResultStore::filterMode()
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
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -383,7 +414,9 @@ void tst_QtConcurrentResultStore::count()
{
// test resultCount in non-filtered mode. It should always be possible
// to iterate through the results 0 to resultCount.
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(0, &int0);
QCOMPARE(store.count(), 1);
@@ -397,7 +430,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(2, &int0);
QCOMPARE(store.count(), 0);
@@ -409,7 +444,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -421,7 +458,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -429,7 +468,9 @@ void tst_QtConcurrentResultStore::count()
QCOMPARE(store.count(), 4);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -441,7 +482,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -454,7 +497,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -464,7 +509,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -477,5 +524,75 @@ void tst_QtConcurrentResultStore::count()
}
}
+// 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;
+ };
+
+ size_t 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"
diff --git a/tests/auto/corelib/thread/qsemaphore/BLACKLIST b/tests/auto/corelib/thread/qsemaphore/BLACKLIST
index f7f6d5149c..ecd42cff7c 100644
--- a/tests/auto/corelib/thread/qsemaphore/BLACKLIST
+++ b/tests/auto/corelib/thread/qsemaphore/BLACKLIST
@@ -1,8 +1,2 @@
[tryAcquireWithTimeout]
-osx
-[tryAcquireWithTimeout:0.2s]
-windows
-macos
-[tryAcquireWithTimeout:2s]
-windows
macos
diff --git a/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt b/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
index d83e088004..9f8a87558a 100644
--- a/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qsemaphore.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qsemaphore Test:
#####################################################################
-qt_add_test(tst_qsemaphore
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qsemaphore LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qsemaphore
SOURCES
tst_qsemaphore.cpp
)
diff --git a/tests/auto/corelib/thread/qsemaphore/qsemaphore.pro b/tests/auto/corelib/thread/qsemaphore/qsemaphore.pro
deleted file mode 100644
index 5a0f0337e6..0000000000
--- a/tests/auto/corelib/thread/qsemaphore/qsemaphore.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qsemaphore
-QT = core testlib
-SOURCES = tst_qsemaphore.cpp
diff --git a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
index cf27c23591..3bb1e1960c 100644
--- a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
+++ b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
@@ -1,37 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#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
@@ -47,9 +26,10 @@ private slots:
void tryAcquireWithTimeoutForever();
void producerConsumer();
void raii();
+ void stdCompat();
};
-static QSemaphore *semaphore = 0;
+static QSemaphore *semaphore = nullptr;
class ThreadOne : public QThread
{
@@ -106,7 +86,7 @@ void tst_QSemaphore::acquire()
QVERIFY(t2.wait(4000));
delete semaphore;
- semaphore = 0;
+ semaphore = nullptr;
}
// old incrementN() test
@@ -126,7 +106,7 @@ void tst_QSemaphore::acquire()
QVERIFY(t2.wait(4000));
delete semaphore;
- semaphore = 0;
+ semaphore = nullptr;
}
QSemaphore semaphore;
@@ -177,7 +157,7 @@ void tst_QSemaphore::multiRelease()
// wait for all threads to reach the sem.acquire() and then
// release them all
QTest::qSleep(1);
- sem.release(threads.size());
+ sem.release(int(threads.size()));
for (Thread *&t : threads)
t->wait();
@@ -323,69 +303,69 @@ void tst_QSemaphore::tryAcquireWithTimeout()
QCOMPARE(semaphore.available(), 1);
time.start();
QVERIFY(!semaphore.tryAcquire(2, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 1);
semaphore.release();
QCOMPARE(semaphore.available(), 2);
time.start();
QVERIFY(!semaphore.tryAcquire(3, timeout));
- FUZZYCOMPARE(time.elapsed(), 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(time.elapsed(), 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(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 22);
time.start();
QVERIFY(semaphore.tryAcquire(1, timeout));
- FUZZYCOMPARE(time.elapsed(), 0);
+ FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 21);
time.start();
QVERIFY(semaphore.tryAcquire(1, timeout));
- FUZZYCOMPARE(time.elapsed(), 0);
+ FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 20);
time.start();
QVERIFY(semaphore.tryAcquire(10, timeout));
- FUZZYCOMPARE(time.elapsed(), 0);
+ FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 10);
time.start();
QVERIFY(semaphore.tryAcquire(10, timeout));
- FUZZYCOMPARE(time.elapsed(), 0);
+ FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 0);
// should not be able to acquire more
time.start();
QVERIFY(!semaphore.tryAcquire(1, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(1, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(10, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(10, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
#undef FUZZYCOMPARE
@@ -481,7 +461,7 @@ const char alphabet[] = "ACGTH";
const int AlphabetSize = sizeof(alphabet) - 1;
const int BufferSize = 4096; // GCD of BufferSize and alphabet size must be 1
-char buffer[BufferSize];
+static char buffer[BufferSize];
const int ProducerChunkSize = 3;
const int ConsumerChunkSize = 7;
@@ -491,8 +471,8 @@ const int Multiplier = 10;
// ProducerChunkSize, ConsumerChunkSize, and BufferSize
const int DataSize = ProducerChunkSize * ConsumerChunkSize * BufferSize * Multiplier;
-QSemaphore freeSpace(BufferSize);
-QSemaphore usedSpace;
+static QSemaphore freeSpace(BufferSize);
+static QSemaphore usedSpace;
class Producer : public QThread
{
@@ -500,7 +480,7 @@ public:
void run() override;
};
-static const int Timeout = 60 * 1000; // 1min
+static const auto Timeout = 1min;
void Producer::run()
{
@@ -599,5 +579,34 @@ void tst_QSemaphore::raii()
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"
diff --git a/tests/auto/corelib/thread/qthread/.prev_CMakeLists.txt b/tests/auto/corelib/thread/qthread/.prev_CMakeLists.txt
deleted file mode 100644
index 2c92a710b3..0000000000
--- a/tests/auto/corelib/thread/qthread/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-# Generated from qthread.pro.
-
-#####################################################################
-## tst_qthread Test:
-#####################################################################
-
-qt_add_test(tst_qthread
- SOURCES
- ../../../../shared/emulationdetector.h
- tst_qthread.cpp
- INCLUDE_DIRECTORIES
- ../../../../shared
-)
-
-## Scopes:
-#####################################################################
diff --git a/tests/auto/corelib/thread/qthread/BLACKLIST b/tests/auto/corelib/thread/qthread/BLACKLIST
index c683154da1..08e9912455 100644
--- a/tests/auto/corelib/thread/qthread/BLACKLIST
+++ b/tests/auto/corelib/thread/qthread/BLACKLIST
@@ -1,5 +1,3 @@
[wait3_slowDestructor]
windows-10
-[sleep]
-windows-7sp1
diff --git a/tests/auto/corelib/thread/qthread/CMakeLists.txt b/tests/auto/corelib/thread/qthread/CMakeLists.txt
index 2c92a710b3..abcea1ef9c 100644
--- a/tests/auto/corelib/thread/qthread/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qthread/CMakeLists.txt
@@ -1,15 +1,21 @@
-# Generated from qthread.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthread Test:
#####################################################################
-qt_add_test(tst_qthread
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qthread LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qthread
SOURCES
- ../../../../shared/emulationdetector.h
tst_qthread.cpp
- INCLUDE_DIRECTORIES
- ../../../../shared
+ LIBRARIES
+ Qt::TestPrivate
)
## Scopes:
diff --git a/tests/auto/corelib/thread/qthread/qthread.pro b/tests/auto/corelib/thread/qthread/qthread.pro
deleted file mode 100644
index 37552f1fca..0000000000
--- a/tests/auto/corelib/thread/qthread/qthread.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qthread
-QT = core testlib
-SOURCES = tst_qthread.cpp
-qtConfig(c++14):CONFIG += c++14
-qtConfig(c++1z):CONFIG += c++1z
-
-INCLUDEPATH += ../../../../shared/
-HEADERS += ../../../../shared/emulationdetector.h
diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
index 8cb846d98f..18c8d5fbd5 100644
--- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp
+++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
@@ -1,32 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QTestEventLoop>
+#include <QSignalSpy>
+#include <QSemaphore>
+#include <QAbstractEventDispatcher>
+#if defined(Q_OS_WIN32)
+#include <QWinEventNotifier>
+#endif
#include <qcoreapplication.h>
#include <qelapsedtimer.h>
@@ -36,12 +18,15 @@
#include <qwaitcondition.h>
#include <qdebug.h>
#include <qmetaobject.h>
+#include <qscopeguard.h>
+#include <private/qobject_p.h>
+#include <private/qthread_p.h>
#ifdef Q_OS_UNIX
#include <pthread.h>
#endif
#if defined(Q_OS_WIN)
-#include <windows.h>
+#include <qt_windows.h>
#if defined(Q_OS_WIN32)
#include <process.h>
#endif
@@ -51,7 +36,9 @@
#include <exception>
#endif
-#include "emulationdetector.h"
+#include <QtTest/private/qemulationdetector_p.h>
+
+using namespace std::chrono_literals;
class tst_QThread : public QObject
{
@@ -66,6 +53,7 @@ private slots:
void setStackSize();
void exit();
void start();
+ void startSlotUsedInStringBasedLookups();
void terminate();
void quit();
void started();
@@ -85,6 +73,7 @@ private slots:
void adoptedThreadExecFinished();
void adoptMultipleThreads();
void adoptMultipleThreadsOverlap();
+ void adoptedThreadBindingStatus();
void exitAndStart();
void exitAndExec();
@@ -106,6 +95,13 @@ private slots:
void quitLock();
void create();
+ void createDestruction();
+ void threadIdReuse();
+
+ void terminateAndPrematureDestruction();
+ void terminateAndDoubleDestruction();
+
+ void bindingListCleanupAfterDelete();
};
enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute };
@@ -141,11 +137,13 @@ class Current_Thread : public QThread
public:
Qt::HANDLE id;
QThread *thread;
+ bool runCalledInCurrentThread = false;
void run() override
{
id = QThread::currentThreadId();
thread = QThread::currentThread();
+ runCalledInCurrentThread = thread->isCurrentThread();
}
};
@@ -241,9 +239,9 @@ public:
enum SleepType { Second, Millisecond, Microsecond };
SleepType sleepType;
- int interval;
+ ulong interval;
- int elapsed; // result, in *MILLISECONDS*
+ qint64 elapsed; // result, in *MILLISECONDS*
void run() override
{
@@ -252,17 +250,19 @@ public:
elapsed = 0;
QElapsedTimer timer;
timer.start();
+ std::chrono::nanoseconds dur{0};
switch (sleepType) {
case Second:
- sleep(interval);
+ dur = std::chrono::seconds{interval};
break;
case Millisecond:
- msleep(interval);
+ dur = std::chrono::milliseconds{interval};
break;
case Microsecond:
- usleep(interval);
+ dur = std::chrono::microseconds{interval};
break;
}
+ sleep(dur);
elapsed = timer.elapsed();
cond.wakeOne();
@@ -272,25 +272,30 @@ public:
void tst_QThread::currentThreadId()
{
Current_Thread thread;
- thread.id = 0;
- thread.thread = 0;
+ thread.id = nullptr;
+ thread.thread = nullptr;
thread.start();
QVERIFY(thread.wait(five_minutes));
- QVERIFY(thread.id != 0);
+ QVERIFY(thread.id != nullptr);
QVERIFY(thread.id != QThread::currentThreadId());
+ QVERIFY(!thread.isCurrentThread());
+ QVERIFY(!thread.thread->isCurrentThread());
+ QVERIFY(thread.QThread::thread()->isCurrentThread());
+ QVERIFY(thread.runCalledInCurrentThread);
+ QVERIFY(qApp->thread()->isCurrentThread());
}
void tst_QThread::currentThread()
{
- QVERIFY(QThread::currentThread() != 0);
+ QVERIFY(QThread::currentThread() != nullptr);
QCOMPARE(QThread::currentThread(), thread());
Current_Thread thread;
- thread.id = 0;
- thread.thread = 0;
+ thread.id = nullptr;
+ thread.thread = nullptr;
thread.start();
QVERIFY(thread.wait(five_minutes));
- QCOMPARE(thread.thread, (QThread *)&thread);
+ QCOMPARE(thread.thread, static_cast<QThread *>(&thread));
}
void tst_QThread::idealThreadCount()
@@ -429,7 +434,7 @@ void tst_QThread::exit()
delete thread.object;
Exit_Thread thread2;
- thread2.object = 0;
+ thread2.object = nullptr;
thread2.code = 53;
thread2.result = 0;
QMutexLocker locker2(&thread2.mutex);
@@ -469,11 +474,65 @@ void tst_QThread::start()
}
}
+class QThreadStarter : public QObject
+{
+ Q_OBJECT
+public:
+ using QObject::QObject;
+Q_SIGNALS:
+ void start(QThread::Priority);
+};
+
+class QThreadSelfStarter : public QThread
+{
+ Q_OBJECT
+public:
+ using QThread::QThread;
+
+ void check()
+ {
+ QVERIFY(connect(this, SIGNAL(starting(Priority)),
+ this, SLOT(start(Priority))));
+ QVERIFY(QMetaObject::invokeMethod(this, "start", Q_ARG(Priority, IdlePriority)));
+ }
+
+Q_SIGNALS:
+ void starting(Priority);
+};
+
+void tst_QThread::startSlotUsedInStringBasedLookups()
+{
+ // QTBUG-124723
+
+ QThread thread;
+ {
+ QThreadStarter starter;
+ QVERIFY(QObject::connect(&starter, SIGNAL(start(QThread::Priority)),
+ &thread, SLOT(start(QThread::Priority))));
+ }
+ {
+ QThreadSelfStarter selfStarter;
+ selfStarter.check();
+ if (QTest::currentTestFailed())
+ return;
+ selfStarter.exit();
+ selfStarter.wait(30s);
+ }
+ QVERIFY(QMetaObject::invokeMethod(&thread, "start",
+ Q_ARG(QThread::Priority, QThread::IdlePriority)));
+ thread.exit();
+ thread.wait(30s);
+}
+
void tst_QThread::terminate()
{
#if defined(Q_OS_ANDROID)
QSKIP("Thread termination is not supported on Android.");
#endif
+#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+ QSKIP("Thread termination might result in stack underflow address sanitizer errors.");
+#endif
+
Terminate_Thread thread;
{
QMutexLocker locker(&thread.mutex);
@@ -505,7 +564,7 @@ void tst_QThread::quit()
delete thread.object;
Quit_Thread thread2;
- thread2.object = 0;
+ thread2.object = nullptr;
thread2.result = -1;
QMutexLocker locker2(&thread2.mutex);
thread2.start();
@@ -540,6 +599,10 @@ void tst_QThread::terminated()
#if defined(Q_OS_ANDROID)
QSKIP("Thread termination is not supported on Android.");
#endif
+#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+ QSKIP("Thread termination might result in stack underflow address sanitizer errors.");
+#endif
+
SignalRecorder recorder;
Terminate_Thread thread;
connect(&thread, SIGNAL(finished()), &recorder, SLOT(slot()), Qt::DirectConnection);
@@ -646,9 +709,9 @@ void noop(void*) { }
class NativeThreadWrapper
{
public:
- NativeThreadWrapper() : qthread(0), waitForStop(false) {}
- void start(FunctionPointer functionPointer = noop, void *data = 0);
- void startAndWait(FunctionPointer functionPointer = noop, void *data = 0);
+ NativeThreadWrapper() : qthread(nullptr), waitForStop(false) {}
+ void start(FunctionPointer functionPointer = noop, void *data = nullptr);
+ void startAndWait(FunctionPointer functionPointer = noop, void *data = nullptr);
void join();
void setWaitForStop() { waitForStop = true; }
void stop();
@@ -672,8 +735,8 @@ void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data)
this->functionPointer = functionPointer;
this->data = data;
#if defined Q_OS_UNIX
- const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this);
- Q_UNUSED(state);
+ const int state = pthread_create(&nativeThreadHandle, nullptr, NativeThreadWrapper::runUnix, this);
+ Q_UNUSED(state)
#elif defined Q_OS_WIN
unsigned thrdid = 0;
nativeThreadHandle = (Qt::HANDLE) _beginthreadex(NULL, 0, NativeThreadWrapper::runWin, this, 0, &thrdid);
@@ -690,7 +753,7 @@ void NativeThreadWrapper::startAndWait(FunctionPointer functionPointer, void *da
void NativeThreadWrapper::join()
{
#if defined Q_OS_UNIX
- pthread_join(nativeThreadHandle, 0);
+ pthread_join(nativeThreadHandle, nullptr);
#elif defined Q_OS_WIN
WaitForSingleObjectEx(nativeThreadHandle, INFINITE, FALSE);
CloseHandle(nativeThreadHandle);
@@ -720,7 +783,7 @@ void *NativeThreadWrapper::runUnix(void *that)
nativeThreadWrapper->stopCondition.wait(lock.mutex());
}
- return 0;
+ return nullptr;
}
unsigned WIN_FIX_STDCALL NativeThreadWrapper::runWin(void *data)
@@ -736,12 +799,12 @@ void NativeThreadWrapper::stop()
stopCondition.wakeOne();
}
-bool threadAdoptedOk = false;
-QThread *mainThread;
+static bool threadAdoptedOk = false;
+static QThread *mainThread;
void testNativeThreadAdoption(void *)
{
- threadAdoptedOk = (QThread::currentThreadId() != 0
- && QThread::currentThread() != 0
+ threadAdoptedOk = (QThread::currentThreadId() != nullptr
+ && QThread::currentThread() != nullptr
&& QThread::currentThread() != mainThread);
}
void tst_QThread::nativeThreadAdoption()
@@ -769,14 +832,15 @@ void adoptedThreadAffinityFunction(void *arg)
void tst_QThread::adoptedThreadAffinity()
{
- QThread *affinity[2] = { 0, 0 };
+ QThread *affinity[2] = { nullptr, nullptr };
NativeThreadWrapper thread;
thread.startAndWait(adoptedThreadAffinityFunction, affinity);
thread.join();
- // adopted thread should have affinity to itself
- QCOMPARE(affinity[0], affinity[1]);
+ // adopted thread (deleted) should have affinity to itself
+ QCOMPARE(static_cast<const void *>(affinity[0]),
+ static_cast<const void *>(affinity[1]));
}
void tst_QThread::adoptedThreadSetPriority()
@@ -944,10 +1008,24 @@ void tst_QThread::adoptMultipleThreadsOverlap()
QCOMPARE(recorder.activationCount.loadRelaxed(), numThreads);
}
+void tst_QThread::adoptedThreadBindingStatus()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+
+ nativeThread.startAndWait();
+ QVERIFY(nativeThread.qthread);
+ auto privThread = static_cast<QThreadPrivate *>(QObjectPrivate::get(nativeThread.qthread));
+ QVERIFY(privThread->m_statusOrPendingObjects.bindingStatus());
+
+ nativeThread.stop();
+ nativeThread.join();
+}
+
// Disconnects on WinCE
void tst_QThread::stressTest()
{
- if (EmulationDetector::isRunningArmOnX86())
+ if (QTestPrivate::isRunningArmOnX86())
QSKIP("Qemu uses too much memory for each thread. Test would run out of memory.");
QElapsedTimer timer;
@@ -1081,8 +1159,8 @@ void tst_QThread::wait2()
qPrintable(msgElapsed(elapsed)));
}
-
-class SlowSlotObject : public QObject {
+class SlowSlotObject : public QObject
+{
Q_OBJECT
public:
QMutex mutex;
@@ -1098,22 +1176,23 @@ void tst_QThread::wait3_slowDestructor()
{
SlowSlotObject slow;
QThread thread;
- QObject::connect(&thread, SIGNAL(finished()), &slow, SLOT(slowSlot()), Qt::DirectConnection);
-
- enum { WaitTime = 1800 };
+ QObject::connect(&thread, &QThread::finished,
+ &slow, &SlowSlotObject::slowSlot, Qt::DirectConnection);
QElapsedTimer timer;
thread.start();
thread.quit();
- //the quit function will cause the thread to finish and enter the slowSlot that is blocking
+ // Calling quit() will cause the thread to finish and enter the blocking slowSlot().
timer.start();
- QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
- qint64 elapsed = timer.elapsed();
- QVERIFY2(elapsed >= Waiting_Thread::WaitTime - 1, qPrintable(QString::fromLatin1("elapsed: %1").arg(elapsed)));
-
- slow.cond.wakeOne();
- //now the thread should finish quickly
+ {
+ // Ensure thread finishes quickly after the checks - regardless of success:
+ QScopeGuard wakeSlow([&slow]() -> void { slow.cond.wakeOne(); });
+ QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
+ const qint64 elapsed = timer.elapsed();
+ QVERIFY2(elapsed >= Waiting_Thread::WaitTime - 1,
+ qPrintable(QString::fromLatin1("elapsed: %1").arg(elapsed)));
+ }
QVERIFY(thread.wait(one_minute));
}
@@ -1142,7 +1221,7 @@ void tst_QThread::startFinishRace()
void run() override
{
i--;
- if (!i) disconnect(this, SIGNAL(finished()), 0, 0);
+ if (!i) disconnect(this, SIGNAL(finished()), nullptr, nullptr);
}
int i;
};
@@ -1208,13 +1287,10 @@ void tst_QThread::isRunningInFinished()
}
}
-QT_BEGIN_NAMESPACE
-Q_CORE_EXPORT uint qGlobalPostedEventsCount();
-QT_END_NAMESPACE
-
-class DummyEventDispatcher : public QAbstractEventDispatcher {
+class DummyEventDispatcher : public QAbstractEventDispatcherV2
+{
+ Q_OBJECT
public:
- DummyEventDispatcher() : QAbstractEventDispatcher() {}
bool processEvents(QEventLoop::ProcessEventsFlags) override {
visited.storeRelaxed(true);
emit awake();
@@ -1223,11 +1299,19 @@ public:
}
void registerSocketNotifier(QSocketNotifier *) override {}
void unregisterSocketNotifier(QSocketNotifier *) override {}
- void registerTimer(int, int, Qt::TimerType, QObject *) override {}
- bool unregisterTimer(int) override { return false; }
+ void registerTimer(Qt::TimerId id, Duration, Qt::TimerType, QObject *) override
+ {
+ if (registeredTimerId <= Qt::TimerId::Invalid)
+ registeredTimerId = id;
+ }
+ bool unregisterTimer(Qt::TimerId id) override
+ {
+ Qt::TimerId oldId = std::exchange(registeredTimerId, Qt::TimerId::Invalid);
+ return id == oldId;
+ }
bool unregisterTimers(QObject *) override { return false; }
- QList<TimerInfo> registeredTimers(QObject *) const override { return QList<TimerInfo>(); }
- int remainingTime(int) override { return 0; }
+ QList<TimerInfoV2> timersForObject(QObject *) const override { return {}; }
+ Duration remainingTime(Qt::TimerId) const override { return 0s; }
void wakeUp() override {}
void interrupt() override {}
@@ -1237,25 +1321,47 @@ public:
#endif
QBasicAtomicInt visited; // bool
+ Qt::TimerId registeredTimerId = Qt::TimerId::Invalid;
};
-class ThreadObj : public QObject
+struct ThreadLocalContent
{
- Q_OBJECT
-public slots:
- void visit() {
- emit visited();
+ static inline const QMetaObject *atStart;
+ static inline const QMetaObject *atEnd;
+ QSemaphore *sem;
+ QBasicTimer timer;
+
+ ThreadLocalContent(QObject *obj, QSemaphore *sem)
+ : sem(sem)
+ {
+ ensureEventDispatcher();
+ atStart = QAbstractEventDispatcher::instance()->metaObject();
+ timer.start(10s, obj);
+ }
+ ~ThreadLocalContent()
+ {
+ ensureEventDispatcher();
+ atEnd = QAbstractEventDispatcher::instance()->metaObject();
+ timer.stop();
+ sem->release();
+ }
+
+ void ensureEventDispatcher()
+ {
+ // QEventLoop's constructor has a call to QThreadData::ensureEventDispatcher()
+ QEventLoop dummy;
}
-signals:
- void visited();
};
void tst_QThread::customEventDispatcher()
{
+ ThreadLocalContent::atStart = ThreadLocalContent::atEnd = nullptr;
+
QThread thr;
// there should be no ED yet
QVERIFY(!thr.eventDispatcher());
DummyEventDispatcher *ed = new DummyEventDispatcher;
+ QPointer<DummyEventDispatcher> weak_ed(ed);
thr.setEventDispatcher(ed);
// the new ED should be set
QCOMPARE(thr.eventDispatcher(), ed);
@@ -1264,32 +1370,46 @@ void tst_QThread::customEventDispatcher()
thr.start();
// start() should not overwrite the ED
QCOMPARE(thr.eventDispatcher(), ed);
+ QVERIFY(!weak_ed.isNull());
- ThreadObj obj;
+ QObject obj;
obj.moveToThread(&thr);
// move was successful?
QCOMPARE(obj.thread(), &thr);
- QEventLoop loop;
- connect(&obj, SIGNAL(visited()), &loop, SLOT(quit()), Qt::QueuedConnection);
- QMetaObject::invokeMethod(&obj, "visit", Qt::QueuedConnection);
- loop.exec();
+
+ QSemaphore threadLocalSemaphore;
+ QMetaObject::invokeMethod(&obj, [&]() {
+#ifndef Q_OS_WIN
+ // On Windows, the thread_locals are unsequenced between DLLs, so this
+ // could run after QThreadPrivate::finish()
+ static thread_local
+#endif
+ ThreadLocalContent d(&obj, &threadLocalSemaphore);
+ }, Qt::BlockingQueuedConnection);
+
// test that the ED has really been used
QVERIFY(ed->visited.loadRelaxed());
+ // and it's ours
+ QCOMPARE(ThreadLocalContent::atStart->className(), "DummyEventDispatcher");
- QPointer<DummyEventDispatcher> weak_ed(ed);
QVERIFY(!weak_ed.isNull());
thr.quit();
+
// wait for thread to be stopped
QVERIFY(thr.wait(30000));
+ QVERIFY(threadLocalSemaphore.tryAcquire(1, 30s));
+
// test that ED has been deleted
QVERIFY(weak_ed.isNull());
+ // test that ED was ours
+ QCOMPARE(ThreadLocalContent::atEnd->className(), "DummyEventDispatcher");
}
class Job : public QObject
{
Q_OBJECT
public:
- Job(QThread *thread, int deleteDelay, bool *flag, QObject *parent = 0)
+ Job(QThread *thread, int deleteDelay, bool *flag, QObject *parent = nullptr)
: QObject(parent), quitLocker(thread), exitThreadCalled(*flag)
{
exitThreadCalled = false;
@@ -1338,9 +1458,6 @@ void tst_QThread::quitLock()
void tst_QThread::create()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
{
const auto &function = [](){};
QScopedPointer<QThread> thread(QThread::create(function));
@@ -1576,11 +1693,70 @@ void tst_QThread::create()
const auto &function = [](const ThrowWhenCopying &){};
QScopedPointer<QThread> thread;
ThrowWhenCopying t;
- QVERIFY_EXCEPTION_THROWN(thread.reset(QThread::create(function, t)), ThreadException);
+ QVERIFY_THROWS_EXCEPTION(ThreadException, thread.reset(QThread::create(function, t)));
QVERIFY(!thread);
}
#endif // QT_NO_EXCEPTIONS
-#endif // QT_CONFIG(cxx11_future)
+}
+
+void tst_QThread::createDestruction()
+{
+ for (int delay : {0, 10, 20}) {
+ auto checkForInterruptions = []() {
+ for (;;) {
+ if (QThread::currentThread()->isInterruptionRequested())
+ return;
+ QThread::sleep(1ms);
+ }
+ };
+
+ QScopedPointer<QThread> thread(QThread::create(checkForInterruptions));
+ QSignalSpy finishedSpy(thread.get(), &QThread::finished);
+ QVERIFY(finishedSpy.isValid());
+
+ thread->start();
+ if (delay)
+ QThread::msleep(delay);
+ thread.reset();
+
+ QCOMPARE(finishedSpy.size(), 1);
+ }
+
+ for (int delay : {0, 10, 20}) {
+ auto runEventLoop = []() {
+ QEventLoop loop;
+ loop.exec();
+ };
+
+ QScopedPointer<QThread> thread(QThread::create(runEventLoop));
+ QSignalSpy finishedSpy(thread.get(), &QThread::finished);
+ QVERIFY(finishedSpy.isValid());
+
+ thread->start();
+ if (delay)
+ QThread::msleep(delay);
+ thread.reset();
+
+ QCOMPARE(finishedSpy.size(), 1);
+ }
+
+ for (int delay : {0, 10, 20}) {
+ auto runEventLoop = [delay]() {
+ if (delay)
+ QThread::msleep(delay);
+ QEventLoop loop;
+ loop.exec();
+ };
+
+ QScopedPointer<QThread> thread(QThread::create(runEventLoop));
+ QSignalSpy finishedSpy(thread.get(), &QThread::finished);
+ QVERIFY(finishedSpy.isValid());
+
+ thread->start();
+ thread.reset();
+
+ QCOMPARE(finishedSpy.size(), 1);
+ }
}
class StopableJob : public QObject
@@ -1621,5 +1797,155 @@ void tst_QThread::requestTermination()
QVERIFY(!thread.isInterruptionRequested());
}
+/*
+ This is a regression test for QTBUG-96846.
+
+ Incorrect system thread ID cleanup can cause QThread::wait() to report that
+ a thread is trying to wait for itself.
+*/
+void tst_QThread::threadIdReuse()
+{
+ // It's important that those thread ID's are not accessed concurrently
+ Qt::HANDLE threadId1;
+
+ auto thread1Fn = [&threadId1]() -> void { threadId1 = QThread::currentThreadId(); };
+ QScopedPointer<QThread> thread1(QThread::create(thread1Fn));
+ thread1->start();
+ QVERIFY(thread1->wait());
+
+ // If the system thread allocated for thread1 is destroyed before thread2 is started,
+ // at least on some versions of Linux the system thread ID for thread2 would be the
+ // same as one that was used for thread1.
+
+ // The system thread may be alive for some time after returning from QThread::wait()
+ // because the implementation is using detachable threads, so some additional time is
+ // required for the system thread to terminate. Not waiting long enough here would result
+ // in a new system thread ID being allocated for thread2 and this test passing even without
+ // a fix for QTBUG-96846.
+ bool threadIdReused = false;
+
+ for (int i = 0; i < 42; i++) {
+ QThread::sleep(1ms);
+
+ Qt::HANDLE threadId2;
+ bool waitOk = false;
+
+ auto waitForThread1 = [&thread1, &threadId2, &waitOk]() -> void {
+ threadId2 = QThread::currentThreadId();
+ waitOk = thread1->wait();
+ };
+
+ QScopedPointer<QThread> thread2(QThread::create(waitForThread1));
+ thread2->start();
+ QVERIFY(thread2->wait());
+ QVERIFY(waitOk);
+
+ if (threadId1 == threadId2) {
+ qDebug("Thread ID reused at iteration %d", i);
+ threadIdReused = true;
+ break;
+ }
+ }
+
+ if (!threadIdReused) {
+ QSKIP("Thread ID was not reused");
+ }
+}
+
+class WaitToRun_Thread : public QThread
+{
+ Q_OBJECT
+public:
+ void run() override
+ {
+ emit running();
+ QThread::exec();
+ }
+
+Q_SIGNALS:
+ void running();
+};
+
+
+void tst_QThread::terminateAndPrematureDestruction()
+{
+#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+ QSKIP("Thread termination might result in stack underflow address sanitizer errors.");
+#endif
+
+ WaitToRun_Thread thread;
+ QSignalSpy spy(&thread, &WaitToRun_Thread::running);
+ thread.start();
+ QVERIFY(spy.wait(500));
+
+ QScopedPointer<QObject> obj(new QObject);
+ QPointer<QObject> pObj(obj.data());
+ obj->deleteLater();
+
+ thread.terminate();
+ QVERIFY2(pObj, "object was deleted prematurely!");
+ thread.wait(500);
+}
+
+void tst_QThread::terminateAndDoubleDestruction()
+{
+#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+ QSKIP("Thread termination might result in stack underflow address sanitizer errors.");
+#endif
+
+ class ChildObject : public QObject
+ {
+ public:
+ ChildObject(QObject *parent)
+ : QObject(parent)
+ {
+ QSignalSpy spy(&thread, &WaitToRun_Thread::running);
+ thread.start();
+ spy.wait(500);
+ }
+
+ ~ChildObject()
+ {
+ QVERIFY2(!inDestruction, "Double object destruction!");
+ inDestruction = true;
+ thread.terminate();
+ thread.wait(500);
+ }
+
+ bool inDestruction = false;
+ WaitToRun_Thread thread;
+ };
+
+ class TestObject : public QObject
+ {
+ public:
+ TestObject()
+ : child(new ChildObject(this))
+ {
+ }
+
+ ~TestObject()
+ {
+ child->deleteLater();
+ }
+
+ ChildObject *child = nullptr;
+ };
+
+ TestObject obj;
+}
+
+void tst_QThread::bindingListCleanupAfterDelete()
+{
+ QThread t;
+ auto optr = std::make_unique<QObject>();
+ optr->moveToThread(&t);
+ auto threadPriv = static_cast<QThreadPrivate *>(QObjectPrivate::get(&t));
+ auto list = threadPriv->m_statusOrPendingObjects.list();
+ QVERIFY(list);
+ optr.reset();
+ QVERIFY(list->empty());
+}
+
QTEST_MAIN(tst_QThread)
#include "tst_qthread.moc"
diff --git a/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt b/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
index 1b5b77cfd8..2c92ca002e 100644
--- a/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
@@ -1,11 +1,20 @@
-# Generated from qthreadonce.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthreadonce Test:
#####################################################################
-qt_add_test(tst_qthreadonce
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qthreadonce LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qthreadonce
SOURCES
qthreadonce.cpp
tst_qthreadonce.cpp
+ LIBRARIES
+ Qt::TestPrivate
)
diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
index 3826fa148e..b32f455241 100644
--- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
+++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qplatformdefs.h"
diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.h b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h
index e5918b8fa5..1f804433e4 100644
--- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.h
+++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QTHREADONCE_H
diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro b/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro
deleted file mode 100644
index e8b95a06a7..0000000000
--- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qthreadonce
-QT = core testlib
-SOURCES = tst_qthreadonce.cpp
-
-# Don't use gcc's threadsafe statics
-# Note: some versions of gcc generate invalid code with this option...
-# Some versions of gcc don't even have it, so disable it
-#*-g++*:QMAKE_CXXFLAGS += -fno-threadsafe-statics
-
-# Temporary:
-SOURCES += qthreadonce.cpp
diff --git a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
index 6859a55725..37e1f744f3 100644
--- a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
+++ b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
@@ -1,33 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+
+#include <QTest>
+#include <QSemaphore>
#include <qcoreapplication.h>
#include <qmutex.h>
@@ -35,11 +11,14 @@
#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();
@@ -51,6 +30,12 @@ private slots:
#endif
};
+void tst_QThreadOnce::initTestCase()
+{
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Flaky on QEMU, QTBUG-94737");
+}
+
class SingletonObject: public QObject
{
Q_OBJECT
@@ -112,7 +97,7 @@ void tst_QThreadOnce::sameThread()
QCOMPARE(controlVariable, 1);
static QSingleton<SingletonObject> s;
- QTEST((int)s->val.loadRelaxed(), "expectedValue");
+ QTEST(int(s->val.loadRelaxed()), "expectedValue");
s->val.ref();
QCOMPARE(SingletonObject::runCount, 1);
@@ -145,7 +130,7 @@ void tst_QThreadOnce::multipleThreads()
delete parent;
QCOMPARE(controlVariable, 1);
- QCOMPARE((int)IncrementThread::runCount.loadRelaxed(), NumberOfThreads);
+ QCOMPARE(int(IncrementThread::runCount.loadRelaxed()), NumberOfThreads);
QCOMPARE(SingletonObject::runCount, 1);
}
diff --git a/tests/auto/corelib/thread/qthreadpool/BLACKLIST b/tests/auto/corelib/thread/qthreadpool/BLACKLIST
deleted file mode 100644
index b8c1f3bf3f..0000000000
--- a/tests/auto/corelib/thread/qthreadpool/BLACKLIST
+++ /dev/null
@@ -1,4 +0,0 @@
-[expiryTimeoutRace]
-opensuse-leap
-ubuntu
-rhel
diff --git a/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt b/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
index 8d2c3719c6..fee9c541db 100644
--- a/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qthreadpool.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthreadpool Test:
#####################################################################
-qt_add_test(tst_qthreadpool
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qthreadpool LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qthreadpool
SOURCES
tst_qthreadpool.cpp
)
diff --git a/tests/auto/corelib/thread/qthreadpool/qthreadpool.pro b/tests/auto/corelib/thread/qthreadpool/qthreadpool.pro
deleted file mode 100644
index eac4ef9365..0000000000
--- a/tests/auto/corelib/thread/qthreadpool/qthreadpool.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qthreadpool
-QT = core testlib
-SOURCES = tst_qthreadpool.cpp
diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
index 9b536aec00..2006016d47 100644
--- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
+++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
@@ -1,33 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QSemaphore>
+
#include <qelapsedtimer.h>
+#include <qrunnable.h>
#include <qthreadpool.h>
#include <qstring.h>
#include <qmutex.h>
@@ -36,6 +15,8 @@
#include <unistd.h>
#endif
+using namespace std::chrono_literals;
+
typedef void (*FunctionPointer)();
class FunctionPointerTask : public QRunnable
@@ -65,6 +46,7 @@ public:
private slots:
void runFunction();
void runFunction2();
+ void runFunction3();
void createThreadRunFunction();
void runMultiple();
void waitcomplete();
@@ -72,6 +54,7 @@ private slots:
void singleton();
void destruction();
void threadRecycling();
+ void threadPriority();
void expiryTimeout();
void expiryTimeoutRace();
#ifndef QT_NO_EXCEPTIONS
@@ -85,6 +68,8 @@ private slots:
void releaseThread_data();
void releaseThread();
void reserveAndStart();
+ void reserveAndStart2();
+ void releaseAndBlock();
void start();
void tryStart();
void tryStartPeakThreadCount();
@@ -93,6 +78,7 @@ private slots:
void priorityStart();
void waitForDone();
void clear();
+ void clearWithAutoDelete();
void tryTake();
void waitForDoneTimeout();
void destroyingWaitsForTasksToFinish();
@@ -100,13 +86,15 @@ private slots:
void stressTest();
void takeAllAndIncreaseMaxThreadCount();
void waitForDoneAfterTake();
+ void threadReuse();
+ void nullFunctions();
private:
QMutex m_functionTestMutex;
};
-QMutex *tst_QThreadPool::functionTestMutex = 0;
+QMutex *tst_QThreadPool::functionTestMutex = nullptr;
tst_QThreadPool::tst_QThreadPool()
{
@@ -115,10 +103,10 @@ tst_QThreadPool::tst_QThreadPool()
tst_QThreadPool::~tst_QThreadPool()
{
- tst_QThreadPool::functionTestMutex = 0;
+ tst_QThreadPool::functionTestMutex = nullptr;
}
-int testFunctionCount;
+static int testFunctionCount;
void sleepTestFunction()
{
@@ -153,10 +141,25 @@ void noSleepTestFunctionMutex()
tst_QThreadPool::functionTestMutex->unlock();
}
+constexpr int DefaultWaitForDoneTimeout = 1 * 60 * 1000; // 1min
+// Using qFatal instead of QVERIFY to force exit if threads are still running after timeout.
+// Otherwise, QCoreApplication will still wait for the stale threads and never exit the test.
+#define WAIT_FOR_DONE(manager) \
+ if ((manager).waitForDone(DefaultWaitForDoneTimeout)) {} else \
+ qFatal("waitForDone returned false. Aborting to stop background threads.")
+
+// uses explicit timeout in dtor's waitForDone() to avoid tests hanging overly long
+class TestThreadPool : public QThreadPool
+{
+public:
+ using QThreadPool::QThreadPool;
+ ~TestThreadPool() { WAIT_FOR_DONE(*this); }
+};
+
void tst_QThreadPool::runFunction()
{
{
- QThreadPool manager;
+ TestThreadPool manager;
testFunctionCount = 0;
manager.start(noSleepTestFunction);
}
@@ -167,16 +170,33 @@ void tst_QThreadPool::runFunction2()
{
int localCount = 0;
{
- QThreadPool manager;
+ TestThreadPool manager;
manager.start([&]() { ++localCount; });
}
QCOMPARE(localCount, 1);
}
+struct DeleteCheck
+{
+ static bool s_deleted;
+ ~DeleteCheck() { s_deleted = true; }
+};
+bool DeleteCheck::s_deleted = false;
+
+void tst_QThreadPool::runFunction3()
+{
+ std::unique_ptr<DeleteCheck> ptr(new DeleteCheck);
+ {
+ TestThreadPool manager;
+ manager.start([my_ptr = std::move(ptr)]() { });
+ }
+ QVERIFY(DeleteCheck::s_deleted);
+}
+
void tst_QThreadPool::createThreadRunFunction()
{
{
- QThreadPool manager;
+ TestThreadPool manager;
testFunctionCount = 0;
manager.start(noSleepTestFunction);
}
@@ -189,7 +209,7 @@ void tst_QThreadPool::runMultiple()
const int runs = 10;
{
- QThreadPool manager;
+ TestThreadPool manager;
testFunctionCount = 0;
for (int i = 0; i < runs; ++i) {
manager.start(sleepTestFunctionMutex);
@@ -198,7 +218,7 @@ void tst_QThreadPool::runMultiple()
QCOMPARE(testFunctionCount, runs);
{
- QThreadPool manager;
+ TestThreadPool manager;
testFunctionCount = 0;
for (int i = 0; i < runs; ++i) {
manager.start(noSleepTestFunctionMutex);
@@ -207,7 +227,7 @@ void tst_QThreadPool::runMultiple()
QCOMPARE(testFunctionCount, runs);
{
- QThreadPool manager;
+ TestThreadPool manager;
for (int i = 0; i < 500; ++i)
manager.start(emptyFunct);
}
@@ -218,13 +238,14 @@ void tst_QThreadPool::waitcomplete()
testFunctionCount = 0;
const int runs = 500;
for (int i = 0; i < 500; ++i) {
+ // TestThreadPool pool; // no, we're checking ~QThreadPool()'s waitForDone()
QThreadPool pool;
pool.start(noSleepTestFunction);
}
QCOMPARE(testFunctionCount, runs);
}
-QAtomicInt ran; // bool
+static QAtomicInt ran; // bool
class TestTask : public QRunnable
{
public:
@@ -236,7 +257,7 @@ public:
void tst_QThreadPool::runTask()
{
- QThreadPool manager;
+ TestThreadPool manager;
ran.storeRelaxed(false);
manager.start(new TestTask());
QTRY_VERIFY(ran.loadRelaxed());
@@ -252,7 +273,7 @@ void tst_QThreadPool::singleton()
QTRY_VERIFY(ran.loadRelaxed());
}
-QAtomicInt *value = 0;
+static QAtomicInt *value = nullptr;
class IntAccessor : public QRunnable
{
public:
@@ -277,11 +298,11 @@ void tst_QThreadPool::destruction()
threadManager->start(new IntAccessor());
delete threadManager;
delete value;
- value = 0;
+ value = nullptr;
}
-QSemaphore threadRecyclingSemaphore;
-QThread *recycledThread = 0;
+static QSemaphore threadRecyclingSemaphore;
+static QThread *recycledThread = nullptr;
class ThreadRecorderTask : public QRunnable
{
@@ -298,7 +319,7 @@ public:
*/
void tst_QThreadPool::threadRecycling()
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.start(new ThreadRecorderTask());
threadRecyclingSemaphore.acquire();
@@ -319,6 +340,25 @@ void tst_QThreadPool::threadRecycling()
QCOMPARE(thread2, thread3);
}
+/*
+ Test that the thread priority from the thread created by the pool matches
+ the one configured on the pool.
+*/
+void tst_QThreadPool::threadPriority()
+{
+ QThread::Priority priority = QThread::HighPriority;
+ TestThreadPool threadPool;
+ threadPool.setThreadPriority(priority);
+
+ threadPool.start(new ThreadRecorderTask());
+ threadRecyclingSemaphore.acquire();
+ QThread *thread = recycledThread;
+
+ QTest::qSleep(100);
+
+ QCOMPARE(thread->priority(), priority);
+}
+
class ExpiryTimeoutTask : public QRunnable
{
public:
@@ -327,7 +367,7 @@ public:
QSemaphore semaphore;
ExpiryTimeoutTask()
- : thread(0), runCount(0)
+ : thread(nullptr), runCount(0)
{
setAutoDelete(false);
}
@@ -344,7 +384,7 @@ void tst_QThreadPool::expiryTimeout()
{
ExpiryTimeoutTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
int expiryTimeout = threadPool.expiryTimeout();
@@ -378,18 +418,15 @@ void tst_QThreadPool::expiryTimeout()
void tst_QThreadPool::expiryTimeoutRace() // QTBUG-3786
{
-#ifdef Q_OS_WIN
- QSKIP("This test is unstable on Windows. See QTBUG-3786.");
-#endif
ExpiryTimeoutTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
threadPool.setExpiryTimeout(50);
const int numTasks = 20;
for (int i = 0; i < numTasks; ++i) {
threadPool.start(&task);
- QThread::msleep(50); // exactly the same as the expiry timeout
+ QThread::sleep(50ms); // exactly the same as the expiry timeout
}
QVERIFY(task.semaphore.tryAcquire(numTasks, 10000));
QCOMPARE(task.runCount.loadRelaxed(), numTasks);
@@ -400,7 +437,7 @@ void tst_QThreadPool::expiryTimeoutRace() // QTBUG-3786
class ExceptionTask : public QRunnable
{
public:
- void run()
+ void run() override
{
throw new int;
}
@@ -410,7 +447,7 @@ void tst_QThreadPool::exceptions()
{
ExceptionTask task;
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
// Uncomment this for a nice crash.
// threadPool.start(&task);
}
@@ -439,6 +476,9 @@ void tst_QThreadPool::setMaxThreadCount()
QFETCH(int, limit);
QThreadPool *threadPool = QThreadPool::globalInstance();
int savedLimit = threadPool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadPool->setMaxThreadCount(savedLimit);
+ });
// maxThreadCount() should always return the previous argument to
// setMaxThreadCount(), regardless of input
@@ -451,7 +491,7 @@ void tst_QThreadPool::setMaxThreadCount()
// setting the limit on children should have no effect on the parent
{
- QThreadPool threadPool2(threadPool);
+ TestThreadPool threadPool2(threadPool);
savedLimit = threadPool2.maxThreadCount();
// maxThreadCount() should always return the previous argument to
@@ -481,58 +521,57 @@ void tst_QThreadPool::setMaxThreadCountStartsAndStopsThreads()
}
};
- QThreadPool threadPool;
- threadPool.setMaxThreadCount(1);
+ TestThreadPool threadPool;
+ threadPool.setMaxThreadCount(-1); // docs say we'll always start at least one
- WaitingTask *task = new WaitingTask;
- threadPool.start(task);
- QVERIFY(task->waitForStarted.tryAcquire(1, 1000));
+ WaitingTask task;
+ threadPool.start(&task);
+ QVERIFY(task.waitForStarted.tryAcquire(1, 1000));
// thread limit is 1, cannot start more tasks
- threadPool.start(task);
- QVERIFY(!task->waitForStarted.tryAcquire(1, 1000));
+ threadPool.start(&task);
+ QVERIFY(!task.waitForStarted.tryAcquire(1, 1000));
// increasing the limit by 1 should start the task immediately
threadPool.setMaxThreadCount(2);
- QVERIFY(task->waitForStarted.tryAcquire(1, 1000));
+ QVERIFY(task.waitForStarted.tryAcquire(1, 1000));
// ... but we still cannot start more tasks
- threadPool.start(task);
- QVERIFY(!task->waitForStarted.tryAcquire(1, 1000));
+ threadPool.start(&task);
+ QVERIFY(!task.waitForStarted.tryAcquire(1, 1000));
// increasing the limit should be able to start more than one at a time
- threadPool.start(task);
+ threadPool.start(&task);
threadPool.setMaxThreadCount(4);
- QVERIFY(task->waitForStarted.tryAcquire(2, 1000));
+ QVERIFY(task.waitForStarted.tryAcquire(2, 1000));
// ... but we still cannot start more tasks
- threadPool.start(task);
- threadPool.start(task);
- QVERIFY(!task->waitForStarted.tryAcquire(2, 1000));
+ threadPool.start(&task);
+ threadPool.start(&task);
+ QVERIFY(!task.waitForStarted.tryAcquire(2, 1000));
// decreasing the thread limit should cause the active thread count to go down
threadPool.setMaxThreadCount(2);
QCOMPARE(threadPool.activeThreadCount(), 4);
- task->waitToFinish.release(2);
+ task.waitToFinish.release(2);
QTest::qWait(1000);
QCOMPARE(threadPool.activeThreadCount(), 2);
// ... and we still cannot start more tasks
- threadPool.start(task);
- threadPool.start(task);
- QVERIFY(!task->waitForStarted.tryAcquire(2, 1000));
+ threadPool.start(&task);
+ threadPool.start(&task);
+ QVERIFY(!task.waitForStarted.tryAcquire(2, 1000));
// start all remaining tasks
- threadPool.start(task);
- threadPool.start(task);
- threadPool.start(task);
- threadPool.start(task);
+ threadPool.start(&task);
+ threadPool.start(&task);
+ threadPool.start(&task);
+ threadPool.start(&task);
threadPool.setMaxThreadCount(8);
- QVERIFY(task->waitForStarted.tryAcquire(6, 1000));
+ QVERIFY(task.waitForStarted.tryAcquire(6, 1000));
- task->waitToFinish.release(10);
+ task.waitToFinish.release(10);
threadPool.waitForDone();
- delete task;
}
void tst_QThreadPool::reserveThread_data()
@@ -544,7 +583,11 @@ void tst_QThreadPool::reserveThread()
{
QFETCH(int, limit);
QThreadPool *threadpool = QThreadPool::globalInstance();
- int savedLimit = threadpool->maxThreadCount();
+ const int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
+
threadpool->setMaxThreadCount(limit);
// reserve up to the limit
@@ -566,7 +609,7 @@ void tst_QThreadPool::reserveThread()
// reserving threads in children should not effect the parent
{
- QThreadPool threadpool2(threadpool);
+ TestThreadPool threadpool2(threadpool);
threadpool2.setMaxThreadCount(limit);
// reserve up to the limit
@@ -593,9 +636,6 @@ void tst_QThreadPool::reserveThread()
while (threadpool2.activeThreadCount() > 0)
threadpool2.releaseThread();
}
-
- // reset limit on global QThreadPool
- threadpool->setMaxThreadCount(savedLimit);
}
void tst_QThreadPool::releaseThread_data()
@@ -607,7 +647,10 @@ void tst_QThreadPool::releaseThread()
{
QFETCH(int, limit);
QThreadPool *threadpool = QThreadPool::globalInstance();
- int savedLimit = threadpool->maxThreadCount();
+ const int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
threadpool->setMaxThreadCount(limit);
// reserve up to the limit
@@ -630,7 +673,7 @@ void tst_QThreadPool::releaseThread()
// releasing threads in children should not effect the parent
{
- QThreadPool threadpool2(threadpool);
+ TestThreadPool threadpool2(threadpool);
threadpool2.setMaxThreadCount(limit);
// reserve up to the limit
@@ -655,9 +698,6 @@ void tst_QThreadPool::releaseThread()
QCOMPARE(threadpool2.activeThreadCount(), 0);
QCOMPARE(threadpool->activeThreadCount(), 0);
}
-
- // reset limit on global QThreadPool
- threadpool->setMaxThreadCount(savedLimit);
}
void tst_QThreadPool::reserveAndStart() // QTBUG-21051
@@ -682,6 +722,10 @@ void tst_QThreadPool::reserveAndStart() // QTBUG-21051
// Set up
QThreadPool *threadpool = QThreadPool::globalInstance();
int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
+
threadpool->setMaxThreadCount(1);
QCOMPARE(threadpool->activeThreadCount(), 0);
@@ -689,37 +733,133 @@ void tst_QThreadPool::reserveAndStart() // QTBUG-21051
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), 1);
- // start a task, to get a running thread
- WaitingTask *task = new WaitingTask;
- threadpool->start(task);
+ // start a task, to get a running thread, works since one thread is always allowed
+ WaitingTask task;
+ threadpool->start(&task);
QCOMPARE(threadpool->activeThreadCount(), 2);
- task->waitForStarted.acquire();
- task->waitBeforeDone.release();
- QTRY_COMPARE(task->count.loadRelaxed(), 1);
+ // tryStart() will fail since activeThreadCount() >= maxThreadCount() and one thread is already running
+ QVERIFY(!threadpool->tryStart(&task));
+ QTRY_COMPARE(threadpool->activeThreadCount(), 2);
+ task.waitForStarted.acquire();
+ task.waitBeforeDone.release();
+ QTRY_COMPARE(task.count.loadRelaxed(), 1);
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
- // now the thread is waiting, but tryStart() will fail since activeThreadCount() >= maxThreadCount()
- QVERIFY(!threadpool->tryStart(task));
+ // start() will wake up the waiting thread.
+ threadpool->start(&task);
+ QTRY_COMPARE(threadpool->activeThreadCount(), 2);
+ QTRY_COMPARE(task.count.loadRelaxed(), 2);
+ WaitingTask task2;
+ // startOnReservedThread() will try to take the reserved task, but end up waiting instead
+ threadpool->startOnReservedThread(&task2);
+ QTRY_COMPARE(threadpool->activeThreadCount(), 1);
+ task.waitForStarted.acquire();
+ task.waitBeforeDone.release();
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
+ task2.waitForStarted.acquire();
+ task2.waitBeforeDone.release();
- // start() will therefore do a failing tryStart(), followed by enqueueTask()
- // which will actually wake up the waiting thread.
- threadpool->start(task);
- QTRY_COMPARE(threadpool->activeThreadCount(), 2);
- task->waitForStarted.acquire();
- task->waitBeforeDone.release();
- QTRY_COMPARE(task->count.loadRelaxed(), 2);
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
+}
+
+void tst_QThreadPool::reserveAndStart2()
+{
+ class WaitingTask : public QRunnable
+ {
+ public:
+ QSemaphore waitBeforeDone;
+
+ WaitingTask() { setAutoDelete(false); }
+
+ void run() override
+ {
+ waitBeforeDone.acquire();
+ }
+ };
+
+ // Set up
+ QThreadPool *threadpool = QThreadPool::globalInstance();
+ int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
+ threadpool->setMaxThreadCount(2);
+
+ // reserve
+ threadpool->reserveThread();
+
+ // start two task, to get a running thread and one queued
+ WaitingTask task1, task2, task3;
+ threadpool->start(&task1);
+ // one running thread, one reserved:
+ QCOMPARE(threadpool->activeThreadCount(), 2);
+ // task2 starts queued
+ threadpool->start(&task2);
+ QCOMPARE(threadpool->activeThreadCount(), 2);
+ // startOnReservedThread() will take the reserved thread however, bypassing the queue
+ threadpool->startOnReservedThread(&task3);
+ // two running threads, none reserved:
+ QCOMPARE(threadpool->activeThreadCount(), 2);
+ task3.waitBeforeDone.release();
+ // task3 can finish even if all other tasks are blocking
+ // then task2 will use the previously reserved thread
+ task2.waitBeforeDone.release();
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
+ task1.waitBeforeDone.release();
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
+}
+
+void tst_QThreadPool::releaseAndBlock()
+{
+ class WaitingTask : public QRunnable
+ {
+ public:
+ QSemaphore waitBeforeDone;
+
+ WaitingTask() { setAutoDelete(false); }
+
+ void run() override
+ {
+ waitBeforeDone.acquire();
+ }
+ };
+
+ // Set up
+ QThreadPool *threadpool = QThreadPool::globalInstance();
+ const int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
+
+ threadpool->setMaxThreadCount(1);
+ QCOMPARE(threadpool->activeThreadCount(), 0);
+
+ // start a task, to get a running thread, works since one thread is always allowed
+ WaitingTask task1, task2;
+ threadpool->start(&task1);
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+ // tryStart() will fail since activeThreadCount() >= maxThreadCount() and one thread is already running
+ QVERIFY(!threadpool->tryStart(&task2));
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+
+ // Use release without reserve to account for the blocking thread.
threadpool->releaseThread();
QTRY_COMPARE(threadpool->activeThreadCount(), 0);
- delete task;
+ // Now we can start task2
+ QVERIFY(threadpool->tryStart(&task2));
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+ task2.waitBeforeDone.release();
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
- threadpool->setMaxThreadCount(savedLimit);
+ threadpool->reserveThread();
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+ task1.waitBeforeDone.release();
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
}
-QAtomicInt count;
+static QAtomicInt count;
class CountingRunnable : public QRunnable
{
public:
@@ -734,7 +874,7 @@ void tst_QThreadPool::start()
const int runs = 1000;
count.storeRelaxed(0);
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
for (int i = 0; i< runs; ++i) {
threadPool.start(new CountingRunnable());
}
@@ -761,19 +901,19 @@ void tst_QThreadPool::tryStart()
count.storeRelaxed(0);
WaitingTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
for (int i = 0; i < threadPool.maxThreadCount(); ++i) {
threadPool.start(&task);
}
QVERIFY(!threadPool.tryStart(&task));
task.semaphore.release(threadPool.maxThreadCount());
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount());
}
-QMutex mutex;
-QAtomicInt activeThreads;
-QAtomicInt peakActiveThreads;
+static QMutex mutex;
+static QAtomicInt activeThreads;
+static QAtomicInt peakActiveThreads;
void tst_QThreadPool::tryStartPeakThreadCount()
{
class CounterTask : public QRunnable
@@ -798,7 +938,7 @@ void tst_QThreadPool::tryStartPeakThreadCount()
};
CounterTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
for (int i = 0; i < 4*QThread::idealThreadCount(); ++i) {
if (threadPool.tryStart(&task) == false)
@@ -827,7 +967,7 @@ void tst_QThreadPool::tryStartCount()
};
SleeperTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
const int runs = 5;
for (int i = 0; i < runs; ++i) {
@@ -867,7 +1007,7 @@ void tst_QThreadPool::priorityStart()
Runner(QAtomicPointer<QRunnable> &ptr) : ptr(ptr) {}
void run() override
{
- ptr.testAndSetRelaxed(0, this);
+ ptr.testAndSetRelaxed(nullptr, this);
}
};
@@ -875,7 +1015,7 @@ void tst_QThreadPool::priorityStart()
QSemaphore sem;
QAtomicPointer<QRunnable> firstStarted;
QRunnable *expected;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(1); // start only one thread at a time
// queue the holder first
@@ -887,7 +1027,7 @@ void tst_QThreadPool::priorityStart()
threadPool.start(expected = new Runner(firstStarted), 1); // priority 1
sem.release();
- QVERIFY(threadPool.waitForDone());
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(firstStarted.loadRelaxed(), expected);
}
@@ -895,8 +1035,9 @@ void tst_QThreadPool::waitForDone()
{
QElapsedTimer total, pass;
total.start();
+ pass.start();
- QThreadPool threadPool;
+ TestThreadPool threadPool;
while (total.elapsed() < 10000) {
int runs;
count.storeRelaxed(runs = 0);
@@ -905,7 +1046,7 @@ void tst_QThreadPool::waitForDone()
threadPool.start(new CountingRunnable());
++runs;
}
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), runs);
count.storeRelaxed(runs = 0);
@@ -915,7 +1056,7 @@ void tst_QThreadPool::waitForDone()
threadPool.start(new CountingRunnable());
runs += 2;
}
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), runs);
}
}
@@ -937,7 +1078,7 @@ void tst_QThreadPool::waitForDoneTimeout()
}
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
mutex.lock();
threadPool.start(new BlockedTask(mutex));
@@ -961,7 +1102,7 @@ void tst_QThreadPool::clear()
}
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(10);
int runs = 2 * threadPool.maxThreadCount();
count.storeRelaxed(0);
@@ -970,10 +1111,34 @@ void tst_QThreadPool::clear()
}
threadPool.clear();
sem.release(threadPool.maxThreadCount());
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount());
}
+void tst_QThreadPool::clearWithAutoDelete()
+{
+ class MyRunnable : public QRunnable
+ {
+ public:
+ MyRunnable() {}
+ void run() override { QThread::sleep(30us); }
+ };
+
+ TestThreadPool threadPool;
+ threadPool.setMaxThreadCount(4);
+ const int loopCount = 20;
+ const int batchSize = 500;
+ // Should not crash see QTBUG-87092
+ for (int i = 0; i < loopCount; i++) {
+ threadPool.clear();
+ for (int j = 0; j < batchSize; j++) {
+ auto *runnable = new MyRunnable();
+ runnable->setAutoDelete(true);
+ threadPool.start(runnable);
+ }
+ }
+}
+
void tst_QThreadPool::tryTake()
{
QSemaphore sem(0);
@@ -991,7 +1156,7 @@ void tst_QThreadPool::tryTake()
explicit BlockingRunnable(QSemaphore &s, QSemaphore &started, QAtomicInt &c, QAtomicInt &r)
: sem(s), startedThreads(started), dtorCounter(c), runCounter(r) {}
- ~BlockingRunnable()
+ ~BlockingRunnable() override
{
dtorCounter.fetchAndAddRelaxed(1);
}
@@ -1011,7 +1176,7 @@ void tst_QThreadPool::tryTake()
Runs = MaxThreadCount * OverProvisioning
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(MaxThreadCount);
BlockingRunnable *runnables[Runs];
@@ -1043,7 +1208,7 @@ void tst_QThreadPool::tryTake()
runnables[0]->dummy = 0; // valgrind will catch this if tryTake() is crazy enough to delete currently running jobs
QCOMPARE(dtorCounter.loadRelaxed(), int(Runs - MaxThreadCount));
sem.release(MaxThreadCount);
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(runCounter.loadRelaxed(), int(MaxThreadCount));
QCOMPARE(count.loadRelaxed(), int(MaxThreadCount));
QCOMPARE(dtorCounter.loadRelaxed(), int(Runs - 1));
@@ -1054,12 +1219,13 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish()
{
QElapsedTimer total, pass;
total.start();
+ pass.start();
while (total.elapsed() < 10000) {
int runs;
count.storeRelaxed(runs = 0);
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
@@ -1070,7 +1236,7 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish()
count.storeRelaxed(runs = 0);
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
@@ -1112,10 +1278,10 @@ void tst_QThreadPool::stackSize()
}
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setStackSize(targetStackSize);
threadPool.start(new StackSizeChecker(&threadStackSize));
- QVERIFY(threadPool.waitForDone(30000)); // 30s timeout
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(threadStackSize, targetStackSize);
}
@@ -1176,24 +1342,24 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
QSemaphore mainBarrier;
QSemaphore taskBarrier;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
- Task *task1 = new Task(&mainBarrier, &taskBarrier);
- Task *task2 = new Task(&mainBarrier, &taskBarrier);
- Task *task3 = new Task(&mainBarrier, &taskBarrier);
+ Task task1(&mainBarrier, &taskBarrier);
+ Task task2(&mainBarrier, &taskBarrier);
+ Task task3(&mainBarrier, &taskBarrier);
- threadPool.start(task1);
- threadPool.start(task2);
- threadPool.start(task3);
+ threadPool.start(&task1);
+ threadPool.start(&task2);
+ threadPool.start(&task3);
mainBarrier.acquire(1);
QCOMPARE(threadPool.activeThreadCount(), 1);
- QVERIFY(!threadPool.tryTake(task1));
- QVERIFY(threadPool.tryTake(task2));
- QVERIFY(threadPool.tryTake(task3));
+ QVERIFY(!threadPool.tryTake(&task1));
+ QVERIFY(threadPool.tryTake(&task2));
+ QVERIFY(threadPool.tryTake(&task3));
// A bad queue implementation can segfault here because two consecutive items in the queue
// have been taken
@@ -1207,13 +1373,9 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
taskBarrier.release(1);
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(threadPool.activeThreadCount(), 0);
-
- delete task1;
- delete task2;
- delete task3;
}
void tst_QThreadPool::waitForDoneAfterTake()
@@ -1244,7 +1406,7 @@ void tst_QThreadPool::waitForDoneAfterTake()
// Blocks the tasks from completing their run function
QSemaphore threadBarrier;
- QThreadPool manager;
+ TestThreadPool manager;
manager.setMaxThreadCount(threadCount);
// Fill all the threads with runnables that wait for the threadBarrier
@@ -1259,10 +1421,9 @@ void tst_QThreadPool::waitForDoneAfterTake()
// This sets the queue elements to nullptr in QThreadPool and we want to test that
// the threads keep going through the queue after encountering a nullptr.
for (int i = 0; i < threadCount; i++) {
- QRunnable *runnable = createTask(emptyFunct);
- manager.start(runnable);
- QVERIFY(manager.tryTake(runnable));
- delete runnable;
+ QScopedPointer<QRunnable> runnable(createTask(emptyFunct));
+ manager.start(runnable.get());
+ QVERIFY(manager.tryTake(runnable.get()));
}
// Add another runnable that will not be removed
@@ -1276,12 +1437,55 @@ void tst_QThreadPool::waitForDoneAfterTake()
// Release runnables that are waiting and expect all runnables to complete
threadBarrier.release(threadCount);
+}
- // Using qFatal instead of QVERIFY to force exit if threads are still running after timeout.
- // Otherwise, QCoreApplication will still wait for the stale threads and never exit the test.
- if (!manager.waitForDone(5 * 60 * 1000))
- qFatal("waitForDone returned false. Aborting to stop background threads.");
+/*
+ Try trigger reuse of expired threads and check that all tasks execute.
+
+ This is a regression test for QTBUG-72872.
+*/
+void tst_QThreadPool::threadReuse()
+{
+ TestThreadPool manager;
+ manager.setExpiryTimeout(-1);
+ manager.setMaxThreadCount(1);
+ constexpr int repeatCount = 10000;
+ constexpr int timeoutMs = 1000;
+ QSemaphore sem;
+
+ for (int i = 0; i < repeatCount; i++) {
+ manager.start([&sem]() { sem.release(); });
+ manager.start([&sem]() { sem.release(); });
+ manager.releaseThread();
+ QVERIFY(sem.tryAcquire(2, timeoutMs));
+ manager.reserveThread();
+ }
+}
+
+void tst_QThreadPool::nullFunctions()
+{
+ const auto expectWarning = [] {
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg,
+ "Trying to create null QRunnable. This may stop working.");
+ };
+ // Note this is not necessarily testing intended behavior, only undocumented behavior.
+ // If this is changed it should be noted in Behavioral Changes.
+ FunctionPointer nullFunction = nullptr;
+ std::function<void()> nullStdFunction(nullptr);
+ {
+ TestThreadPool manager;
+ // should not crash:
+ expectWarning();
+ manager.start(nullFunction);
+ expectWarning();
+ manager.start(nullStdFunction);
+ // should fail (and not leak):
+ expectWarning();
+ QVERIFY(!manager.tryStart(nullStdFunction));
+ expectWarning();
+ QVERIFY(!manager.tryStart(nullFunction));
+ }
}
QTEST_MAIN(tst_QThreadPool);
diff --git a/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
index e57f0c71ac..14d8d7dd40 100644
--- a/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
@@ -1,11 +1,17 @@
-# special case skip regeneration
-# Generated from qthreadstorage.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthreadstorage Test:
#####################################################################
-add_qt_test(tst_qthreadstorage
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qthreadstorage LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qthreadstorage
SOURCES
tst_qthreadstorage.cpp
)
@@ -15,4 +21,7 @@ add_qt_test(tst_qthreadstorage
if(NOT ANDROID)
add_subdirectory(crashonexit)
+ if(QT_FEATURE_process)
+ add_dependencies(tst_qthreadstorage crashOnExit_helper)
+ endif()
endif()
diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/.prev_CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/crashonexit/.prev_CMakeLists.txt
deleted file mode 100644
index 8a8e24587d..0000000000
--- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/.prev_CMakeLists.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-# Generated from crashonexit.pro.
-
-#####################################################################
-## crashonexit Binary:
-#####################################################################
-
-qt_add_executable(crashonexit
- OUTPUT_DIRECTORY "${INSTALL_TESTSDIR}/tst_qthreadstorage/crashonexit"
- INSTALL_DIRECTORY "${INSTALL_TESTSDIR}/tst_qthreadstorage/crashonexit"
- SOURCES
- crashOnExit.cpp
-)
-
-## Scopes:
-#####################################################################
diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt
index 0951c7df53..4d62e61a36 100644
--- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt
@@ -1,15 +1,13 @@
-# Generated from crashonexit.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## crashonexit Binary:
#####################################################################
-qt_add_executable(crashOnExit_helper # special case
- OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." # special case
- INSTALL_DIRECTORY "${INSTALL_TESTSDIR}/tst_qthreadstorage/crashOnExit_helper" # special case
+qt_internal_add_executable(crashOnExit_helper
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.."
+ INSTALL_DIRECTORY "${INSTALL_TESTSDIR}/tst_qthreadstorage/crashOnExit_helper"
SOURCES
crashOnExit.cpp
)
-
-## Scopes:
-#####################################################################
diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp
index 5a42156b85..3b3a4d4813 100644
--- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp
+++ b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QCoreApplication>
#include <QtCore/QThreadStorage>
diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro
deleted file mode 100644
index 57bd78bcee..0000000000
--- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCES += crashOnExit.cpp
-debug_and_release {
- CONFIG(debug, debug|release) {
- TARGET = ../../debug/crashOnExit_helper
- } else {
- TARGET = ../../release/crashOnExit_helper
- }
-} else {
- TARGET = ../crashOnExit_helper
-}
-QT = core
-CONFIG += cmdline
-
-# This app is testdata for tst_qthreadstorage
-target.path = $$[QT_INSTALL_TESTS]/tst_qthreadstorage/$$TARGET
-INSTALLS += target
diff --git a/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro b/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro
deleted file mode 100644
index c3eae5f7e1..0000000000
--- a/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro
+++ /dev/null
@@ -1,8 +0,0 @@
-TEMPLATE = subdirs
-
-!android {
- test.depends = crashonexit
- SUBDIRS += crashonexit
-}
-
-SUBDIRS += test
diff --git a/tests/auto/corelib/thread/qthreadstorage/test/test.pro b/tests/auto/corelib/thread/qthreadstorage/test/test.pro
deleted file mode 100644
index e0725d038e..0000000000
--- a/tests/auto/corelib/thread/qthreadstorage/test/test.pro
+++ /dev/null
@@ -1,16 +0,0 @@
-CONFIG += testcase
-debug_and_release {
- CONFIG(debug, debug|release) {
- TARGET = ../../debug/tst_qthreadstorage
- !android: TEST_HELPER_INSTALLS = ../../debug/crashonexit_helper
- } else {
- TARGET = ../../release/tst_qthreadstorage
- !android: TEST_HELPER_INSTALLS = ../../release/crashonexit_helper
- }
-} else {
- TARGET = ../tst_qthreadstorage
- !android: TEST_HELPER_INSTALLS = ../crashonexit_helper
-}
-CONFIG += console
-QT = core testlib
-SOURCES = ../tst_qthreadstorage.cpp
diff --git a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
index ab1dd677e9..ca382cf60c 100644
--- a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
+++ b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
@@ -1,32 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#if QT_CONFIG(process)
+#include <QProcess>
+#endif
+#include <QTestEventLoop>
#include <qcoreapplication.h>
#include <qmutex.h>
@@ -76,7 +55,7 @@ void tst_QThreadStorage::hasLocalData()
QVERIFY(!pointers.hasLocalData());
pointers.setLocalData(new Pointer);
QVERIFY(pointers.hasLocalData());
- pointers.setLocalData(0);
+ pointers.setLocalData(nullptr);
QVERIFY(!pointers.hasLocalData());
}
@@ -88,8 +67,8 @@ void tst_QThreadStorage::localData()
pointers.setLocalData(p);
QVERIFY(pointers.hasLocalData());
QCOMPARE(pointers.localData(), p);
- pointers.setLocalData(0);
- QCOMPARE(pointers.localData(), (Pointer *)0);
+ pointers.setLocalData(nullptr);
+ QCOMPARE(pointers.localData(), nullptr);
QVERIFY(!pointers.hasLocalData());
}
@@ -102,8 +81,8 @@ void tst_QThreadStorage::localData_const()
pointers.setLocalData(p);
QVERIFY(pointers.hasLocalData());
QCOMPARE(const_pointers.localData(), p);
- pointers.setLocalData(0);
- QCOMPARE(const_pointers.localData(), (Pointer *)0);
+ pointers.setLocalData(nullptr);
+ QCOMPARE(const_pointers.localData(), nullptr);
QVERIFY(!pointers.hasLocalData());
}
@@ -113,7 +92,7 @@ void tst_QThreadStorage::setLocalData()
QVERIFY(!pointers.hasLocalData());
pointers.setLocalData(new Pointer);
QVERIFY(pointers.hasLocalData());
- pointers.setLocalData(0);
+ pointers.setLocalData(nullptr);
QVERIFY(!pointers.hasLocalData());
}
@@ -157,7 +136,7 @@ void tst_QThreadStorage::autoDelete()
QCOMPARE(Pointer::count, c);
}
-bool threadStorageOk;
+static bool threadStorageOk;
void testAdoptedThreadStorageWin(void *p)
{
QThreadStorage<Pointer *> *pointers = reinterpret_cast<QThreadStorage<Pointer *> *>(p);
@@ -183,7 +162,7 @@ void testAdoptedThreadStorageWin(void *p)
void *testAdoptedThreadStorageUnix(void *pointers)
{
testAdoptedThreadStorageWin(pointers);
- return 0;
+ return nullptr;
}
void tst_QThreadStorage::adoptedThreads()
{
@@ -194,9 +173,9 @@ void tst_QThreadStorage::adoptedThreads()
{
#ifdef Q_OS_UNIX
pthread_t thread;
- const int state = pthread_create(&thread, 0, testAdoptedThreadStorageUnix, &pointers);
+ const int state = pthread_create(&thread, nullptr, testAdoptedThreadStorageUnix, &pointers);
QCOMPARE(state, 0);
- pthread_join(thread, 0);
+ pthread_join(thread, nullptr);
#elif defined Q_OS_WIN
HANDLE thread;
thread = (HANDLE)_beginthread(testAdoptedThreadStorageWin, 0, &pointers);
@@ -212,7 +191,7 @@ void tst_QThreadStorage::adoptedThreads()
QTRY_COMPARE(Pointer::count, c);
}
-QBasicAtomicInt cleanupOrder = Q_BASIC_ATOMIC_INITIALIZER(0);
+static QBasicAtomicInt cleanupOrder = Q_BASIC_ATOMIC_INITIALIZER(0);
class First
{
@@ -293,11 +272,14 @@ static inline bool runCrashOnExit(const QString &binary, QString *errorMessage)
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),
+ QVERIFY2(runCrashOnExit("./crashOnExit_helper", &errorMessage),
qPrintable(errorMessage));
#endif
}
diff --git a/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt b/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
index 91db59c391..0a2830622e 100644
--- a/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qwaitcondition.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qwaitcondition Test:
#####################################################################
-qt_add_test(tst_qwaitcondition
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qwaitcondition LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qwaitcondition
SOURCES
tst_qwaitcondition.cpp
)
diff --git a/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro b/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro
deleted file mode 100644
index 2098d9dd2f..0000000000
--- a/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qwaitcondition
-QT = core testlib
-SOURCES = tst_qwaitcondition.cpp
diff --git a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
index 3fcac48788..4e3413afe8 100644
--- a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
+++ b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
@@ -1,32 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QReadWriteLock>
#include <qatomic.h>
#include <qcoreapplication.h>
@@ -95,7 +71,7 @@ public:
QWaitCondition *cond;
inline wait_QMutex_Thread_2()
- : mutex(0), cond(0)
+ : mutex(nullptr), cond(nullptr)
{ }
void run() override
@@ -134,7 +110,7 @@ public:
QWaitCondition *cond;
inline wait_QReadWriteLock_Thread_2()
- : readWriteLock(0), cond(0)
+ : readWriteLock(nullptr), cond(nullptr)
{ }
void run() override
@@ -392,11 +368,11 @@ public:
QWaitCondition *cond;
inline wake_Thread()
- : mutex(0), cond(0)
+ : mutex(nullptr), cond(nullptr)
{ }
static inline void sleep(ulong s)
- { QThread::sleep(s); }
+ { QThread::sleep(std::chrono::seconds{s}); }
void run() override
{
@@ -424,11 +400,11 @@ public:
QWaitCondition *cond;
inline wake_Thread_2()
- : readWriteLock(0), cond(0)
+ : readWriteLock(nullptr), cond(nullptr)
{ }
static inline void sleep(ulong s)
- { QThread::sleep(s); }
+ { QThread::sleep(std::chrono::seconds{s}); }
void run() override
{
diff --git a/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt b/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
index 5db1124951..5345522ea5 100644
--- a/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
@@ -1,10 +1,17 @@
-# Generated from qwritelocker.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qwritelocker Test:
#####################################################################
-qt_add_test(tst_qwritelocker
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qwritelocker LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qwritelocker
SOURCES
tst_qwritelocker.cpp
)
diff --git a/tests/auto/corelib/thread/qwritelocker/qwritelocker.pro b/tests/auto/corelib/thread/qwritelocker/qwritelocker.pro
deleted file mode 100644
index a6c4f70b8d..0000000000
--- a/tests/auto/corelib/thread/qwritelocker/qwritelocker.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qwritelocker
-QT = core testlib
-SOURCES = tst_qwritelocker.cpp
diff --git a/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
index 3dc3c37961..b4e6b45dbd 100644
--- a/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
+++ b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QCoreApplication>
#include <QWriteLocker>
@@ -109,7 +84,7 @@ void tst_QWriteLocker::scopeTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
@@ -156,7 +131,7 @@ void tst_QWriteLocker::unlockAndRelockTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
void tst_QWriteLocker::lockerStateTest()
@@ -196,7 +171,7 @@ void tst_QWriteLocker::lockerStateTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
QTEST_MAIN(tst_QWriteLocker)
diff --git a/tests/auto/corelib/thread/thread.pro b/tests/auto/corelib/thread/thread.pro
deleted file mode 100644
index 510614ef66..0000000000
--- a/tests/auto/corelib/thread/thread.pro
+++ /dev/null
@@ -1,28 +0,0 @@
-TEMPLATE=subdirs
-
-qtConfig(thread) {
- SUBDIRS=\
- qatomicint \
- qatomicinteger \
- qatomicpointer \
- qresultstore \
- qfuture \
- qfuturesynchronizer \
- qmutex \
- qmutexlocker \
- qreadlocker \
- qreadwritelock \
- qsemaphore \
- qthread \
- qthreadonce \
- qthreadpool \
- qthreadstorage \
- qwaitcondition \
- qwritelocker \
- qpromise
-}
-
-qtHaveModule(concurrent) {
- SUBDIRS += \
- qfuturewatcher
-}