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