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/CMakeLists.txt37
-rw-r--r--tests/auto/corelib/thread/qatomicint/BLACKLIST3
-rw-r--r--tests/auto/corelib/thread/qatomicint/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp50
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt3
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp189
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp31
-rw-r--r--tests/auto/corelib/thread/qfuture/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp2065
-rw-r--r--tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp64
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp275
-rw-r--r--tests/auto/corelib/thread/qmutex/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qmutex/tst_qmutex.cpp257
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp109
-rw-r--r--tests/auto/corelib/thread/qpromise/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp55
-rw-r--r--tests/auto/corelib/thread/qpromise/tst_qpromise.cpp265
-rw-r--r--tests/auto/corelib/thread/qreadlocker/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp29
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt12
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp266
-rw-r--r--tests/auto/corelib/thread/qresultstore/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp145
-rw-r--r--tests/auto/corelib/thread/qsemaphore/BLACKLIST6
-rw-r--r--tests/auto/corelib/thread/qsemaphore/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp65
-rw-r--r--tests/auto/corelib/thread/qthread/BLACKLIST2
-rw-r--r--tests/auto/corelib/thread/qthread/CMakeLists.txt11
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp398
-rw-r--r--tests/auto/corelib/thread/qthreadonce/CMakeLists.txt11
-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/tst_qthreadonce.cpp38
-rw-r--r--tests/auto/corelib/thread/qthreadpool/BLACKLIST4
-rw-r--r--tests/auto/corelib/thread/qthreadpool/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp373
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/BLACKLIST3
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt13
-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/tst_qthreadstorage.cpp36
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp33
-rw-r--r--tests/auto/corelib/thread/qwritelocker/CMakeLists.txt9
-rw-r--r--tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp29
65 files changed, 3760 insertions, 1511 deletions
diff --git a/tests/auto/corelib/thread/CMakeLists.txt b/tests/auto/corelib/thread/CMakeLists.txt
index 931d8f0c6a..68110b652b 100644
--- a/tests/auto/corelib/thread/CMakeLists.txt
+++ b/tests/auto/corelib/thread/CMakeLists.txt
@@ -1,38 +1,47 @@
-# Generated from thread.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(QT_BUILD_WASM_BATCHED_TESTS) # not all tests currently work in WebAssembly
+ add_subdirectory(qatomicint)
+ add_subdirectory(qatomicinteger)
+ add_subdirectory(qatomicpointer)
+ 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)
add_subdirectory(qresultstore)
- add_subdirectory(qfuture)
+ if(QT_FEATURE_concurrent AND NOT INTEGRITY)
+ add_subdirectory(qfuture)
+ endif()
add_subdirectory(qfuturesynchronizer)
add_subdirectory(qmutex)
add_subdirectory(qmutexlocker)
add_subdirectory(qreadlocker)
add_subdirectory(qreadwritelock)
add_subdirectory(qsemaphore)
- # special case begin
# QTBUG-85364
if(NOT CMAKE_CROSSCOMPILING)
add_subdirectory(qthread)
endif()
- # special case end
add_subdirectory(qthreadonce)
add_subdirectory(qthreadpool)
- # special case begin
- # QTBUG-87431
- if(NOT ANDROID)
- add_subdirectory(qthreadstorage)
- endif()
- # special case end
+ add_subdirectory(qthreadstorage)
add_subdirectory(qwaitcondition)
add_subdirectory(qwritelocker)
- add_subdirectory(qpromise)
+ if(NOT INTEGRITY)
+ add_subdirectory(qpromise)
+ endif()
endif()
-# special case begin
+
# QTBUG-87431
-if(TARGET Qt::Concurrent AND NOT ANDROID)
+if(TARGET Qt::Concurrent AND NOT INTEGRITY)
add_subdirectory(qfuturewatcher)
endif()
-# special case end
diff --git a/tests/auto/corelib/thread/qatomicint/BLACKLIST b/tests/auto/corelib/thread/qatomicint/BLACKLIST
deleted file mode 100644
index 406fb7cc1c..0000000000
--- a/tests/auto/corelib/thread/qatomicint/BLACKLIST
+++ /dev/null
@@ -1,3 +0,0 @@
-# QTBUG-87422
-[alignment]
-android
diff --git a/tests/auto/corelib/thread/qatomicint/CMakeLists.txt b/tests/auto/corelib/thread/qatomicint/CMakeLists.txt
index 867388cfbf..239f3cce87 100644
--- a/tests/auto/corelib/thread/qatomicint/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicint/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qatomicint.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicint Test:
#####################################################################
+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/tst_qatomicint.cpp b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
index 1f0d9e7779..63cb494c11 100644
--- a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
+++ b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// 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>
@@ -186,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__))
@@ -230,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
}
@@ -517,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
@@ -552,7 +515,6 @@ void tst_QAtomicInt::testAndSet()
if (!result)
QCOMPARE(currentval, value);
}
-#endif
}
void tst_QAtomicInt::isFetchAndStoreNative()
@@ -714,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;
@@ -730,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 57601dc0e4..882a9298f6 100644
--- a/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from char.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt
index 6ac58c9136..8e53b59689 100644
--- a/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from char16_t.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char16_t Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt
index 911e9f5067..5881d475f4 100644
--- a/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from char32_t.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_char32_t Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
index be59e2dc98..0915e77a8d 100644
--- a/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from int.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_int Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
index 3f632ff212..adf6638bfa 100644
--- a/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from long.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_long Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt
index 3de9227bb1..2ec977d7cb 100644
--- a/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qlonglong.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qlonglong Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt
index cc6b5126d9..a2450931d5 100644
--- a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qptrdiff.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qptrdiff Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt
index 44cf39f06d..98302b5d07 100644
--- a/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from quintptr.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_quintptr Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt
index 18d3e384b9..13acfc3e2b 100644
--- a/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qulonglong.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_qulonglong Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt
index 760967532a..127f752cc2 100644
--- a/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from schar.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_schar Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt
index 3f762e34ab..df9d2af4c3 100644
--- a/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from short.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_short Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
index dce27ee9c1..d1a8a8f729 100644
--- a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
+++ b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
@@ -1,54 +1,5 @@
-/****************************************************************************
-**
-** 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 <QTest>
#include <QAtomicInt>
@@ -57,6 +8,12 @@
#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
@@ -342,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));
}
@@ -484,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()
@@ -565,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()
@@ -788,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 912365a6e9..95d88d31a6 100644
--- a/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from uchar.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_uchar Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
index 725fa91704..2ab977ef6a 100644
--- a/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from uint.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_uint Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt
index d5d35f587d..7707bd53b5 100644
--- a/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from ulong.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_ulong Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt
index fd7c002345..667e9eade6 100644
--- a/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from ushort.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_ushort Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt
index aecb81005c..0e2d084b58 100644
--- a/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from wchar_t.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicinteger_wchar_t Test:
#####################################################################
+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
diff --git a/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt b/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
index 2c3f32d7e3..cd8df9db66 100644
--- a/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qatomicpointer.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qatomicpointer Test:
#####################################################################
+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/tst_qatomicpointer.cpp b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
index 6ca0d3c227..347831819e 100644
--- a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
+++ b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// 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>
diff --git a/tests/auto/corelib/thread/qfuture/CMakeLists.txt b/tests/auto/corelib/thread/qfuture/CMakeLists.txt
index dcf7846fd2..aa989f3df1 100644
--- a/tests/auto/corelib/thread/qfuture/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qfuture/CMakeLists.txt
@@ -1,14 +1,26 @@
-# Generated from qfuture.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfuture Test:
#####################################################################
+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_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/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
index 71cfbd054b..3fc796514d 100644
--- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
+++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
@@ -1,38 +1,20 @@
-/****************************************************************************
-**
-** 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
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
+
+#define QFUTURE_TEST
+
#include <QCoreApplication>
#include <QDebug>
#include <QSemaphore>
#include <QTestEventLoop>
#include <QTimer>
#include <QSignalSpy>
-
-#define QFUTURE_TEST
+#include <QVarLengthArray>
+#include <QSet>
+#include <QList>
+#include <private/qobject_p.h>
#include <QTest>
#include <qfuture.h>
@@ -44,18 +26,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
{
@@ -69,12 +54,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
@@ -95,6 +143,18 @@ 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
@@ -107,6 +167,7 @@ private slots:
void futureInterface();
void refcounting();
void cancel();
+ void cancelAndFinish();
void statePropagation();
void multipleResults();
void indexedResults();
@@ -142,7 +203,10 @@ 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();
@@ -162,6 +226,29 @@ private slots:
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;
@@ -176,6 +263,16 @@ 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::resultStore()
{
int int0 = 0;
@@ -183,7 +280,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());
@@ -191,7 +290,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();
@@ -213,7 +314,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();
@@ -236,7 +339,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);
@@ -267,7 +372,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);
@@ -297,7 +404,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);
@@ -309,7 +418,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);
@@ -320,7 +431,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);
@@ -328,7 +441,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);
@@ -362,7 +477,9 @@ void tst_QFuture::resultStore()
{
// test canceled results
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -399,7 +516,9 @@ void tst_QFuture::resultStore()
{
// test addResult return value
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -445,7 +564,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);
@@ -459,7 +580,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(2, &int0);
QCOMPARE(store.count(), 0);
@@ -471,7 +594,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -483,7 +608,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -491,7 +618,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);
@@ -503,7 +632,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -516,7 +647,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -526,7 +659,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -539,7 +674,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);
@@ -554,7 +691,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(1, &int0);
store.addResult(3, &int0);
@@ -582,7 +721,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);
@@ -730,6 +871,26 @@ void tst_QFuture::futureInterface()
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>
@@ -842,6 +1003,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;
@@ -908,13 +1102,13 @@ void tst_QFuture::multipleResults()
QList<int> fasit = QList<int>() << 1 << 2 << 3 << 4;
{
QList<int> results;
- for (int result : qAsConst(f))
+ for (int result : std::as_const(f))
results.append(result);
QCOMPARE(results, fasit);
}
{
QList<int> results;
- for (int result : qAsConst(copy))
+ for (int result : std::as_const(copy))
results.append(result);
QCOMPARE(results, fasit);
}
@@ -1787,7 +1981,7 @@ void tst_QFuture::nonGlobalThreadPool()
void run() override
{
const int ms = 100 + (QRandomGenerator::global()->bounded(100) - 100/2);
- QThread::msleep(ulong(ms));
+ QThread::sleep(std::chrono::milliseconds{ms});
reportResult(Answer);
reportFinished();
}
@@ -2072,6 +2266,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>
@@ -2825,15 +3039,220 @@ 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()
{
- QThread thread;
- thread.start();
+ QFETCH(bool, inOtherThread);
+ auto tstThread = QThread::currentThread();
+ QThread *thread = inOtherThread ? new QThread
+ : tstThread;
auto context = new QObject();
- context->moveToThread(&thread);
- auto tstThread = QThread::currentThread();
+ const auto cleanupGuard = qScopeGuard([&] {
+ context->deleteLater();
+ if (thread != tstThread) {
+ thread->quit();
+ thread->wait();
+ delete thread;
+ }
+ });
+
+ if (inOtherThread) {
+ thread->start();
+ context->moveToThread(thread);
+ }
// .then()
{
@@ -2846,12 +3265,12 @@ void tst_QFuture::continuationsWithContext()
})
.then(context,
[&](int val) {
- if (QThread::currentThread() != &thread)
+ if (QThread::currentThread() != thread)
return 0;
return val + 1;
})
.then([&](int val) {
- if (QThread::currentThread() != &thread)
+ if (QThread::currentThread() != thread)
return 0;
return val + 1;
});
@@ -2867,12 +3286,12 @@ void tst_QFuture::continuationsWithContext()
auto future = promise.future()
.onCanceled(context,
[&] {
- if (QThread::currentThread() != &thread)
+ if (QThread::currentThread() != thread)
return 0;
return 1;
})
.then([&](int val) {
- if (QThread::currentThread() != &thread)
+ if (QThread::currentThread() != thread)
return 0;
return val + 1;
});
@@ -2882,6 +3301,40 @@ void tst_QFuture::continuationsWithContext()
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()
{
@@ -2894,12 +3347,12 @@ void tst_QFuture::continuationsWithContext()
})
.onFailed(context,
[&] {
- if (QThread::currentThread() != &thread)
+ if (QThread::currentThread() != thread)
return 0;
return 1;
})
.then([&](int val) {
- if (QThread::currentThread() != &thread)
+ if (QThread::currentThread() != thread)
return 0;
return val + 1;
});
@@ -2908,11 +3361,72 @@ void tst_QFuture::continuationsWithContext()
QCOMPARE(future.result(), 2);
}
#endif // QT_NO_EXCEPTIONS
+}
- context->deleteLater();
+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;
- thread.quit();
- thread.wait();
+ 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)
@@ -2937,17 +3451,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);
}
@@ -3035,7 +3538,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));
};
@@ -3048,7 +3551,7 @@ 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");
@@ -3119,14 +3622,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);
}
@@ -3167,11 +3670,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);
}
@@ -3200,16 +3713,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(std::get<0>(result), 42);
- QCOMPARE(std::get<1>(result), 42.5);
- QCOMPARE(std::get<2>(result), "42");
+ 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(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
@@ -3221,20 +3869,64 @@ 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()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
QFutureInterface<void> fi;
auto future = fi.future();
@@ -3255,7 +3947,6 @@ void tst_QFuture::waitForFinished()
QVERIFY(waitingThread->wait());
QVERIFY(waitingThread->isFinished());
-#endif
}
void tst_QFuture::rejectResultOverwrite_data()
@@ -3300,9 +3991,9 @@ void tst_QFuture::rejectResultOverwrite()
});
// Run event loop, QCoreApplication::postEvent is in use
// in QFutureInterface:
- eventProcessor.enterLoopMSecs(2000);
+ eventProcessor.enterLoop(DefaultWaitTime);
QVERIFY(!eventProcessor.timeout());
- QCOMPARE(resultCounter.count(), 1);
+ QCOMPARE(resultCounter.size(), 1);
f.resume();
// overwrite with lvalue
@@ -3339,9 +4030,9 @@ void tst_QFuture::rejectResultOverwrite()
QTimer::singleShot(50, [&f]() {
f.suspend(); // should exit the loop
});
- eventProcessor.enterLoopMSecs(2000);
+ eventProcessor.enterLoop(DefaultWaitTime);
QVERIFY(!eventProcessor.timeout());
- QCOMPARE(resultCounter.count(), 1);
+ QCOMPARE(resultCounter.size(), 1);
f.resume();
QCOMPARE(f.results(), initResults);
}
@@ -3378,9 +4069,9 @@ void tst_QFuture::rejectPendingResultOverwrite()
});
// Run event loop, QCoreApplication::postEvent is in use
// in QFutureInterface:
- eventProcessor.enterLoopMSecs(2000);
+ eventProcessor.enterLoop(DefaultWaitTime);
QVERIFY(!eventProcessor.timeout());
- QCOMPARE(resultCounter.count(), 1);
+ QCOMPARE(resultCounter.size(), 1);
f.resume();
}
@@ -3422,9 +4113,9 @@ void tst_QFuture::rejectPendingResultOverwrite()
QTimer::singleShot(50, [&f]() {
f.suspend(); // should exit the loop
});
- eventProcessor.enterLoopMSecs(2000);
+ eventProcessor.enterLoop(DefaultWaitTime);
QVERIFY(!eventProcessor.timeout());
- QCOMPARE(resultCounter.count(), 1);
+ QCOMPARE(resultCounter.size(), 1);
f.resume();
}
@@ -3437,6 +4128,9 @@ void tst_QFuture::rejectPendingResultOverwrite()
void tst_QFuture::createReadyFutures()
{
+#if QT_DEPRECATED_SINCE(6, 10)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
// using const T &
{
const int val = 42;
@@ -3472,6 +4166,30 @@ void tst_QFuture::createReadyFutures()
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
@@ -3500,6 +4218,1139 @@ void tst_QFuture::createReadyFutures()
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)
diff --git a/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt b/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
index e41b8e70c6..c0f4561c51 100644
--- a/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qfuturesynchronizer.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfuturesynchronizer Test:
#####################################################################
+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/tst_qfuturesynchronizer.cpp b/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp
index ab42bdf341..62ad4f872a 100644
--- a/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp
+++ b/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.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 <QTest>
@@ -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.count(), synchronizer.futures().count());
+ QCOMPARE(futures.size(), synchronizer.futures().size());
}
void tst_QFutureSynchronizer::setFuture()
diff --git a/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt b/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt
index 93ac2dd870..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:
#####################################################################
+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/tst_qfuturewatcher.cpp b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
index 0d759eebd6..40aa89ded4 100644
--- a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
+++ b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.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 <QCoreApplication>
#include <QDebug>
#include <QElapsedTimer>
@@ -34,6 +9,7 @@
#include <private/qfutureinterface_p.h>
using namespace QtConcurrent;
+using namespace std::chrono_literals;
#include <QTest>
@@ -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()
@@ -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,7 +896,7 @@ void tst_QFutureWatcher::suspendEvents()
a.resume(); // should give us no results.
QTest::qWait(10);
- QCOMPARE(resultReadySpy.count(), 0);
+ QCOMPARE(resultReadySpy.size(), 0);
}
}
@@ -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 41b7b1da68..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:
#####################################################################
+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_internal_extend_target(tst_qmutex CONDITION WIN32
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
)
diff --git a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
index fb080a1a12..4753444ab9 100644
--- a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
+++ b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// 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>
@@ -35,7 +10,11 @@
#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
{
@@ -50,8 +29,6 @@ public:
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();
@@ -84,121 +61,15 @@ static 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(QtPrivate::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()
{
@@ -228,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();
@@ -254,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));
@@ -265,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();
@@ -276,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
@@ -325,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:
@@ -355,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();
@@ -381,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));
@@ -392,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();
@@ -443,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:
@@ -484,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();
@@ -510,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));
@@ -521,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();
@@ -572,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()
@@ -610,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));
@@ -635,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));
@@ -708,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:
@@ -737,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));
@@ -762,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));
@@ -831,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:
@@ -865,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));
@@ -891,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));
@@ -960,7 +817,6 @@ void tst_QMutex::try_lock_until_recursive()
testsTurn.acquire();
threadsTurn.release();
thread.wait();
-#endif
}
class mutex_Thread : public QThread
@@ -1086,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
{
@@ -1191,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();
}
@@ -1210,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();
}
@@ -1324,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 b73d0d073d..7b2d6dc1c2 100644
--- a/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qmutexlocker.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qmutexlocker Test:
#####################################################################
+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/tst_qmutexlocker.cpp b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
index d873291fd6..6ccab04c27 100644
--- a/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
+++ b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.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 <QTest>
@@ -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();
}
@@ -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());
}
};
@@ -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();
@@ -200,5 +187,71 @@ void tst_QMutexLocker::lockerStateTest()
thread = nullptr;
}
+void tst_QMutexLocker::moveSemantics()
+{
+ {
+ QMutexLocker<QMutex> locker(nullptr);
+ QVERIFY(!locker.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+
+ QMutexLocker locker2(std::move(locker));
+ QVERIFY(!locker.isLocked());
+ QVERIFY(!locker2.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+ QCOMPARE(locker2.mutex(), nullptr);
+ }
+
+ QMutex mutex;
+
+ {
+ QMutexLocker locker(&mutex);
+ QVERIFY(locker.isLocked());
+ QCOMPARE(locker.mutex(), &mutex);
+ QVERIFY(!mutex.tryLock());
+
+ QMutexLocker locker2(std::move(locker));
+ QVERIFY(!locker.isLocked());
+ QVERIFY(locker2.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+ QCOMPARE(locker2.mutex(), &mutex);
+ QVERIFY(!mutex.tryLock());
+ }
+
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
+
+ {
+ QMutex mutex;
+ QMutexLocker locker(&mutex);
+ QVERIFY(locker.isLocked());
+ QCOMPARE(locker.mutex(), &mutex);
+ QVERIFY(!mutex.tryLock());
+
+ locker.unlock();
+ QVERIFY(!locker.isLocked());
+ QCOMPARE(locker.mutex(), &mutex);
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
+
+ QMutexLocker locker2(std::move(locker));
+ QVERIFY(!locker.isLocked());
+ QVERIFY(!locker2.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+ QCOMPARE(locker2.mutex(), &mutex);
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
+
+ locker2.relock();
+ QVERIFY(!locker.isLocked());
+ QVERIFY(locker2.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+ QCOMPARE(locker2.mutex(), &mutex);
+ QVERIFY(!mutex.tryLock());
+ }
+
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
+}
+
QTEST_MAIN(tst_QMutexLocker)
#include "tst_qmutexlocker.moc"
diff --git a/tests/auto/corelib/thread/qpromise/CMakeLists.txt b/tests/auto/corelib/thread/qpromise/CMakeLists.txt
index b841c7812b..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:
#####################################################################
+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/snippet_qpromise.cpp b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
index 2e7b8832d3..2cef0dca95 100644
--- a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
+++ b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** 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
@@ -100,9 +53,9 @@ void snippet_QPromise::multithreadExample()
QFuture<int> future = sharedPromise->future();
// ...
-//! [multithread_init]
sharedPromise->start();
+//! [multithread_init]
//! [multithread_main]
// here, QPromise is shared between threads via a smart pointer
@@ -135,7 +88,9 @@ void snippet_QPromise::multithreadExample()
for (auto& t : threads)
t->wait();
+//! [multithread_cleanup]
sharedPromise->finish();
+//! [multithread_cleanup]
#endif
}
diff --git a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
index b956619e11..2c12e41c93 100644
--- a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
+++ b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** 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>
@@ -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;
+ 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; }
@@ -192,6 +173,15 @@ void tst_QPromise::addResult()
QCOMPARE(f.resultCount(), 3);
QCOMPARE(f.resultAt(2), result);
}
+ // 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;
@@ -209,6 +199,28 @@ void tst_QPromise::addResult()
}
}
+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);
+ }
+}
+
void tst_QPromise::addResultOutOfOrder()
{
// Compare results available in QFuture to expected results
@@ -280,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
@@ -293,6 +309,8 @@ void tst_QPromise::cancel()
testCancel(QPromise<void>());
testCancel(QPromise<int>());
+ testCancel(QPromise<CopyOnlyType>());
+ testCancel(QPromise<MoveOnlyType>());
}
void tst_QPromise::progress()
@@ -320,13 +338,13 @@ 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)
- QSKIP("This test requires QThread::create");
-#else
+#if QT_CONFIG(cxx11_future)
const auto testAddResult = [] (auto promise, const auto &result) {
promise.start();
auto f = promise.future();
@@ -347,9 +365,7 @@ void tst_QPromise::addInThread()
void tst_QPromise::addInThreadMoveOnlyObject()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
+#if QT_CONFIG(cxx11_future)
QPromise<MoveOnlyType> promise;
promise.start();
auto f = promise.future();
@@ -366,9 +382,7 @@ void tst_QPromise::addInThreadMoveOnlyObject()
void tst_QPromise::reportFromMultipleThreads()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
+#if QT_CONFIG(cxx11_future)
QPromise<int> promise;
auto f = promise.future();
promise.start();
@@ -392,9 +406,7 @@ void tst_QPromise::reportFromMultipleThreads()
void tst_QPromise::reportFromMultipleThreadsByMovedPromise()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
+#if QT_CONFIG(cxx11_future)
QPromise<int> initialPromise;
auto f = initialPromise.future();
{
@@ -426,9 +438,7 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise()
void tst_QPromise::doNotCancelWhenFinished()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
+#if QT_CONFIG(cxx11_future)
const auto testFinishedPromise = [] (auto promise) {
auto f = promise.future();
promise.start();
@@ -445,15 +455,15 @@ 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)
- QSKIP("This test requires QThread::create");
-#else
+#if QT_CONFIG(cxx11_future)
QPromise<int> initialPromise;
auto f = initialPromise.future();
@@ -487,15 +497,13 @@ void tst_QPromise::cancelWhenDestroyed()
void tst_QPromise::cancelWhenReassigned()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
+#if QT_CONFIG(cxx11_future)
QPromise<int> promise;
auto f = promise.future();
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
});
@@ -506,11 +514,123 @@ void tst_QPromise::cancelWhenReassigned()
#endif
}
-void tst_QPromise::finishWhenSwapped()
+template <typename T>
+static inline void testCancelWhenDestroyedWithoutStarting()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
+ 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.start();
@@ -520,7 +640,7 @@ void tst_QPromise::finishWhenSwapped()
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
@@ -548,22 +668,21 @@ void tst_QPromise::finishWhenSwapped()
#endif
}
-void tst_QPromise::cancelWhenMoved()
+template <typename T>
+void testCancelWhenMoved()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
- QPromise<int> promise1;
+#if QT_CONFIG(cxx11_future)
+ QPromise<T> promise1;
auto f1 = promise1.future();
promise1.start();
- QPromise<int> promise2;
+ QPromise<T> promise2;
auto f2 = promise2.future();
promise2.start();
// Move promises to local scope to test cancellation behavior
ThreadWrapper thr([p1 = std::move(promise1), p2 = std::move(promise2)] () mutable {
- QThread::msleep(100);
+ QThread::sleep(100ms);
p1 = std::move(p2);
p1.finish(); // this finish is for future #2
});
@@ -581,6 +700,14 @@ void tst_QPromise::cancelWhenMoved()
#endif
}
+void tst_QPromise::cancelWhenMoved()
+{
+ testCancelWhenMoved<void>();
+ testCancelWhenMoved<int>();
+ testCancelWhenMoved<CopyOnlyType>();
+ testCancelWhenMoved<MoveOnlyType>();
+}
+
void tst_QPromise::waitUntilResumed()
{
#if !QT_CONFIG(cxx11_future)
@@ -599,7 +726,7 @@ void tst_QPromise::waitUntilResumed()
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();
@@ -612,9 +739,7 @@ void tst_QPromise::waitUntilResumed()
void tst_QPromise::waitUntilCanceled()
{
-#if !QT_CONFIG(cxx11_future)
- QSKIP("This test requires QThread::create");
-#else
+#if QT_CONFIG(cxx11_future)
QPromise<int> promise;
promise.start();
auto f = promise.future();
@@ -628,7 +753,7 @@ void tst_QPromise::waitUntilCanceled()
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();
diff --git a/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt b/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
index da6091d588..7e80a4df81 100644
--- a/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qreadlocker.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qreadlocker Test:
#####################################################################
+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/tst_qreadlocker.cpp b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
index c270aa2683..e21f80c05d 100644
--- a/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
+++ b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.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 <QTest>
diff --git a/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt b/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt
index 4b81229024..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:
#####################################################################
+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/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
index ca282e2723..86dfa5faff 100644
--- a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
+++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.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) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QSemaphore>
@@ -34,14 +9,12 @@
#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.
@@ -53,6 +26,8 @@
#include <stdio.h>
+using namespace std::chrono_literals;
+
class tst_QReadWriteLock : public QObject
{
Q_OBJECT
@@ -495,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)
@@ -512,9 +487,9 @@ public:
while (t.elapsed()<runTime) {
testRwlock.lockForRead();
if(print) printf("reading\n");
- if (holdTime) msleep(ulong(holdTime));
+ if (holdTime > 0ms) sleep(holdTime);
testRwlock.unlock();
- if (waitTime) msleep(ulong(waitTime));
+ if (waitTime > 0ms) sleep(waitTime);
}
}
};
@@ -531,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)
@@ -548,9 +523,9 @@ public:
while (t.elapsed() < runTime) {
testRwlock.lockForWrite();
if (print) printf(".");
- if (holdTime) msleep(ulong(holdTime));
+ if (holdTime > 0ms) sleep(holdTime);
testRwlock.unlock();
- if (waitTime) msleep(ulong(waitTime));
+ if (waitTime > 0ms) sleep(waitTime);
}
}
};
@@ -570,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)
@@ -587,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(ulong(waitTime));
+ sleep(waitTime);
}
}
};
@@ -612,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)
@@ -627,7 +598,7 @@ public:
if(count)
qFatal("Non-zero count at Read! (%d)",count );
testRwlock.unlock();
- msleep(ulong(waitTime));
+ sleep(waitTime);
}
}
};
@@ -644,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);
@@ -661,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);
@@ -680,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();
@@ -696,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;
}
/*
@@ -724,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;
}
/*
@@ -746,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;
}
/*
@@ -788,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()
diff --git a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt
index ca30061d15..0f9d8d9e52 100644
--- a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt
@@ -1,12 +1,19 @@
-# Generated from qresultstore.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qresultstore Test:
#####################################################################
+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
)
diff --git a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
index 10ac19137e..265b2cd1f6 100644
--- a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
+++ b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.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 <QTest>
@@ -32,9 +7,14 @@
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
@@ -87,7 +67,9 @@ 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();
@@ -110,7 +92,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);
@@ -156,7 +140,9 @@ 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();
@@ -177,11 +163,21 @@ void tst_QtConcurrentResultStore::addResults()
++it;
QCOMPARE(it, store.end());
+
+ QList<int> empty;
+ const auto countBefore = store.count();
+ QCOMPARE(store.addResults(countBefore, &empty), -1);
+ QCOMPARE(store.count(), countBefore);
+
+ QCOMPARE(store.addResults(countBefore, &vec1), countBefore);
+ QCOMPARE(store.count(), countBefore + vec1.size());
}
void tst_QtConcurrentResultStore::resultIndex()
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(-1, &int1);
@@ -214,7 +210,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);
@@ -225,7 +223,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);
@@ -239,7 +239,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);
@@ -251,7 +253,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);
@@ -266,7 +270,9 @@ void tst_QtConcurrentResultStore::contains()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(1, &int0);
store.addResult(3, &int0);
@@ -294,7 +300,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);
@@ -308,7 +316,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());
@@ -340,12 +350,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);
@@ -385,7 +405,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);
@@ -399,7 +421,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(2, &int0);
QCOMPARE(store.count(), 0);
@@ -411,7 +435,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -423,7 +449,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -431,7 +459,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);
@@ -443,7 +473,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -456,7 +488,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -466,7 +500,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -513,17 +549,12 @@ struct CountedObject
const size_t previousLiveCount;
};
- int id = 0;
+ size_t id = 0;
static size_t liveCount;
};
size_t CountedObject::liveCount = 0;
-struct ResultStoreCountedObject : ResultStoreBase
-{
- ~ResultStoreCountedObject() { clear<CountedObject>(); }
-};
-
void tst_QtConcurrentResultStore::pendingResultsDoNotLeak_data()
{
QTest::addColumn<bool>("filterMode");
@@ -537,7 +568,9 @@ void tst_QtConcurrentResultStore::pendingResultsDoNotLeak()
QFETCH(bool, filterMode);
CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker)
- ResultStoreCountedObject store;
+ QtPrivate::ResultStoreBase store;
+ auto cleanGaurd = qScopeGuard([&] { store.clear<CountedObject>(); });
+
store.setFilterMode(filterMode);
// lvalue
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 1860347448..9f8a87558a 100644
--- a/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qsemaphore.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qsemaphore Test:
#####################################################################
+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/tst_qsemaphore.cpp b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
index 9f1512bc44..3bb1e1960c 100644
--- a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
+++ b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.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 <QTest>
@@ -32,6 +7,10 @@
#include <qthread.h>
#include <qsemaphore.h>
+#include <chrono>
+
+using namespace std::chrono_literals;
+
class tst_QSemaphore : public QObject
{
Q_OBJECT
@@ -47,6 +26,7 @@ private slots:
void tryAcquireWithTimeoutForever();
void producerConsumer();
void raii();
+ void stdCompat();
};
static QSemaphore *semaphore = nullptr;
@@ -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/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 96c649f106..abcea1ef9c 100644
--- a/tests/auto/corelib/thread/qthread/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qthread/CMakeLists.txt
@@ -1,13 +1,20 @@
-# Generated from qthread.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthread Test:
#####################################################################
+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
tst_qthread.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::TestPrivate
)
diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
index 7e06e7f51f..b163235d81 100644
--- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp
+++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
@@ -1,36 +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$
-**
-****************************************************************************/
+// 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>
@@ -40,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
@@ -57,6 +38,8 @@
#include <QtTest/private/qemulationdetector_p.h>
+using namespace std::chrono_literals;
+
class tst_QThread : public QObject
{
Q_OBJECT
@@ -70,6 +53,7 @@ private slots:
void setStackSize();
void exit();
void start();
+ void startSlotUsedInStringBasedLookups();
void terminate();
void quit();
void started();
@@ -89,6 +73,7 @@ private slots:
void adoptedThreadExecFinished();
void adoptMultipleThreads();
void adoptMultipleThreadsOverlap();
+ void adoptedThreadBindingStatus();
void exitAndStart();
void exitAndExec();
@@ -110,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 };
@@ -145,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();
}
};
@@ -256,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();
@@ -282,6 +278,11 @@ void tst_QThread::currentThreadId()
QVERIFY(thread.wait(five_minutes));
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()
@@ -473,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);
@@ -544,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);
@@ -779,8 +838,9 @@ void tst_QThread::adoptedThreadAffinity()
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()
@@ -948,6 +1008,20 @@ 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()
{
@@ -1085,8 +1159,8 @@ void tst_QThread::wait2()
qPrintable(msgElapsed(elapsed)));
}
-
-class SlowSlotObject : public QObject {
+class SlowSlotObject : public QObject
+{
Q_OBJECT
public:
QMutex mutex;
@@ -1102,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));
}
@@ -1212,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();
@@ -1227,11 +1299,11 @@ public:
}
void registerSocketNotifier(QSocketNotifier *) override {}
void unregisterSocketNotifier(QSocketNotifier *) override {}
- void registerTimer(int, qint64, Qt::TimerType, QObject *) override {}
- bool unregisterTimer(int) override { return false; }
+ void registerTimer(Qt::TimerId, Duration, Qt::TimerType, QObject *) override {}
+ bool unregisterTimer(Qt::TimerId) override { return false; }
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 {}
@@ -1342,9 +1414,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));
@@ -1580,11 +1649,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
@@ -1625,5 +1753,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 cd4aaa5d72..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:
#####################################################################
+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/tst_qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
index 7bb39da85c..37e1f744f3 100644
--- a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
+++ b/tests/auto/corelib/thread/qthreadonce/tst_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 <QTest>
@@ -36,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();
@@ -52,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
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 ebf9fc104f..fee9c541db 100644
--- a/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qthreadpool.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthreadpool Test:
#####################################################################
+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/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
index 5960ac20a3..2006016d47 100644
--- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
+++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
@@ -1,36 +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$
-**
-****************************************************************************/
+// 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>
@@ -39,6 +15,8 @@
#include <unistd.h>
#endif
+using namespace std::chrono_literals;
+
typedef void (*FunctionPointer)();
class FunctionPointerTask : public QRunnable
@@ -68,6 +46,7 @@ public:
private slots:
void runFunction();
void runFunction2();
+ void runFunction3();
void createThreadRunFunction();
void runMultiple();
void waitcomplete();
@@ -75,6 +54,7 @@ private slots:
void singleton();
void destruction();
void threadRecycling();
+ void threadPriority();
void expiryTimeout();
void expiryTimeoutRace();
#ifndef QT_NO_EXCEPTIONS
@@ -88,6 +68,8 @@ private slots:
void releaseThread_data();
void releaseThread();
void reserveAndStart();
+ void reserveAndStart2();
+ void releaseAndBlock();
void start();
void tryStart();
void tryStartPeakThreadCount();
@@ -104,6 +86,8 @@ private slots:
void stressTest();
void takeAllAndIncreaseMaxThreadCount();
void waitForDoneAfterTake();
+ void threadReuse();
+ void nullFunctions();
private:
QMutex m_functionTestMutex;
@@ -157,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);
}
@@ -171,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);
}
@@ -193,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);
@@ -202,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);
@@ -211,7 +227,7 @@ void tst_QThreadPool::runMultiple()
QCOMPARE(testFunctionCount, runs);
{
- QThreadPool manager;
+ TestThreadPool manager;
for (int i = 0; i < 500; ++i)
manager.start(emptyFunct);
}
@@ -222,6 +238,7 @@ 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);
}
@@ -240,7 +257,7 @@ public:
void tst_QThreadPool::runTask()
{
- QThreadPool manager;
+ TestThreadPool manager;
ran.storeRelaxed(false);
manager.start(new TestTask());
QTRY_VERIFY(ran.loadRelaxed());
@@ -302,7 +319,7 @@ public:
*/
void tst_QThreadPool::threadRecycling()
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.start(new ThreadRecorderTask());
threadRecyclingSemaphore.acquire();
@@ -323,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:
@@ -348,7 +384,7 @@ void tst_QThreadPool::expiryTimeout()
{
ExpiryTimeoutTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
int expiryTimeout = threadPool.expiryTimeout();
@@ -382,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);
@@ -404,7 +437,7 @@ void tst_QThreadPool::expiryTimeoutRace() // QTBUG-3786
class ExceptionTask : public QRunnable
{
public:
- void run()
+ void run() override
{
throw new int;
}
@@ -414,7 +447,7 @@ void tst_QThreadPool::exceptions()
{
ExceptionTask task;
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
// Uncomment this for a nice crash.
// threadPool.start(&task);
}
@@ -443,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
@@ -455,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
@@ -485,8 +521,8 @@ 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;
threadPool.start(&task);
@@ -547,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
@@ -569,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
@@ -596,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()
@@ -610,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
@@ -633,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
@@ -658,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
@@ -685,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);
@@ -692,32 +733,130 @@ void tst_QThreadPool::reserveAndStart() // QTBUG-21051
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), 1);
- // start a task, to get a running thread
+ // start a task, to get a running thread, works since one thread is always allowed
WaitingTask task;
threadpool->start(&task);
QCOMPARE(threadpool->activeThreadCount(), 2);
+ // 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));
- QTRY_COMPARE(threadpool->activeThreadCount(), 1);
-
- // start() will therefore do a failing tryStart(), followed by enqueueTask()
- // which will actually wake up the waiting thread.
+ // 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(task.count.loadRelaxed(), 2);
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
+ task2.waitForStarted.acquire();
+ task2.waitBeforeDone.release();
+
+ 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);
- threadpool->setMaxThreadCount(savedLimit);
+ // Now we can start task2
+ QVERIFY(threadpool->tryStart(&task2));
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+ task2.waitBeforeDone.release();
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
+
+ threadpool->reserveThread();
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+ task1.waitBeforeDone.release();
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
}
static QAtomicInt count;
@@ -735,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());
}
@@ -762,13 +901,13 @@ 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());
}
@@ -799,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)
@@ -828,7 +967,7 @@ void tst_QThreadPool::tryStartCount()
};
SleeperTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
const int runs = 5;
for (int i = 0; i < runs; ++i) {
@@ -876,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
@@ -888,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);
}
@@ -896,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);
@@ -906,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);
@@ -916,7 +1056,7 @@ void tst_QThreadPool::waitForDone()
threadPool.start(new CountingRunnable());
runs += 2;
}
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), runs);
}
}
@@ -938,7 +1078,7 @@ void tst_QThreadPool::waitForDoneTimeout()
}
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
mutex.lock();
threadPool.start(new BlockedTask(mutex));
@@ -962,7 +1102,7 @@ void tst_QThreadPool::clear()
}
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(10);
int runs = 2 * threadPool.maxThreadCount();
count.storeRelaxed(0);
@@ -971,7 +1111,7 @@ void tst_QThreadPool::clear()
}
threadPool.clear();
sem.release(threadPool.maxThreadCount());
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount());
}
@@ -981,10 +1121,10 @@ void tst_QThreadPool::clearWithAutoDelete()
{
public:
MyRunnable() {}
- void run() override { QThread::usleep(30); }
+ void run() override { QThread::sleep(30us); }
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(4);
const int loopCount = 20;
const int batchSize = 500;
@@ -997,7 +1137,6 @@ void tst_QThreadPool::clearWithAutoDelete()
threadPool.start(runnable);
}
}
- QVERIFY(threadPool.waitForDone());
}
void tst_QThreadPool::tryTake()
@@ -1037,7 +1176,7 @@ void tst_QThreadPool::tryTake()
Runs = MaxThreadCount * OverProvisioning
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(MaxThreadCount);
BlockingRunnable *runnables[Runs];
@@ -1069,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));
@@ -1080,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());
@@ -1096,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());
@@ -1138,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);
}
@@ -1202,7 +1342,7 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
QSemaphore mainBarrier;
QSemaphore taskBarrier;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
Task task1(&mainBarrier, &taskBarrier);
@@ -1233,7 +1373,7 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
taskBarrier.release(1);
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(threadPool.activeThreadCount(), 0);
}
@@ -1266,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
@@ -1297,12 +1437,55 @@ void tst_QThreadPool::waitForDoneAfterTake()
// Release runnables that are waiting and expect all runnables to complete
threadBarrier.release(threadCount);
+}
+
+/*
+ Try trigger reuse of expired threads and check that all tasks execute.
- // 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.");
+ 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/BLACKLIST b/tests/auto/corelib/thread/qthreadstorage/BLACKLIST
deleted file mode 100644
index 84906ff86e..0000000000
--- a/tests/auto/corelib/thread/qthreadstorage/BLACKLIST
+++ /dev/null
@@ -1,3 +0,0 @@
-# QTBUG-87431
-[crashOnExit]
-android
diff --git a/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
index 1340b30388..14d8d7dd40 100644
--- a/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
@@ -1,10 +1,16 @@
-# special case skip regeneration
-# Generated from qthreadstorage.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qthreadstorage Test:
#####################################################################
+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 @@ qt_internal_add_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/CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt
index a6de9678c0..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_internal_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/tst_qthreadstorage.cpp b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
index 40bc815c33..ca382cf60c 100644
--- a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
+++ b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
@@ -1,33 +1,10 @@
-/****************************************************************************
-**
-** 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 <QTest>
+#if QT_CONFIG(process)
#include <QProcess>
+#endif
#include <QTestEventLoop>
#include <qcoreapplication.h>
@@ -295,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 e600ff3a42..0a2830622e 100644
--- a/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qwaitcondition.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qwaitcondition Test:
#####################################################################
+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/tst_qwaitcondition.cpp b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
index 56ca0bc53d..4e3413afe8 100644
--- a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
+++ b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.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 <QTest>
#include <QReadWriteLock>
@@ -397,7 +372,7 @@ public:
{ }
static inline void sleep(ulong s)
- { QThread::sleep(s); }
+ { QThread::sleep(std::chrono::seconds{s}); }
void run() override
{
@@ -429,7 +404,7 @@ public:
{ }
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 200fa2ee3c..5345522ea5 100644
--- a/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
+++ b/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qwritelocker.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qwritelocker Test:
#####################################################################
+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/tst_qwritelocker.cpp b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
index 2828b529a8..b4e6b45dbd 100644
--- a/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
+++ b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.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 <QTest>