diff options
Diffstat (limited to 'tests/auto/corelib/thread')
63 files changed, 1529 insertions, 632 deletions
diff --git a/tests/auto/corelib/thread/CMakeLists.txt b/tests/auto/corelib/thread/CMakeLists.txt index 356ae0346c..68110b652b 100644 --- a/tests/auto/corelib/thread/CMakeLists.txt +++ b/tests/auto/corelib/thread/CMakeLists.txt @@ -1,11 +1,24 @@ -# Generated from thread.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(QT_BUILD_WASM_BATCHED_TESTS) # not all tests currently work in WebAssembly + add_subdirectory(qatomicint) + add_subdirectory(qatomicinteger) + add_subdirectory(qatomicpointer) + add_subdirectory(qfuturesynchronizer) + add_subdirectory(qmutexlocker) + add_subdirectory(qreadlocker) + add_subdirectory(qresultstore) + add_subdirectory(qwritelocker) + return() +endif() if(QT_FEATURE_thread) add_subdirectory(qatomicint) add_subdirectory(qatomicinteger) add_subdirectory(qatomicpointer) add_subdirectory(qresultstore) - if(NOT INTEGRITY) + if(QT_FEATURE_concurrent AND NOT INTEGRITY) add_subdirectory(qfuture) endif() add_subdirectory(qfuturesynchronizer) @@ -14,12 +27,10 @@ if(QT_FEATURE_thread) 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) @@ -29,9 +40,8 @@ if(QT_FEATURE_thread) add_subdirectory(qpromise) endif() endif() -# special case begin + # QTBUG-87431 if(TARGET Qt::Concurrent AND NOT INTEGRITY) add_subdirectory(qfuturewatcher) endif() -# special case end diff --git a/tests/auto/corelib/thread/qatomicint/CMakeLists.txt b/tests/auto/corelib/thread/qatomicint/CMakeLists.txt index 867388cfbf..239f3cce87 100644 --- a/tests/auto/corelib/thread/qatomicint/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicint/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qatomicint.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicint Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicint LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicint SOURCES tst_qatomicint.cpp diff --git a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp index 3d1b96a896..63cb494c11 100644 --- a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp +++ b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -161,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__)) @@ -205,17 +201,9 @@ 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 #if !defined(Q_PROCESSOR_X86_32) && defined(Q_ATOMIC_INT64_IS_SUPPORTED) // The alignment is different on x86_32 @@ -493,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 @@ -528,7 +515,6 @@ void tst_QAtomicInt::testAndSet() if (!result) QCOMPARE(currentval, value); } -#endif } void tst_QAtomicInt::isFetchAndStoreNative() @@ -690,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; @@ -706,7 +691,6 @@ void tst_QAtomicInt::fetchAndAdd_data() QTest::newRow("5451+-4362") << 5451 << -4362; QTest::newRow("7272+-2181") << 7272 << -2181; - QTest::newRow("0+1") << 0 << 1; QTest::newRow("-1+0") << -1 << 0; QTest::newRow("-1+2") << -1 << 2; QTest::newRow("-2+1") << -2 << 1; diff --git a/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt index 8f0637a4d2..03a6323a1f 100644 --- a/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from qatomicinteger.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(char) add_subdirectory(char16_t) diff --git a/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt index 57601dc0e4..882a9298f6 100644 --- a/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from char.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_char Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_char LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_char SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt index 6ac58c9136..8e53b59689 100644 --- a/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from char16_t.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_char16_t Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_char16_t LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_char16_t SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt index 911e9f5067..5881d475f4 100644 --- a/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from char32_t.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_char32_t Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_char32_t LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_char32_t SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt index be59e2dc98..0915e77a8d 100644 --- a/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from int.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_int Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_int LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_int SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt index 3f632ff212..adf6638bfa 100644 --- a/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from long.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_long Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_long LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_long SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt index 3de9227bb1..2ec977d7cb 100644 --- a/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qlonglong.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_qlonglong Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_qlonglong LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_qlonglong SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt index cc6b5126d9..a2450931d5 100644 --- a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qptrdiff.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_qptrdiff Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_qptrdiff LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_qptrdiff SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt index 44cf39f06d..98302b5d07 100644 --- a/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from quintptr.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_quintptr Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_quintptr LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_quintptr SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt index 18d3e384b9..13acfc3e2b 100644 --- a/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qulonglong.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_qulonglong Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_qulonglong LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_qulonglong SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt index 760967532a..127f752cc2 100644 --- a/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from schar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_schar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_schar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_schar SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt index 3f762e34ab..df9d2af4c3 100644 --- a/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from short.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_short Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_short LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_short SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp index 61f0697d6c..d1a8a8f729 100644 --- a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp +++ b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp @@ -1,29 +1,5 @@ // Copyright (C) 2016 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#ifdef QT_ATOMIC_FORCE_CXX11 -// We need to check if this compiler has C++11 atomics and constexpr support. -// We can't rely on qcompilerdetection.h because it forces all of qglobal.h to -// be included, which causes qbasicatomic.h to be included too. -// Incomplete, but ok -# if defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1500 && (__cplusplus >= 201103L || defined(__INTEL_CXX11_MODE__)) -# elif defined(__clang__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) -# if !__has_feature(cxx_constexpr) || !__has_feature(cxx_atomic) || !__has_include(<atomic>) -# undef QT_ATOMIC_FORCE_CXX11 -# endif -# elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)) -# elif defined(_MSC_VER) - // We need MSVC 2015 because of: atomics (2012), constexpr (2015), and unrestricted unions (2015). - // Support for constexpr is not working completely on MSVC 2015 but it's enough for the test. -# else -# undef QT_ATOMIC_FORCE_CXX11 -# endif - -# ifndef QT_ATOMIC_FORCE_CXX11 -# undef QATOMIC_TEST_TYPE -# define QATOMIC_TEST_TYPE unsupported -# endif -#endif +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QAtomicInt> @@ -32,6 +8,12 @@ #include <limits.h> #include <wchar.h> +#if !defined(Q_ATOMIC_INT8_IS_SUPPORTED) +# error "QAtomicInteger for 8-bit types must be supported!" +#endif +#if !defined(Q_ATOMIC_INT16_IS_SUPPORTED) +# error "QAtomicInteger for 16-bit types must be supported!" +#endif #if !defined(Q_ATOMIC_INT32_IS_SUPPORTED) # error "QAtomicInteger for 32-bit types must be supported!" #endif @@ -40,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 @@ -92,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 @@ -102,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 @@ -317,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)); } @@ -459,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() @@ -540,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() @@ -763,8 +674,9 @@ void tst_QAtomicIntegerXX::fetchAndXor() QCOMPARE(atomic ^= minusOne, T(~value)); QCOMPARE(atomic ^= minusOne, T(value)); } - -#include "tst_qatomicinteger.moc" +} QTEST_APPLESS_MAIN(tst_QAtomicIntegerXX) +#include "tst_qatomicinteger.moc" + diff --git a/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt index 912365a6e9..95d88d31a6 100644 --- a/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from uchar.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_uchar Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_uchar LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_uchar SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt index 725fa91704..2ab977ef6a 100644 --- a/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from uint.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_uint Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_uint LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_uint SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt index d5d35f587d..7707bd53b5 100644 --- a/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from ulong.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_ulong Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_ulong LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_ulong SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt index fd7c002345..667e9eade6 100644 --- a/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from ushort.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_ushort Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_ushort LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_ushort SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt index aecb81005c..0e2d084b58 100644 --- a/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from wchar_t.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicinteger_wchar_t Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicinteger_wchar_t LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicinteger_wchar_t SOURCES ../tst_qatomicinteger.cpp diff --git a/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt b/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt index 2c3f32d7e3..cd8df9db66 100644 --- a/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt +++ b/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qatomicpointer.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qatomicpointer Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qatomicpointer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qatomicpointer SOURCES tst_qatomicpointer.cpp diff --git a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp index c6bd784270..347831819e 100644 --- a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp +++ b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/corelib/thread/qfuture/CMakeLists.txt b/tests/auto/corelib/thread/qfuture/CMakeLists.txt index 7321f89d83..aa989f3df1 100644 --- a/tests/auto/corelib/thread/qfuture/CMakeLists.txt +++ b/tests/auto/corelib/thread/qfuture/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qfuture.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfuture Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfuture LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfuture SOURCES tst_qfuture.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index 85239395d5..3fc796514d 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -1,5 +1,8 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses + #define QFUTURE_TEST #include <QCoreApplication> @@ -11,6 +14,7 @@ #include <QVarLengthArray> #include <QSet> #include <QList> +#include <private/qobject_p.h> #include <QTest> #include <qfuture.h> @@ -22,14 +26,20 @@ #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 +using namespace std::chrono_literals; +static constexpr auto DefaultWaitTime = 2s; + using namespace Qt::StringLiterals; class SenderObject : public QObject @@ -133,6 +143,18 @@ private: std::function<void ()> m_fn; }; +// Emulates QWidget behavior by deleting its children early in the destructor +// instead of leaving it to ~QObject() +class FakeQWidget : public QObject +{ + Q_OBJECT +public: + ~FakeQWidget() override { + auto *d = QObjectPrivate::get(this); + d->deleteChildren(); + } +}; + using UniquePtr = std::unique_ptr<int>; class tst_QFuture: public QObject @@ -182,6 +204,7 @@ private slots: #endif void onCanceled(); void cancelContinuations(); + void continuationsWithContext_data(); void continuationsWithContext(); void continuationsWithMoveOnlyLambda(); #if 0 @@ -203,6 +226,7 @@ private slots: void rejectPendingResultOverwrite(); void createReadyFutures(); + void continuationsAfterReadyFutures(); void getFutureInterface(); void convertQMetaType(); @@ -220,7 +244,9 @@ private slots: void whenAnyDifferentTypesWithCanceled(); void whenAnyDifferentTypesWithFailed(); + void continuationOverride(); void continuationsDontLeak(); + void cancelAfterFinishWithContinuations(); void unwrap(); @@ -1076,13 +1102,13 @@ void tst_QFuture::multipleResults() QList<int> fasit = QList<int>() << 1 << 2 << 3 << 4; { QList<int> results; - for (int result : qAsConst(f)) + for (int result : std::as_const(f)) results.append(result); QCOMPARE(results, fasit); } { QList<int> results; - for (int result : qAsConst(copy)) + for (int result : std::as_const(copy)) results.append(result); QCOMPARE(results, fasit); } @@ -1955,7 +1981,7 @@ void tst_QFuture::nonGlobalThreadPool() void run() override { const int ms = 100 + (QRandomGenerator::global()->bounded(100) - 100/2); - QThread::msleep(ulong(ms)); + QThread::sleep(std::chrono::milliseconds{ms}); reportResult(Answer); reportFinished(); } @@ -2240,6 +2266,26 @@ void tst_QFuture::then() QVERIFY(threadId1 != QThread::currentThreadId()); QVERIFY(threadId2 != QThread::currentThreadId()); } + + // QTBUG-106083 & QTBUG-105182 + { + QThread thread; + thread.start(); + + QObject context; + context.moveToThread(&thread); + + auto future = QtConcurrent::run([] { + return 42; + }).then([] (int result) { + return result + 1; + }).then(&context, [] (int result) { + return result + 1; + }); + QCOMPARE(future.result(), 44); + thread.quit(); + thread.wait(); + } } template<class Type, class Callable> @@ -3024,7 +3070,7 @@ void tst_QFuture::cancelContinuations() // The chain is cancelled before the execution of continuations { - auto f = QtFuture::makeReadyFuture(42); + auto f = QtFuture::makeReadyValueFuture(42); f.cancel(); int checkpoint = 0; @@ -3079,7 +3125,7 @@ void tst_QFuture::cancelContinuations() #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 handeled inside + // while there's an exception in the chain, which is handled inside // the continuations. { QPromise<int> promise; @@ -3116,17 +3162,97 @@ void tst_QFuture::cancelContinuations() QCOMPARE(checkpoint, 3); } #endif // QT_NO_EXCEPTIONS + + // Check notifications from QFutureWatcher + { + QPromise<void> p; + auto f = p.future(); + + auto f1 = f.then([] {}); + auto f2 = f1.then([] {}); + + QFutureWatcher<void> watcher1, watcher2; + int state = 0; + QObject::connect(&watcher1, &QFutureWatcher<void>::started, [&] { + QCOMPARE(state, 0); + ++state; + }); + QObject::connect(&watcher1, &QFutureWatcher<void>::canceled, [&] { + QCOMPARE(state, 1); + ++state; + }); + QObject::connect(&watcher1, &QFutureWatcher<void>::finished, [&] { + QCOMPARE(state, 2); + ++state; + }); + QObject::connect(&watcher2, &QFutureWatcher<void>::started, [&] { + QCOMPARE(state, 3); + ++state; + }); + QObject::connect(&watcher2, &QFutureWatcher<void>::canceled, [&] { + QCOMPARE(state, 4); + ++state; + }); + QObject::connect(&watcher2, &QFutureWatcher<int>::finished, [&] { + QCOMPARE(state, 5); + ++state; + }); + + watcher1.setFuture(f1); + watcher2.setFuture(f2); + + p.start(); + f.cancel(); + p.finish(); + + qApp->processEvents(); + + QCOMPARE(state, 6); + QVERIFY(watcher1.isFinished()); + QVERIFY(watcher1.isCanceled()); + QVERIFY(watcher2.isFinished()); + QVERIFY(watcher2.isCanceled()); + } + + // Cancel continuations with context (QTBUG-108790) + { + // This test should pass with ASan + auto future = QtConcurrent::run([] {}); + future.then(this, [] {}); + future.waitForFinished(); + future.cancel(); + } +} + +void tst_QFuture::continuationsWithContext_data() +{ + QTest::addColumn<bool>("inOtherThread"); + QTest::addRow("in-other-thread") << true; + QTest::addRow("in-main-thread-qtbug119406") << false; } void tst_QFuture::continuationsWithContext() { - QThread thread; - thread.start(); + QFETCH(bool, inOtherThread); + auto tstThread = QThread::currentThread(); + QThread *thread = inOtherThread ? new QThread + : tstThread; auto context = new QObject(); - context->moveToThread(&thread); - auto tstThread = QThread::currentThread(); + const auto cleanupGuard = qScopeGuard([&] { + context->deleteLater(); + if (thread != tstThread) { + thread->quit(); + thread->wait(); + delete thread; + } + }); + + if (inOtherThread) { + thread->start(); + context->moveToThread(thread); + } // .then() { @@ -3139,12 +3265,12 @@ void tst_QFuture::continuationsWithContext() }) .then(context, [&](int val) { - if (QThread::currentThread() != &thread) + if (QThread::currentThread() != thread) return 0; return val + 1; }) .then([&](int val) { - if (QThread::currentThread() != &thread) + if (QThread::currentThread() != thread) return 0; return val + 1; }); @@ -3160,12 +3286,12 @@ void tst_QFuture::continuationsWithContext() auto future = promise.future() .onCanceled(context, [&] { - if (QThread::currentThread() != &thread) + if (QThread::currentThread() != thread) return 0; return 1; }) .then([&](int val) { - if (QThread::currentThread() != &thread) + if (QThread::currentThread() != thread) return 0; return val + 1; }); @@ -3175,6 +3301,40 @@ void tst_QFuture::continuationsWithContext() QCOMPARE(future.result(), 2); } + // Cancellation when the context object is destroyed + { + // Use something like QWidget which deletes its children early, i.e. + // before ~QObject() runs. This behavior can lead to side-effects + // like QPointers to the parent not being set to nullptr during child + // object destruction. + QPointer shortLivedContext = new FakeQWidget(); + shortLivedContext->moveToThread(thread); + + QPromise<int> promise; + auto future = promise.future() + .then(shortLivedContext, [&](int val) { + if (QThread::currentThread() != thread) + return 0; + return val + 1000; + }) + .onCanceled([&, ptr=QPointer(shortLivedContext)] { + if (QThread::currentThread() != thread) + return 0; + if (ptr) + return 1; + return 2; + }); + promise.start(); + + QMetaObject::invokeMethod(shortLivedContext, [&]() { + delete shortLivedContext; + }, inOtherThread ? Qt::BlockingQueuedConnection + : Qt::DirectConnection); + + promise.finish(); + QCOMPARE(future.result(), 2); + } + #ifndef QT_NO_EXCEPTIONS // .onFaled() { @@ -3187,12 +3347,12 @@ void tst_QFuture::continuationsWithContext() }) .onFailed(context, [&] { - if (QThread::currentThread() != &thread) + if (QThread::currentThread() != thread) return 0; return 1; }) .then([&](int val) { - if (QThread::currentThread() != &thread) + if (QThread::currentThread() != thread) return 0; return val + 1; }); @@ -3201,11 +3361,6 @@ void tst_QFuture::continuationsWithContext() QCOMPARE(future.result(), 2); } #endif // QT_NO_EXCEPTIONS - - context->deleteLater(); - - thread.quit(); - thread.wait(); } void tst_QFuture::continuationsWithMoveOnlyLambda() @@ -3213,7 +3368,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda() // .then() { std::unique_ptr<int> uniquePtr(new int(42)); - auto future = QtFuture::makeReadyFuture().then([p = std::move(uniquePtr)] { return *p; }); + auto future = QtFuture::makeReadyVoidFuture() + .then([p = std::move(uniquePtr)] { return *p; }); QCOMPARE(future.result(), 42); } // .then() with thread pool @@ -3221,8 +3377,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda() QThreadPool pool; std::unique_ptr<int> uniquePtr(new int(42)); - auto future = - QtFuture::makeReadyFuture().then(&pool, [p = std::move(uniquePtr)] { return *p; }); + auto future = QtFuture::makeReadyVoidFuture() + .then(&pool, [p = std::move(uniquePtr)] { return *p; }); QCOMPARE(future.result(), 42); } // .then() with context @@ -3230,8 +3386,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda() QObject object; std::unique_ptr<int> uniquePtr(new int(42)); - auto future = QtFuture::makeReadyFuture().then(&object, - [p = std::move(uniquePtr)] { return *p; }); + auto future = QtFuture::makeReadyVoidFuture() + .then(&object, [p = std::move(uniquePtr)] { return *p; }); QCOMPARE(future.result(), 42); } @@ -3295,17 +3451,6 @@ void tst_QFuture::testFutureTaken(QFuture<T> &noMoreFuture) { QCOMPARE(noMoreFuture.isValid(), false); QCOMPARE(noMoreFuture.resultCount(), 0); - QCOMPARE(noMoreFuture.isStarted(), false); - QCOMPARE(noMoreFuture.isRunning(), false); - QCOMPARE(noMoreFuture.isSuspending(), false); - QCOMPARE(noMoreFuture.isSuspended(), false); -#if QT_DEPRECATED_SINCE(6, 0) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - QCOMPARE(noMoreFuture.isPaused(), false); -QT_WARNING_POP -#endif - QCOMPARE(noMoreFuture.isFinished(), false); QCOMPARE(noMoreFuture.progressValue(), 0); } @@ -3393,7 +3538,7 @@ void tst_QFuture::runAndTake() auto rabbit = [](){ // Let's wait a bit to give the test below some time // to sync up with us with its watcher. - QThread::currentThread()->msleep(100); + QThread::currentThread()->sleep(std::chrono::milliseconds{100}); return UniquePtr(new int(10)); }; @@ -3406,7 +3551,7 @@ void tst_QFuture::runAndTake() auto gotcha = QtConcurrent::run(rabbit); watcha.setFuture(gotcha); - loop.enterLoopMSecs(500); + loop.enterLoop(500ms); if (loop.timeout()) QSKIP("Failed to run the task, nothing to test"); @@ -3477,14 +3622,14 @@ void tst_QFuture::resultsReadyAt() // Run event loop, QCoreApplication::postEvent is in use // in QFutureInterface: - eventProcessor.enterLoopMSecs(2000); + eventProcessor.enterLoop(DefaultWaitTime); QVERIFY(!eventProcessor.timeout()); if (QTest::currentTestFailed()) // Failed in our lambda observing 'ready at' return; QCOMPARE(reported, nExpectedResults); QCOMPARE(nExpectedResults, iface.future().resultCount()); - QCOMPARE(readyCounter.count(), 3); + QCOMPARE(readyCounter.size(), 3); QCOMPARE(taken, 0b1111); } @@ -3724,8 +3869,6 @@ 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()); @@ -3764,7 +3907,7 @@ void tst_QFuture::signalConnect() { SenderObject sender; -#if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) +#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"); @@ -3784,9 +3927,6 @@ void tst_QFuture::signalConnect() void tst_QFuture::waitForFinished() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else QFutureInterface<void> fi; auto future = fi.future(); @@ -3807,7 +3947,6 @@ void tst_QFuture::waitForFinished() QVERIFY(waitingThread->wait()); QVERIFY(waitingThread->isFinished()); -#endif } void tst_QFuture::rejectResultOverwrite_data() @@ -3852,9 +3991,9 @@ void tst_QFuture::rejectResultOverwrite() }); // Run event loop, QCoreApplication::postEvent is in use // in QFutureInterface: - eventProcessor.enterLoopMSecs(2000); + eventProcessor.enterLoop(DefaultWaitTime); QVERIFY(!eventProcessor.timeout()); - QCOMPARE(resultCounter.count(), 1); + QCOMPARE(resultCounter.size(), 1); f.resume(); // overwrite with lvalue @@ -3891,9 +4030,9 @@ void tst_QFuture::rejectResultOverwrite() QTimer::singleShot(50, [&f]() { f.suspend(); // should exit the loop }); - eventProcessor.enterLoopMSecs(2000); + eventProcessor.enterLoop(DefaultWaitTime); QVERIFY(!eventProcessor.timeout()); - QCOMPARE(resultCounter.count(), 1); + QCOMPARE(resultCounter.size(), 1); f.resume(); QCOMPARE(f.results(), initResults); } @@ -3930,9 +4069,9 @@ void tst_QFuture::rejectPendingResultOverwrite() }); // Run event loop, QCoreApplication::postEvent is in use // in QFutureInterface: - eventProcessor.enterLoopMSecs(2000); + eventProcessor.enterLoop(DefaultWaitTime); QVERIFY(!eventProcessor.timeout()); - QCOMPARE(resultCounter.count(), 1); + QCOMPARE(resultCounter.size(), 1); f.resume(); } @@ -3974,9 +4113,9 @@ void tst_QFuture::rejectPendingResultOverwrite() QTimer::singleShot(50, [&f]() { f.suspend(); // should exit the loop }); - eventProcessor.enterLoopMSecs(2000); + eventProcessor.enterLoop(DefaultWaitTime); QVERIFY(!eventProcessor.timeout()); - QCOMPARE(resultCounter.count(), 1); + QCOMPARE(resultCounter.size(), 1); f.resume(); } @@ -3989,6 +4128,9 @@ void tst_QFuture::rejectPendingResultOverwrite() void tst_QFuture::createReadyFutures() { +#if QT_DEPRECATED_SINCE(6, 10) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED // using const T & { const int val = 42; @@ -4024,6 +4166,30 @@ void tst_QFuture::createReadyFutures() QCOMPARE(f.resultCount(), 3); QCOMPARE(f.results(), values); } +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(6, 10) + + // test makeReadyValueFuture<T>() + { + const int val = 42; + auto f = QtFuture::makeReadyValueFuture(val); + QCOMPARE_EQ(f.result(), val); + + int otherVal = 42; + f = QtFuture::makeReadyValueFuture(otherVal); + QCOMPARE_EQ(f.result(), otherVal); + } + { + auto f = QtFuture::makeReadyValueFuture(std::make_unique<int>(42)); + QCOMPARE(*f.takeResult(), 42); + } + // test makeReadyVoidFuture() + { + auto f = QtFuture::makeReadyVoidFuture(); + QVERIFY(f.isStarted()); + QVERIFY(!f.isRunning()); + QVERIFY(f.isFinished()); + } #ifndef QT_NO_EXCEPTIONS // using QException @@ -4052,12 +4218,205 @@ void tst_QFuture::createReadyFutures() QVERIFY(caught); } #endif + + // testing makeReadyRangeFuture with various containers + { + const QList<int> expectedResult{1, 2, 3}; + + const QList<int> list{1, 2, 3}; + auto f = QtFuture::makeReadyRangeFuture(list); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + QVarLengthArray<int> varArray{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(varArray); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::vector<int> vec{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(std::move(vec)); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + f = QtFuture::makeReadyRangeFuture(std::array<int, 3>{1, 2, 3}); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + f = QtFuture::makeReadyRangeFuture(std::list<int>{1, 2, 3}); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::forward_list<int> fwdlist{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(fwdlist); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + const QSet<int> qset{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(qset); + QCOMPARE_EQ(f.resultCount(), 3); + auto result = f.results(); + std::sort(result.begin(), result.end()); + QCOMPARE_EQ(result, expectedResult); + + const QMap<QString, int> qmap{ + {"one", 1}, + {"two", 2}, + {"three", 3} + }; + f = QtFuture::makeReadyRangeFuture(qmap); + QCOMPARE_EQ(f.resultCount(), 3); + result = f.results(); + std::sort(result.begin(), result.end()); + QCOMPARE_EQ(result, expectedResult); + + std::set<int> stdset{1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(stdset); + QCOMPARE_EQ(f.resultCount(), 3); + result = f.results(); + std::sort(result.begin(), result.end()); + QCOMPARE_EQ(result, expectedResult); + + // testing ValueType[N] overload + const int c_array[] = {1, 2, 3}; + f = QtFuture::makeReadyRangeFuture(c_array); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + f = QtFuture::makeReadyRangeFuture({1, 2, 3}); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + } + // testing makeReadyRangeFuture with a more complex underlying type + { + QObject obj1; + QObject obj2; + QObject obj3; + + const QList<QObject*> expectedResult{&obj1, &obj2, &obj3}; + + const QList<QObject*> list{&obj1, &obj2, &obj3}; + auto f = QtFuture::makeReadyRangeFuture(list); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + std::list<QObject*> stdlist{&obj1, &obj2, &obj3}; + f = QtFuture::makeReadyRangeFuture(std::move(stdlist)); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + + QObject* const c_array[] = {&obj1, &obj2, &obj3}; + f = QtFuture::makeReadyRangeFuture(c_array); + QCOMPARE_EQ(f.resultCount(), 3); + QCOMPARE_EQ(f.results(), expectedResult); + } +} + +void tst_QFuture::continuationsAfterReadyFutures() +{ + // continuations without a context + { + QFuture<int> f = QtFuture::makeReadyValueFuture(42) + .then([](int val) { + return val + 10; + }) + .onCanceled([]() { + return -1; + }); + QCOMPARE(f.result(), 52); + } + { + auto rangeF = QtFuture::makeReadyRangeFuture({1, 2, 3}); + QFuture<int> f = rangeF + .then([vals = rangeF.results()](auto) { + return vals.last(); + }) + .onCanceled([]() { + return -1; + }); + QCOMPARE(f.result(), 3); + } + { + QFuture<int> f = QtFuture::makeReadyVoidFuture() + .then([]() { + return 1; + }) + .onCanceled([]() { + return -1; + }); + QCOMPARE(f.result(), 1); + } +#ifndef QT_NO_EXCEPTIONS + { + QException e; + QFuture<int> f = QtFuture::makeExceptionalFuture<int>(e) + .then([](int) { + return 1; + }) + .onCanceled([]() { + return -1; + }) + .onFailed([](const QException &) { + return -2; + }); + QCOMPARE(f.result(), -2); + } +#endif + + // continuations with a context + QObject context; + { + QFuture<int> f = QtFuture::makeReadyValueFuture(42) + .then(&context, [](int val) { + return val + 10; + }) + .onCanceled([]() { + return -1; + }); + QCOMPARE(f.result(), 52); + } + { + auto rangeF = QtFuture::makeReadyRangeFuture({1, 2, 3}); + QFuture<int> f = rangeF + .then(&context, [vals = rangeF.results()](auto) { + return vals.last(); + }) + .onCanceled([]() { + return -1; + }); + QCOMPARE(f.result(), 3); + } + { + QFuture<int> f = QtFuture::makeReadyVoidFuture() + .then(&context, []() { + return 1; + }) + .onCanceled([]() { + return -1; + }); + QCOMPARE(f.result(), 1); + } +#ifndef QT_NO_EXCEPTIONS + { + QException e; + QFuture<int> f = QtFuture::makeExceptionalFuture<int>(e) + .then(&context, [](int) { + return 1; + }) + .onCanceled([]() { + return -1; + }) + .onFailed([](const QException &) { + return -2; + }); + QCOMPARE(f.result(), -2); + } +#endif } void tst_QFuture::getFutureInterface() { const int val = 42; - QFuture<int> f = QtFuture::makeReadyFuture(val); + QFuture<int> f = QtFuture::makeReadyValueFuture(val); auto interface = QFutureInterfaceBase::get(f); QCOMPARE(interface.resultCount(), 1); @@ -4071,7 +4430,7 @@ void tst_QFuture::convertQMetaType() QVERIFY(QMetaType::canConvert(intType, voidType)); const int val = 42; - QFuture<int> f = QtFuture::makeReadyFuture(val); + QFuture<int> f = QtFuture::makeReadyValueFuture(val); auto variant = QVariant::fromValue(f); QVERIFY(variant.convert(voidType)); @@ -4200,7 +4559,7 @@ void tst_QFuture::whenAllIteratorsWithFailed() p1.finish(); QVERIFY(finished); #else - QSKIP("Exceptions are disabled, skipping the test") + QSKIP("Exceptions are disabled, skipping the test"); #endif } @@ -4581,6 +4940,35 @@ void tst_QFuture::whenAnyDifferentTypesWithFailed() #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; } @@ -4639,6 +5027,65 @@ void tst_QFuture::continuationsDontLeak() 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() diff --git a/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt b/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt index e41b8e70c6..c0f4561c51 100644 --- a/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt +++ b/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qfuturesynchronizer.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfuturesynchronizer Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfuturesynchronizer LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfuturesynchronizer SOURCES tst_qfuturesynchronizer.cpp diff --git a/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp b/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp index 5f1e26ad02..62ad4f872a 100644 --- a/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp +++ b/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -13,6 +13,7 @@ class tst_QFutureSynchronizer : public QObject private Q_SLOTS: void construction(); + void setFutureAliasingExistingMember(); void addFuture(); void cancelOnWait(); void clearFutures(); @@ -33,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; @@ -82,7 +115,7 @@ void tst_QFutureSynchronizer::futures() synchronizer.addFuture(future); } - QCOMPARE(futures.count(), synchronizer.futures().count()); + QCOMPARE(futures.size(), synchronizer.futures().size()); } void tst_QFutureSynchronizer::setFuture() diff --git a/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt b/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt index 93ac2dd870..65417199a3 100644 --- a/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt +++ b/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qfuturewatcher.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfuturewatcher Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfuturewatcher LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qfuturewatcher SOURCES tst_qfuturewatcher.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Concurrent Qt::CorePrivate ) diff --git a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp index e9eb7b24b0..40aa89ded4 100644 --- a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp +++ b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCoreApplication> #include <QDebug> #include <QElapsedTimer> @@ -9,6 +9,7 @@ #include <private/qfutureinterface_p.h> using namespace QtConcurrent; +using namespace std::chrono_literals; #include <QTest> @@ -25,6 +26,7 @@ private slots: void cancelAndFinish(); void resultAt(); void resultReadyAt(); + void orderedResultReadyAt(); void futureSignals(); void watchFinishedFuture(); void watchCanceledFuture(); @@ -238,8 +240,8 @@ void tst_QFutureWatcher::cancelAndFinish() fi.cancelAndFinish(); // The signals should be emitted only once - QTRY_COMPARE(canceledSpy.count(), 1); - QTRY_COMPARE(finishedSpy.count(), 1); + QTRY_COMPARE(canceledSpy.size(), 1); + QTRY_COMPARE(finishedSpy.size(), 1); } class IntTask : public RunFunctionTaskBase<int> @@ -279,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 @@ -349,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); } } @@ -402,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() @@ -436,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() @@ -462,17 +486,17 @@ 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; @@ -672,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 @@ -707,7 +731,7 @@ void tst_QFutureWatcher::cancelEvents() QVERIFY(finishedSpy.wait()); - QCOMPARE(resultReadySpy.count(), 0); + QCOMPARE(resultReadySpy.size(), 0); } #if QT_DEPRECATED_SINCE(6, 0) @@ -734,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(); } @@ -769,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); } } @@ -789,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); @@ -836,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(); } @@ -872,7 +896,7 @@ void tst_QFutureWatcher::suspendEvents() a.resume(); // should give us no results. QTest::qWait(10); - QCOMPARE(resultReadySpy.count(), 0); + QCOMPARE(resultReadySpy.size(), 0); } } @@ -900,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() @@ -951,23 +975,23 @@ 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); @@ -997,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()); @@ -1139,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 diff --git a/tests/auto/corelib/thread/qmutex/CMakeLists.txt b/tests/auto/corelib/thread/qmutex/CMakeLists.txt index 1412036fc9..5a92a2bffd 100644 --- a/tests/auto/corelib/thread/qmutex/CMakeLists.txt +++ b/tests/auto/corelib/thread/qmutex/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qmutex.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmutex Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmutex LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmutex SOURCES tst_qmutex.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp index 3449b9109d..4753444ab9 100644 --- a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp +++ b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp @@ -1,6 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSemaphore> @@ -10,9 +10,12 @@ #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 { Q_OBJECT @@ -26,8 +29,6 @@ public: Q_ENUM(TimeUnit) private slots: - void convertToMilliseconds_data(); - void convertToMilliseconds(); void tryLock_non_recursive(); void try_lock_for_non_recursive(); void try_lock_until_non_recursive(); @@ -60,121 +61,15 @@ static QSemaphore threadsTurn; enum { #ifdef Q_OS_WIN systemTimersResolution = 16, +#elif defined(Q_OS_QNX) + systemTimersResolution = 10, #else systemTimersResolution = 1, #endif waitTime = 100 }; -#if __has_include(<chrono>) static constexpr std::chrono::milliseconds waitTimeAsDuration(waitTime); -#endif - -void tst_QMutex::convertToMilliseconds_data() -{ - QTest::addColumn<TimeUnit>("unit"); - QTest::addColumn<double>("doubleValue"); - QTest::addColumn<qint64>("intValue"); - QTest::addColumn<qint64>("expected"); - -#if !__has_include(<chrono>) - QSKIP("This test requires <chrono>"); -#endif - - auto add = [](TimeUnit unit, double d, long long i, qint64 expected) { - const QScopedArrayPointer<char> enumName(QTest::toString(unit)); - QTest::addRow("%s:%f:%lld", enumName.data(), d, i) - << unit << d << qint64(i) << expected; - }; - - auto forAllUnitsAdd = [=](double d, long long i, qint64 expected) { - for (auto unit : {TimeUnit::Nanoseconds, TimeUnit::Microseconds, TimeUnit::Milliseconds, TimeUnit::Seconds}) - add(unit, d, i, expected); - }; - - forAllUnitsAdd(-0.5, -1, 0); // all negative values result in 0 - - forAllUnitsAdd(0, 0, 0); - - add(TimeUnit::Nanoseconds, 1, 1, 1); - add(TimeUnit::Nanoseconds, 1000 * 1000, 1000 * 1000, 1); - add(TimeUnit::Nanoseconds, 1000 * 1000 + 0.5, 1000 * 1000 + 1, 2); - - add(TimeUnit::Microseconds, 1, 1, 1); - add(TimeUnit::Microseconds, 1000, 1000, 1); - add(TimeUnit::Microseconds, 1000 + 0.5, 1000 + 1, 2); - - add(TimeUnit::Milliseconds, 1, 1, 1); - add(TimeUnit::Milliseconds, 1.5, 2, 2); - - add(TimeUnit::Seconds, 0.9991, 1, 1000); - - // - // overflowing int results in INT_MAX (equivalent to a spurious wakeup after ~24 days); check it: - // - - // spot on: - add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000, INT_MAX * Q_INT64_C(1000) * 1000, INT_MAX); - add(TimeUnit::Microseconds, INT_MAX * 1000., INT_MAX * Q_INT64_C(1000), INT_MAX); - add(TimeUnit::Milliseconds, INT_MAX, INT_MAX, INT_MAX); - - // minimally above: - add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 + 1, INT_MAX * Q_INT64_C(1000) * 1000 + 1, INT_MAX); - add(TimeUnit::Microseconds, INT_MAX * 1000. + 1, INT_MAX * Q_INT64_C(1000) + 1, INT_MAX); - add(TimeUnit::Milliseconds, INT_MAX + 1., INT_MAX + Q_INT64_C(1), INT_MAX); - add(TimeUnit::Seconds, INT_MAX / 1000. + 1, INT_MAX / 1000 + 1, INT_MAX); - - // minimally below: - add(TimeUnit::Nanoseconds, INT_MAX * 1000. * 1000 - 1, INT_MAX * Q_INT64_C(1000) * 1000 - 1, INT_MAX); - add(TimeUnit::Microseconds, INT_MAX * 1000. - 1, INT_MAX * Q_INT64_C(1000) - 1, INT_MAX); - add(TimeUnit::Milliseconds, INT_MAX - 0.1, INT_MAX , INT_MAX); - -} - -void tst_QMutex::convertToMilliseconds() -{ -#if !__has_include(<chrono>) - QSKIP("This test requires <chrono>"); -#else - QFETCH(TimeUnit, unit); - QFETCH(double, doubleValue); - QFETCH(qint64, intValue); - QFETCH(qint64, expected); - - constexpr qint64 maxShort = std::numeric_limits<short>::max(); - constexpr qint64 maxInt = std::numeric_limits<int>::max(); - constexpr qint64 maxUInt = std::numeric_limits<uint>::max(); - - switch (unit) { -#define CASE(Unit, Period) \ - case TimeUnit::Unit: \ - DO(double, Period, doubleValue); \ - if (intValue < maxShort) \ - DO(short, Period, short(intValue)); \ - if (intValue < maxInt) \ - DO(int, Period, int(intValue)); \ - DO(qint64, Period, intValue); \ - if (intValue >= 0) { \ - if (intValue < maxUInt) \ - DO(uint, Period, uint(intValue)); \ - DO(quint64, Period, quint64(intValue)); \ - } \ - break -#define DO(Rep, Period, val) \ - do { \ - const std::chrono::duration<Rep, Period> wait((val)); \ - QCOMPARE(QtPrivate::convertToMilliseconds(wait), expected); \ - } while (0) - - CASE(Nanoseconds, std::nano); - CASE(Microseconds, std::micro); - CASE(Milliseconds, std::milli); - CASE(Seconds, std::ratio<1>); -#undef DO -#undef CASE - } -#endif -} void tst_QMutex::tryLock_non_recursive() { @@ -204,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(); @@ -230,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)); @@ -241,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(); @@ -252,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 @@ -301,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: @@ -331,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(); @@ -357,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)); @@ -368,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(); @@ -419,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: @@ -460,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(); @@ -486,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)); @@ -497,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(); @@ -548,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() @@ -586,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)); @@ -611,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)); @@ -684,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: @@ -713,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)); @@ -738,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)); @@ -807,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: @@ -841,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)); @@ -867,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)); @@ -936,7 +817,6 @@ void tst_QMutex::try_lock_until_recursive() testsTurn.acquire(); threadsTurn.release(); thread.wait(); -#endif } class mutex_Thread : public QThread @@ -1062,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 { @@ -1300,12 +1180,13 @@ QAtomicInt MoreStressTestThread::errorCount = 0; void tst_QMutex::moreStress() { - MoreStressTestThread threads[threadCount]; - for (int i = 0; i < threadCount; ++i) - threads[i].start(); + QVarLengthArray<MoreStressTestThread, threadCount> threads(qMin(QThread::idealThreadCount(), + int(threadCount))); + for (auto &thread : threads) + thread.start(); QVERIFY(threads[0].wait(one_minute + 10000)); - for (int i = 1; i < threadCount; ++i) - QVERIFY(threads[i].wait(10000)); + for (auto &thread : threads) + QVERIFY(thread.wait(10000)); qDebug("locked %d times", MoreStressTestThread::lockCount.loadRelaxed()); QCOMPARE(MoreStressTestThread::errorCount.loadRelaxed(), 0); } diff --git a/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt b/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt index b73d0d073d..7b2d6dc1c2 100644 --- a/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt +++ b/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qmutexlocker.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qmutexlocker Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qmutexlocker LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qmutexlocker SOURCES tst_qmutexlocker.cpp diff --git a/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp index 6227d62641..6ccab04c27 100644 --- a/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp +++ b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/corelib/thread/qpromise/CMakeLists.txt b/tests/auto/corelib/thread/qpromise/CMakeLists.txt index b841c7812b..c1ca30b34a 100644 --- a/tests/auto/corelib/thread/qpromise/CMakeLists.txt +++ b/tests/auto/corelib/thread/qpromise/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qpromise.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qpromise Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qpromise LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qpromise SOURCES tst_qpromise.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp index e87ba82309..2cef0dca95 100644 --- a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp +++ b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// 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 @@ -53,9 +53,9 @@ void snippet_QPromise::multithreadExample() QFuture<int> future = sharedPromise->future(); // ... -//! [multithread_init] sharedPromise->start(); +//! [multithread_init] //! [multithread_main] // here, QPromise is shared between threads via a smart pointer @@ -88,7 +88,9 @@ void snippet_QPromise::multithreadExample() for (auto& t : threads) t->wait(); +//! [multithread_cleanup] sharedPromise->finish(); +//! [multithread_cleanup] #endif } diff --git a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp index 5de8ce13ab..2c12e41c93 100644 --- a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp +++ b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCoreApplication> #include <QDebug> @@ -14,6 +14,8 @@ #include <memory> #include <chrono> +using namespace std::chrono_literals; + class tst_QPromise : public QObject { Q_OBJECT @@ -22,6 +24,7 @@ private slots: void promise(); void futureFromPromise(); void addResult(); + void addResultWithBracedInitializer(); void addResultOutOfOrder(); #ifndef QT_NO_EXCEPTIONS void setException(); @@ -40,6 +43,9 @@ private slots: #endif void cancelWhenReassigned(); void cancelWhenDestroyedWithoutStarting(); + void cancelWhenDestroyedRunsContinuations(); + void cancelWhenDestroyedWithFailureHandler(); // QTBUG-114606 + void continuationsRunWhenFinished(); void finishWhenSwapped(); void cancelWhenMoved(); void waitUntilResumed(); @@ -167,6 +173,15 @@ void tst_QPromise::addResult() QCOMPARE(f.resultCount(), 3); QCOMPARE(f.resultAt(2), result); } + // add multiple results in one go: + { + QList results = {42, 4242, 424242}; + QVERIFY(promise.addResults(results)); + QCOMPARE(f.resultCount(), 6); + QCOMPARE(f.resultAt(3), 42); + QCOMPARE(f.resultAt(4), 4242); + QCOMPARE(f.resultAt(5), 424242); + } // add as lvalue at position and overwrite { int result = -1; @@ -184,6 +199,28 @@ void tst_QPromise::addResult() } } +void tst_QPromise::addResultWithBracedInitializer() // QTBUG-111826 +{ + struct MyClass + { + QString strValue; + int intValue = 0; +#ifndef __cpp_aggregate_paren_init // make emplacement work with MyClass + MyClass(QString s, int i) : strValue(std::move(s)), intValue(i) {} +#endif + }; + + { + QPromise<MyClass> myPromise; + myPromise.addResult({"bar", 1}); + } + + { + QPromise<MyClass> myPromise; + myPromise.emplaceResult("bar", 1); + } +} + void tst_QPromise::addResultOutOfOrder() { // Compare results available in QFuture to expected results @@ -255,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 @@ -268,6 +309,8 @@ void tst_QPromise::cancel() testCancel(QPromise<void>()); testCancel(QPromise<int>()); + testCancel(QPromise<CopyOnlyType>()); + testCancel(QPromise<MoveOnlyType>()); } void tst_QPromise::progress() @@ -295,13 +338,13 @@ void tst_QPromise::progress() RUN_TEST_FUNC(testProgress, QPromise<void>()); RUN_TEST_FUNC(testProgress, QPromise<int>()); + RUN_TEST_FUNC(testProgress, QPromise<CopyOnlyType>()); + RUN_TEST_FUNC(testProgress, QPromise<MoveOnlyType>()); } void tst_QPromise::addInThread() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) const auto testAddResult = [] (auto promise, const auto &result) { promise.start(); auto f = promise.future(); @@ -322,9 +365,7 @@ void tst_QPromise::addInThread() void tst_QPromise::addInThreadMoveOnlyObject() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<MoveOnlyType> promise; promise.start(); auto f = promise.future(); @@ -341,9 +382,7 @@ void tst_QPromise::addInThreadMoveOnlyObject() void tst_QPromise::reportFromMultipleThreads() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> promise; auto f = promise.future(); promise.start(); @@ -367,9 +406,7 @@ void tst_QPromise::reportFromMultipleThreads() void tst_QPromise::reportFromMultipleThreadsByMovedPromise() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> initialPromise; auto f = initialPromise.future(); { @@ -401,9 +438,7 @@ void tst_QPromise::reportFromMultipleThreadsByMovedPromise() void tst_QPromise::doNotCancelWhenFinished() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) const auto testFinishedPromise = [] (auto promise) { auto f = promise.future(); promise.start(); @@ -420,15 +455,15 @@ void tst_QPromise::doNotCancelWhenFinished() RUN_TEST_FUNC(testFinishedPromise, QPromise<void>()); RUN_TEST_FUNC(testFinishedPromise, QPromise<int>()); RUN_TEST_FUNC(testFinishedPromise, QPromise<QString>()); + RUN_TEST_FUNC(testFinishedPromise, QPromise<CopyOnlyType>()); + RUN_TEST_FUNC(testFinishedPromise, QPromise<MoveOnlyType>()); #endif } #ifndef QT_NO_EXCEPTIONS void tst_QPromise::cancelWhenDestroyed() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> initialPromise; auto f = initialPromise.future(); @@ -462,15 +497,13 @@ void tst_QPromise::cancelWhenDestroyed() void tst_QPromise::cancelWhenReassigned() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> promise; auto f = promise.future(); promise.start(); ThreadWrapper thr([p = std::move(promise)] () mutable { - QThread::msleep(100); + QThread::sleep(100ms); p = QPromise<int>(); // assign new promise, old must be correctly destroyed }); @@ -481,11 +514,12 @@ void tst_QPromise::cancelWhenReassigned() #endif } -void tst_QPromise::cancelWhenDestroyedWithoutStarting() +template <typename T> +static inline void testCancelWhenDestroyedWithoutStarting() { - QFuture<void> future; + QFuture<T> future; { - QPromise<void> promise; + QPromise<T> promise; future = promise.future(); } future.waitForFinished(); @@ -494,11 +528,109 @@ void tst_QPromise::cancelWhenDestroyedWithoutStarting() QVERIFY(future.isFinished()); } -void tst_QPromise::finishWhenSwapped() +void tst_QPromise::cancelWhenDestroyedWithoutStarting() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); + testCancelWhenDestroyedWithoutStarting<void>(); + testCancelWhenDestroyedWithoutStarting<int>(); + testCancelWhenDestroyedWithoutStarting<CopyOnlyType>(); + testCancelWhenDestroyedWithoutStarting<MoveOnlyType>(); +} + +template <typename T> +static inline void testCancelWhenDestroyedRunsContinuations() +{ + QFuture<T> future; + bool onCanceledCalled = false; + bool thenCalled = false; + { + QPromise<T> promise; + future = promise.future(); + future.then([&] (auto&&) { + thenCalled = true; + }).onCanceled([&] () { + onCanceledCalled = true; + }); + } + QVERIFY(future.isFinished()); + QVERIFY(!thenCalled); + QVERIFY(onCanceledCalled); +} + +void tst_QPromise::cancelWhenDestroyedRunsContinuations() +{ + testCancelWhenDestroyedRunsContinuations<void>(); + testCancelWhenDestroyedRunsContinuations<int>(); + testCancelWhenDestroyedRunsContinuations<CopyOnlyType>(); + testCancelWhenDestroyedRunsContinuations<MoveOnlyType>(); +} + +template <typename T> +static inline void testCancelWhenDestroyedWithFailureHandler() +{ + QFuture<T> future; + bool onFailedCalled = false; + bool thenCalled = false; + { + QPromise<T> promise; + future = promise.future(); + future + .onFailed([&] () { + onFailedCalled = true; + if constexpr (!std::is_same_v<void, T>) + return T{}; + }) + .then([&] (auto&&) { + thenCalled = true; + }); + } + QVERIFY(future.isFinished()); + QVERIFY(!onFailedCalled); + QVERIFY(!thenCalled); +} + +void tst_QPromise::cancelWhenDestroyedWithFailureHandler() +{ +#ifndef QT_NO_EXCEPTIONS + testCancelWhenDestroyedWithFailureHandler<void>(); + testCancelWhenDestroyedWithFailureHandler<int>(); + testCancelWhenDestroyedWithFailureHandler<CopyOnlyType>(); + testCancelWhenDestroyedWithFailureHandler<MoveOnlyType>(); #else + QSKIP("Exceptions are disabled, skipping the test"); +#endif +} + +template <typename T> +static inline void testContinuationsRunWhenFinished() +{ + QPromise<T> promise; + QFuture<T> future = promise.future(); + + bool thenCalled = false; + future.then([&] (auto&&) { + thenCalled = true; + }); + + promise.start(); + if constexpr (!std::is_void_v<T>) { + promise.addResult(T{}); + } + promise.finish(); + + QVERIFY(thenCalled); +} + +void tst_QPromise::continuationsRunWhenFinished() +{ + testContinuationsRunWhenFinished<void>(); + testContinuationsRunWhenFinished<int>(); + testContinuationsRunWhenFinished<CopyOnlyType>(); + testContinuationsRunWhenFinished<MoveOnlyType>(); +} + +void tst_QPromise::finishWhenSwapped() +{ +#if QT_CONFIG(cxx11_future) QPromise<int> promise1; auto f1 = promise1.future(); promise1.start(); @@ -508,7 +640,7 @@ void tst_QPromise::finishWhenSwapped() promise2.start(); ThreadWrapper thr([&promise1, &promise2] () mutable { - QThread::msleep(100); + QThread::sleep(100ms); promise1.addResult(0); promise2.addResult(1); swap(promise1, promise2); // ADL must resolve this @@ -536,22 +668,21 @@ void tst_QPromise::finishWhenSwapped() #endif } -void tst_QPromise::cancelWhenMoved() +template <typename T> +void testCancelWhenMoved() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else - QPromise<int> promise1; +#if QT_CONFIG(cxx11_future) + QPromise<T> promise1; auto f1 = promise1.future(); promise1.start(); - QPromise<int> promise2; + QPromise<T> promise2; auto f2 = promise2.future(); promise2.start(); // Move promises to local scope to test cancellation behavior ThreadWrapper thr([p1 = std::move(promise1), p2 = std::move(promise2)] () mutable { - QThread::msleep(100); + QThread::sleep(100ms); p1 = std::move(p2); p1.finish(); // this finish is for future #2 }); @@ -569,6 +700,14 @@ void tst_QPromise::cancelWhenMoved() #endif } +void tst_QPromise::cancelWhenMoved() +{ + testCancelWhenMoved<void>(); + testCancelWhenMoved<int>(); + testCancelWhenMoved<CopyOnlyType>(); + testCancelWhenMoved<MoveOnlyType>(); +} + void tst_QPromise::waitUntilResumed() { #if !QT_CONFIG(cxx11_future) @@ -587,7 +726,7 @@ void tst_QPromise::waitUntilResumed() while (!f.isSuspended()) { // busy wait until worker thread suspends QCOMPARE(f.isFinished(), false); // exit condition in case of failure - QThread::msleep(50); // allow another thread to actually carry on + QThread::sleep(50ms); // allow another thread to actually carry on } f.resume(); @@ -600,9 +739,7 @@ void tst_QPromise::waitUntilResumed() void tst_QPromise::waitUntilCanceled() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else +#if QT_CONFIG(cxx11_future) QPromise<int> promise; promise.start(); auto f = promise.future(); @@ -616,7 +753,7 @@ void tst_QPromise::waitUntilCanceled() while (!f.isSuspended()) { // busy wait until worker thread suspends QCOMPARE(f.isFinished(), false); // exit condition in case of failure - QThread::msleep(50); // allow another thread to actually carry on + QThread::sleep(50ms); // allow another thread to actually carry on } f.cancel(); diff --git a/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt b/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt index da6091d588..7e80a4df81 100644 --- a/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt +++ b/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qreadlocker.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qreadlocker Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qreadlocker LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qreadlocker SOURCES tst_qreadlocker.cpp diff --git a/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp index 69cfc330b1..e21f80c05d 100644 --- a/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp +++ b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt b/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt index 8852a31fa4..5ed3012e04 100644 --- a/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt +++ b/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qreadwritelock.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qreadwritelock Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qreadwritelock LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qreadwritelock SOURCES tst_qreadwritelock.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::TestPrivate ) diff --git a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp index 1b3ab69197..86dfa5faff 100644 --- a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp +++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSemaphore> @@ -15,10 +15,6 @@ #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. @@ -30,6 +26,8 @@ #include <stdio.h> +using namespace std::chrono_literals; + class tst_QReadWriteLock : public QObject { Q_OBJECT @@ -472,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) @@ -489,9 +487,9 @@ public: while (t.elapsed()<runTime) { testRwlock.lockForRead(); if(print) printf("reading\n"); - if (holdTime) msleep(ulong(holdTime)); + if (holdTime > 0ms) sleep(holdTime); testRwlock.unlock(); - if (waitTime) msleep(ulong(waitTime)); + if (waitTime > 0ms) sleep(waitTime); } } }; @@ -508,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) @@ -525,9 +523,9 @@ public: while (t.elapsed() < runTime) { testRwlock.lockForWrite(); if (print) printf("."); - if (holdTime) msleep(ulong(holdTime)); + if (holdTime > 0ms) sleep(holdTime); testRwlock.unlock(); - if (waitTime) msleep(ulong(waitTime)); + if (waitTime > 0ms) sleep(waitTime); } } }; @@ -547,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) @@ -568,7 +566,7 @@ public: QtPrivate::volatilePreIncrement(count); count=0; testRwlock.unlock(); - msleep(ulong(waitTime)); + sleep(waitTime); } } }; @@ -585,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) @@ -600,7 +598,7 @@ public: if(count) qFatal("Non-zero count at Read! (%d)",count ); testRwlock.unlock(); - msleep(ulong(waitTime)); + sleep(waitTime); } } }; @@ -617,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); @@ -634,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); @@ -653,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(); diff --git a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt index ca30061d15..0f9d8d9e52 100644 --- a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt +++ b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qresultstore.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qresultstore Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qresultstore LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qresultstore SOURCES tst_qresultstore.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp index 902323a79c..265b2cd1f6 100644 --- a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp +++ b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -549,7 +549,7 @@ struct CountedObject const size_t previousLiveCount; }; - int id = 0; + size_t id = 0; static size_t liveCount; }; diff --git a/tests/auto/corelib/thread/qsemaphore/BLACKLIST b/tests/auto/corelib/thread/qsemaphore/BLACKLIST index f7f6d5149c..ecd42cff7c 100644 --- a/tests/auto/corelib/thread/qsemaphore/BLACKLIST +++ b/tests/auto/corelib/thread/qsemaphore/BLACKLIST @@ -1,8 +1,2 @@ [tryAcquireWithTimeout] -osx -[tryAcquireWithTimeout:0.2s] -windows -macos -[tryAcquireWithTimeout:2s] -windows macos diff --git a/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt b/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt index 1860347448..9f8a87558a 100644 --- a/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt +++ b/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qsemaphore.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qsemaphore Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsemaphore LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qsemaphore SOURCES tst_qsemaphore.cpp diff --git a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp index 21b37feef0..3bb1e1960c 100644 --- a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp +++ b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/corelib/thread/qthread/BLACKLIST b/tests/auto/corelib/thread/qthread/BLACKLIST index c683154da1..08e9912455 100644 --- a/tests/auto/corelib/thread/qthread/BLACKLIST +++ b/tests/auto/corelib/thread/qthread/BLACKLIST @@ -1,5 +1,3 @@ [wait3_slowDestructor] windows-10 -[sleep] -windows-7sp1 diff --git a/tests/auto/corelib/thread/qthread/CMakeLists.txt b/tests/auto/corelib/thread/qthread/CMakeLists.txt index 96c649f106..abcea1ef9c 100644 --- a/tests/auto/corelib/thread/qthread/CMakeLists.txt +++ b/tests/auto/corelib/thread/qthread/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qthread.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qthread Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qthread LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qthread SOURCES tst_qthread.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::TestPrivate ) diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index 125c969ea2..b163235d81 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QTestEventLoop> @@ -19,6 +19,8 @@ #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> @@ -36,6 +38,8 @@ #include <QtTest/private/qemulationdetector_p.h> +using namespace std::chrono_literals; + class tst_QThread : public QObject { Q_OBJECT @@ -49,6 +53,7 @@ private slots: void setStackSize(); void exit(); void start(); + void startSlotUsedInStringBasedLookups(); void terminate(); void quit(); void started(); @@ -68,6 +73,7 @@ private slots: void adoptedThreadExecFinished(); void adoptMultipleThreads(); void adoptMultipleThreadsOverlap(); + void adoptedThreadBindingStatus(); void exitAndStart(); void exitAndExec(); @@ -91,6 +97,11 @@ private slots: void create(); void createDestruction(); void threadIdReuse(); + + void terminateAndPrematureDestruction(); + void terminateAndDoubleDestruction(); + + void bindingListCleanupAfterDelete(); }; enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute }; @@ -126,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(); } }; @@ -237,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(); @@ -263,6 +278,11 @@ void tst_QThread::currentThreadId() QVERIFY(thread.wait(five_minutes)); QVERIFY(thread.id != nullptr); QVERIFY(thread.id != QThread::currentThreadId()); + QVERIFY(!thread.isCurrentThread()); + QVERIFY(!thread.thread->isCurrentThread()); + QVERIFY(thread.QThread::thread()->isCurrentThread()); + QVERIFY(thread.runCalledInCurrentThread); + QVERIFY(qApp->thread()->isCurrentThread()); } void tst_QThread::currentThread() @@ -454,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); @@ -525,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); @@ -930,6 +1008,20 @@ void tst_QThread::adoptMultipleThreadsOverlap() QCOMPARE(recorder.activationCount.loadRelaxed(), numThreads); } +void tst_QThread::adoptedThreadBindingStatus() +{ + NativeThreadWrapper nativeThread; + nativeThread.setWaitForStop(); + + nativeThread.startAndWait(); + QVERIFY(nativeThread.qthread); + auto privThread = static_cast<QThreadPrivate *>(QObjectPrivate::get(nativeThread.qthread)); + QVERIFY(privThread->m_statusOrPendingObjects.bindingStatus()); + + nativeThread.stop(); + nativeThread.join(); +} + // Disconnects on WinCE void tst_QThread::stressTest() { @@ -1195,9 +1287,10 @@ void tst_QThread::isRunningInFinished() } } -class DummyEventDispatcher : public QAbstractEventDispatcher { +class DummyEventDispatcher : public QAbstractEventDispatcherV2 +{ + Q_OBJECT public: - DummyEventDispatcher() : QAbstractEventDispatcher() {} bool processEvents(QEventLoop::ProcessEventsFlags) override { visited.storeRelaxed(true); emit awake(); @@ -1206,11 +1299,11 @@ public: } void registerSocketNotifier(QSocketNotifier *) override {} void unregisterSocketNotifier(QSocketNotifier *) override {} - void registerTimer(int, qint64, Qt::TimerType, QObject *) override {} - bool unregisterTimer(int) override { return false; } + void registerTimer(Qt::TimerId, Duration, Qt::TimerType, QObject *) override {} + bool unregisterTimer(Qt::TimerId) override { return false; } bool unregisterTimers(QObject *) override { return false; } - QList<TimerInfo> registeredTimers(QObject *) const override { return QList<TimerInfo>(); } - int remainingTime(int) override { return 0; } + QList<TimerInfoV2> timersForObject(QObject *) const override { return {}; } + Duration remainingTime(Qt::TimerId) const override { return 0s; } void wakeUp() override {} void interrupt() override {} @@ -1321,9 +1414,6 @@ void tst_QThread::quitLock() void tst_QThread::create() { -#if !QT_CONFIG(cxx11_future) - QSKIP("This test requires QThread::create"); -#else { const auto &function = [](){}; QScopedPointer<QThread> thread(QThread::create(function)); @@ -1563,7 +1653,6 @@ void tst_QThread::create() QVERIFY(!thread); } #endif // QT_NO_EXCEPTIONS -#endif // QT_CONFIG(cxx11_future) } void tst_QThread::createDestruction() @@ -1573,7 +1662,7 @@ void tst_QThread::createDestruction() for (;;) { if (QThread::currentThread()->isInterruptionRequested()) return; - QThread::msleep(1); + QThread::sleep(1ms); } }; @@ -1692,7 +1781,7 @@ void tst_QThread::threadIdReuse() bool threadIdReused = false; for (int i = 0; i < 42; i++) { - QThread::msleep(1); + QThread::sleep(1ms); Qt::HANDLE threadId2; bool waitOk = false; @@ -1719,5 +1808,100 @@ void tst_QThread::threadIdReuse() } } +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 18ec7723f2..2c92ca002e 100644 --- a/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt +++ b/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qthreadonce.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qthreadonce Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qthreadonce LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qthreadonce SOURCES qthreadonce.cpp diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp index a712903da8..b32f455241 100644 --- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp +++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// 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 05c3788fd9..1f804433e4 100644 --- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.h +++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QTHREADONCE_H diff --git a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp index c91d2bd110..37e1f744f3 100644 --- a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp +++ b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> diff --git a/tests/auto/corelib/thread/qthreadpool/BLACKLIST b/tests/auto/corelib/thread/qthreadpool/BLACKLIST deleted file mode 100644 index b8c1f3bf3f..0000000000 --- a/tests/auto/corelib/thread/qthreadpool/BLACKLIST +++ /dev/null @@ -1,4 +0,0 @@ -[expiryTimeoutRace] -opensuse-leap -ubuntu -rhel diff --git a/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt b/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt index ebf9fc104f..fee9c541db 100644 --- a/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt +++ b/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qthreadpool.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qthreadpool Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qthreadpool LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qthreadpool SOURCES tst_qthreadpool.cpp diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp index d263c5013a..2006016d47 100644 --- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp +++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp @@ -1,11 +1,12 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// 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> @@ -14,6 +15,8 @@ #include <unistd.h> #endif +using namespace std::chrono_literals; + typedef void (*FunctionPointer)(); class FunctionPointerTask : public QRunnable @@ -43,6 +46,7 @@ public: private slots: void runFunction(); void runFunction2(); + void runFunction3(); void createThreadRunFunction(); void runMultiple(); void waitcomplete(); @@ -83,6 +87,7 @@ private slots: void takeAllAndIncreaseMaxThreadCount(); void waitForDoneAfterTake(); void threadReuse(); + void nullFunctions(); private: QMutex m_functionTestMutex; @@ -136,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); } @@ -150,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); } @@ -172,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); @@ -181,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); @@ -190,7 +227,7 @@ void tst_QThreadPool::runMultiple() QCOMPARE(testFunctionCount, runs); { - QThreadPool manager; + TestThreadPool manager; for (int i = 0; i < 500; ++i) manager.start(emptyFunct); } @@ -201,6 +238,7 @@ void tst_QThreadPool::waitcomplete() testFunctionCount = 0; const int runs = 500; for (int i = 0; i < 500; ++i) { + // TestThreadPool pool; // no, we're checking ~QThreadPool()'s waitForDone() QThreadPool pool; pool.start(noSleepTestFunction); } @@ -219,7 +257,7 @@ public: void tst_QThreadPool::runTask() { - QThreadPool manager; + TestThreadPool manager; ran.storeRelaxed(false); manager.start(new TestTask()); QTRY_VERIFY(ran.loadRelaxed()); @@ -281,7 +319,7 @@ public: */ void tst_QThreadPool::threadRecycling() { - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.start(new ThreadRecorderTask()); threadRecyclingSemaphore.acquire(); @@ -309,7 +347,7 @@ void tst_QThreadPool::threadRecycling() void tst_QThreadPool::threadPriority() { QThread::Priority priority = QThread::HighPriority; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setThreadPriority(priority); threadPool.start(new ThreadRecorderTask()); @@ -346,7 +384,7 @@ void tst_QThreadPool::expiryTimeout() { ExpiryTimeoutTask task; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(1); int expiryTimeout = threadPool.expiryTimeout(); @@ -380,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); @@ -412,7 +447,7 @@ void tst_QThreadPool::exceptions() { ExceptionTask task; { - QThreadPool threadPool; + TestThreadPool threadPool; // Uncomment this for a nice crash. // threadPool.start(&task); } @@ -456,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 @@ -486,7 +521,7 @@ void tst_QThreadPool::setMaxThreadCountStartsAndStopsThreads() } }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(-1); // docs say we'll always start at least one WaitingTask task; @@ -574,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 @@ -638,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 @@ -839,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()); } @@ -866,13 +901,13 @@ void tst_QThreadPool::tryStart() count.storeRelaxed(0); WaitingTask task; - QThreadPool threadPool; + TestThreadPool threadPool; for (int i = 0; i < threadPool.maxThreadCount(); ++i) { threadPool.start(&task); } QVERIFY(!threadPool.tryStart(&task)); task.semaphore.release(threadPool.maxThreadCount()); - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount()); } @@ -903,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) @@ -932,7 +967,7 @@ void tst_QThreadPool::tryStartCount() }; SleeperTask task; - QThreadPool threadPool; + TestThreadPool threadPool; const int runs = 5; for (int i = 0; i < runs; ++i) { @@ -980,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 @@ -992,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); } @@ -1002,7 +1037,7 @@ void tst_QThreadPool::waitForDone() total.start(); pass.start(); - QThreadPool threadPool; + TestThreadPool threadPool; while (total.elapsed() < 10000) { int runs; count.storeRelaxed(runs = 0); @@ -1011,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); @@ -1021,7 +1056,7 @@ void tst_QThreadPool::waitForDone() threadPool.start(new CountingRunnable()); runs += 2; } - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(count.loadRelaxed(), runs); } } @@ -1043,7 +1078,7 @@ void tst_QThreadPool::waitForDoneTimeout() } }; - QThreadPool threadPool; + TestThreadPool threadPool; mutex.lock(); threadPool.start(new BlockedTask(mutex)); @@ -1067,7 +1102,7 @@ void tst_QThreadPool::clear() } }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(10); int runs = 2 * threadPool.maxThreadCount(); count.storeRelaxed(0); @@ -1076,7 +1111,7 @@ void tst_QThreadPool::clear() } threadPool.clear(); sem.release(threadPool.maxThreadCount()); - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount()); } @@ -1086,10 +1121,10 @@ void tst_QThreadPool::clearWithAutoDelete() { public: MyRunnable() {} - void run() override { QThread::usleep(30); } + void run() override { QThread::sleep(30us); } }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(4); const int loopCount = 20; const int batchSize = 500; @@ -1102,7 +1137,6 @@ void tst_QThreadPool::clearWithAutoDelete() threadPool.start(runnable); } } - QVERIFY(threadPool.waitForDone()); } void tst_QThreadPool::tryTake() @@ -1142,7 +1176,7 @@ void tst_QThreadPool::tryTake() Runs = MaxThreadCount * OverProvisioning }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(MaxThreadCount); BlockingRunnable *runnables[Runs]; @@ -1174,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)); @@ -1191,7 +1225,7 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish() int runs; count.storeRelaxed(runs = 0); { - QThreadPool threadPool; + TestThreadPool threadPool; pass.restart(); while (pass.elapsed() < 100) { threadPool.start(new CountingRunnable()); @@ -1202,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()); @@ -1244,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); } @@ -1308,7 +1342,7 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() { QSemaphore mainBarrier; QSemaphore taskBarrier; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(1); Task task1(&mainBarrier, &taskBarrier); @@ -1339,7 +1373,7 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() { taskBarrier.release(1); - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(threadPool.activeThreadCount(), 0); } @@ -1372,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 @@ -1403,12 +1437,6 @@ 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."); - } /* @@ -1418,7 +1446,7 @@ void tst_QThreadPool::waitForDoneAfterTake() */ void tst_QThreadPool::threadReuse() { - QThreadPool manager; + TestThreadPool manager; manager.setExpiryTimeout(-1); manager.setMaxThreadCount(1); @@ -1435,5 +1463,30 @@ void tst_QThreadPool::threadReuse() } } +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); #include "tst_qthreadpool.moc" diff --git a/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt index 26a691e8ca..14d8d7dd40 100644 --- a/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt +++ b/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt @@ -1,7 +1,16 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + ##################################################################### ## tst_qthreadstorage Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qthreadstorage LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qthreadstorage SOURCES tst_qthreadstorage.cpp diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt index c76dbda738..4d62e61a36 100644 --- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt +++ b/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + ##################################################################### ## crashonexit Binary: ##################################################################### diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp index cc730cd4a1..3b3a4d4813 100644 --- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp +++ b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> #include <QtCore/QThreadStorage> diff --git a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp index b3debebdc1..ca382cf60c 100644 --- a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp +++ b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #if QT_CONFIG(process) diff --git a/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt b/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt index e600ff3a42..0a2830622e 100644 --- a/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt +++ b/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qwaitcondition.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwaitcondition Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwaitcondition LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwaitcondition SOURCES tst_qwaitcondition.cpp diff --git a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp index 46584d0a50..4e3413afe8 100644 --- a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp +++ b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QReadWriteLock> @@ -372,7 +372,7 @@ public: { } static inline void sleep(ulong s) - { QThread::sleep(s); } + { QThread::sleep(std::chrono::seconds{s}); } void run() override { @@ -404,7 +404,7 @@ public: { } static inline void sleep(ulong s) - { QThread::sleep(s); } + { QThread::sleep(std::chrono::seconds{s}); } void run() override { diff --git a/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt b/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt index 200fa2ee3c..5345522ea5 100644 --- a/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt +++ b/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qwritelocker.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qwritelocker Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qwritelocker LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qwritelocker SOURCES tst_qwritelocker.cpp diff --git a/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp index fd8221d609..b4e6b45dbd 100644 --- a/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp +++ b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> |