summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/thread
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/thread')
-rw-r--r--tests/auto/corelib/thread/CMakeLists.txt49
-rw-r--r--tests/auto/corelib/thread/qatomicint/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qatomicint/qatomicint.pro4
-rw-r--r--tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp141
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt19
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char/char.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char16_t/char16_t.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/char32_t/char32_t.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/int/int.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/long/long.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pri11
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pro18
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qlonglong/qlonglong.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qptrdiff/qptrdiff.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/quintptr/quintptr.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/qulonglong/qulonglong.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/schar/schar.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/short/short.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp405
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uchar/uchar.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/uint/uint.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ulong/ulong.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/ushort/ushort.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qatomicinteger/wchar_t/wchar_t.pro1
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro4
-rw-r--r--tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp231
-rw-r--r--tests/auto/corelib/thread/qfuture/CMakeLists.txt27
-rw-r--r--tests/auto/corelib/thread/qfuture/qfuture.pro5
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp4125
-rw-r--r--tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qfuturesynchronizer/qfuturesynchronizer.pro4
-rw-r--r--tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp69
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/qfuturewatcher.pro4
-rw-r--r--tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp513
-rw-r--r--tests/auto/corelib/thread/qmutex/CMakeLists.txt19
-rw-r--r--tests/auto/corelib/thread/qmutex/qmutex.pro5
-rw-r--r--tests/auto/corelib/thread/qmutex/tst_qmutex.cpp334
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro4
-rw-r--r--tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp131
-rw-r--r--tests/auto/corelib/thread/qpromise/CMakeLists.txt19
-rw-r--r--tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp158
-rw-r--r--tests/auto/corelib/thread/qpromise/tst_qpromise.cpp784
-rw-r--r--tests/auto/corelib/thread/qreadlocker/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qreadlocker/qreadlocker.pro4
-rw-r--r--tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp45
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro4
-rw-r--r--tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp344
-rw-r--r--tests/auto/corelib/thread/qresultstore/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qresultstore/qresultstore.pro5
-rw-r--r--tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp249
-rw-r--r--tests/auto/corelib/thread/qsemaphore/BLACKLIST10
-rw-r--r--tests/auto/corelib/thread/qsemaphore/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qsemaphore/qsemaphore.pro4
-rw-r--r--tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp121
-rw-r--r--tests/auto/corelib/thread/qthread/BLACKLIST3
-rw-r--r--tests/auto/corelib/thread/qthread/CMakeLists.txt22
-rw-r--r--tests/auto/corelib/thread/qthread/qthread.pro9
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp644
-rw-r--r--tests/auto/corelib/thread/qthreadonce/CMakeLists.txt20
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp31
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.h29
-rw-r--r--tests/auto/corelib/thread/qthreadonce/qthreadonce.pro12
-rw-r--r--tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp55
-rw-r--r--tests/auto/corelib/thread/qthreadpool/BLACKLIST3
-rw-r--r--tests/auto/corelib/thread/qthreadpool/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qthreadpool/qthreadpool.pro4
-rw-r--r--tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp707
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt27
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt13
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp29
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro16
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro8
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/test/test.pro16
-rw-r--r--tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp99
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/BLACKLIST2
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro4
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp96
-rw-r--r--tests/auto/corelib/thread/qwritelocker/CMakeLists.txt17
-rw-r--r--tests/auto/corelib/thread/qwritelocker/qwritelocker.pro4
-rw-r--r--tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp45
-rw-r--r--tests/auto/corelib/thread/thread.pro27
102 files changed, 8046 insertions, 2297 deletions
diff --git a/tests/auto/corelib/thread/CMakeLists.txt b/tests/auto/corelib/thread/CMakeLists.txt
new file mode 100644
index 0000000000..d25d0205f5
--- /dev/null
+++ b/tests/auto/corelib/thread/CMakeLists.txt
@@ -0,0 +1,49 @@
+# 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)
+ if(QT_FEATURE_future)
+ if(QT_FEATURE_concurrent AND NOT INTEGRITY)
+ add_subdirectory(qfuture)
+ endif()
+ add_subdirectory(qresultstore)
+ add_subdirectory(qfuturesynchronizer)
+ if(NOT INTEGRITY)
+ add_subdirectory(qpromise)
+ endif()
+ # QTBUG-87431
+ if(TARGET Qt::Concurrent AND NOT INTEGRITY)
+ add_subdirectory(qfuturewatcher)
+ endif()
+ endif()
+ add_subdirectory(qmutex)
+ add_subdirectory(qmutexlocker)
+ add_subdirectory(qreadlocker)
+ add_subdirectory(qreadwritelock)
+ add_subdirectory(qsemaphore)
+ # QTBUG-85364
+ if(NOT CMAKE_CROSSCOMPILING)
+ add_subdirectory(qthread)
+ endif()
+ add_subdirectory(qthreadonce)
+ add_subdirectory(qthreadpool)
+ add_subdirectory(qthreadstorage)
+ add_subdirectory(qwaitcondition)
+ add_subdirectory(qwritelocker)
+endif()
+
diff --git a/tests/auto/corelib/thread/qatomicint/CMakeLists.txt b/tests/auto/corelib/thread/qatomicint/CMakeLists.txt
new file mode 100644
index 0000000000..239f3cce87
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicint/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qatomicint.pro b/tests/auto/corelib/thread/qatomicint/qatomicint.pro
deleted file mode 100644
index 89ac465e81..0000000000
--- a/tests/auto/corelib/thread/qatomicint/qatomicint.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qatomicint
-QT = core testlib
-SOURCES = tst_qatomicint.cpp
diff --git a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
index cc197cabba..63cb494c11 100644
--- a/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
+++ b/tests/auto/corelib/thread/qatomicint/tst_qatomicint.cpp
@@ -1,36 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QAtomicInt>
#include <QCoreApplication>
+#include <QElapsedTimer>
#include <limits.h>
@@ -84,7 +60,7 @@ private:
};
template <int I>
-static inline void assemblyMarker(void *ptr = 0)
+static inline void assemblyMarker(void *ptr = nullptr)
{
puts((char *)ptr + I);
}
@@ -104,12 +80,12 @@ static void warningFreeHelperTemplate()
assemblyMarker<1>(&i);
// the loads sometimes generate no assembly output
- i.load();
+ i.loadRelaxed();
assemblyMarker<11>(&i);
i.loadAcquire();
assemblyMarker<12>(&i);
- i.store(newValue);
+ i.storeRelaxed(newValue);
assemblyMarker<21>(&i);
i.storeRelease(newValue);
assemblyMarker<22>(&i);
@@ -153,17 +129,11 @@ template <bool> inline void booleanHelper()
template <typename Atomic>
static void constexprFunctionsHelperTemplate()
{
-#ifdef Q_COMPILER_CONSTEXPR
// this is a compile-time test only
- booleanHelper<Atomic::isReferenceCountingNative()>();
booleanHelper<Atomic::isReferenceCountingWaitFree()>();
- booleanHelper<Atomic::isTestAndSetNative()>();
booleanHelper<Atomic::isTestAndSetWaitFree()>();
- booleanHelper<Atomic::isFetchAndStoreNative()>();
booleanHelper<Atomic::isFetchAndStoreWaitFree()>();
- booleanHelper<Atomic::isFetchAndAddNative()>();
booleanHelper<Atomic::isFetchAndAddWaitFree()>();
-#endif
}
void tst_QAtomicInt::warningFreeHelper()
@@ -176,10 +146,8 @@ void tst_QAtomicInt::warningFreeHelper()
warningFreeHelperTemplate<unsigned int, QBasicAtomicInteger<unsigned int> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<int> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned int> >();
-# ifdef Q_COMPILER_UNICODE_STRINGS
warningFreeHelperTemplate<qint16, QBasicAtomicInteger<char32_t> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<char32_t> >();
-# endif
// pointer-sized integers are always supported:
warningFreeHelperTemplate<int, QBasicAtomicInteger<qptrdiff> >();
@@ -193,25 +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> >();
-# ifdef Q_COMPILER_UNICODE_STRINGS
warningFreeHelperTemplate<qint16, QBasicAtomicInteger<char16_t> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<char16_t> >();
-# endif
-#endif
-#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
warningFreeHelperTemplate<char, QBasicAtomicInteger<char> >();
warningFreeHelperTemplate<signed char, QBasicAtomicInteger<signed char> >();
warningFreeHelperTemplate<unsigned char, QBasicAtomicInteger<unsigned char> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<char> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<signed char> >();
constexprFunctionsHelperTemplate<QBasicAtomicInteger<unsigned char> >();
-#endif
#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
#if !defined(__i386__) || (defined(Q_CC_GNU) && defined(__OPTIMIZE__))
@@ -236,28 +198,16 @@ template <typename T> struct TypeInStruct { T type; };
void tst_QAtomicInt::alignment()
{
-#ifdef Q_ALIGNOF
- // this will cause a build error if the alignment isn't the same
- char dummy1[Q_ALIGNOF(QBasicAtomicInt) == Q_ALIGNOF(TypeInStruct<int>) ? 1 : -1];
- char dummy2[Q_ALIGNOF(QAtomicInt) == Q_ALIGNOF(TypeInStruct<int>) ? 1 : -1];
- (void)dummy1; (void)dummy2;
+ static_assert(alignof(QBasicAtomicInt) == alignof(TypeInStruct<int>));
+ static_assert(alignof(QBasicAtomicInt) == alignof(TypeInStruct<int>));
-#ifdef Q_ATOMIC_INT32_IS_SUPPORTED
- QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<int>), Q_ALIGNOF(TypeInStruct<int>));
-#endif
-
-#ifdef Q_ATOMIC_INT16_IS_SUPPORTED
- QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<short>), Q_ALIGNOF(TypeInStruct<short>));
-#endif
-
-#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
- QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<char>), Q_ALIGNOF(TypeInStruct<char>));
-#endif
-
-#ifdef Q_ATOMIC_INT64_IS_SUPPORTED
- QCOMPARE(Q_ALIGNOF(QBasicAtomicInteger<qlonglong>), Q_ALIGNOF(TypeInStruct<qlonglong>));
-#endif
+ QCOMPARE(alignof(QBasicAtomicInteger<int>), alignof(TypeInStruct<int>));
+ QCOMPARE(alignof(QBasicAtomicInteger<short>), alignof(TypeInStruct<short>));
+ QCOMPARE(alignof(QBasicAtomicInteger<char>), alignof(TypeInStruct<char>));
+#if !defined(Q_PROCESSOR_X86_32) && defined(Q_ATOMIC_INT64_IS_SUPPORTED)
+ // The alignment is different on x86_32
+ QCOMPARE(alignof(QBasicAtomicInteger<qlonglong>), alignof(TypeInStruct<qlonglong>));
#endif
}
@@ -281,9 +231,9 @@ void tst_QAtomicInt::constructor()
{
QFETCH(int, value);
QAtomicInt atomic1(value);
- QCOMPARE(atomic1.load(), value);
+ QCOMPARE(atomic1.loadRelaxed(), value);
QAtomicInt atomic2 = value;
- QCOMPARE(atomic2.load(), value);
+ QCOMPARE(atomic2.loadRelaxed(), value);
}
void tst_QAtomicInt::copy_constructor_data()
@@ -293,16 +243,16 @@ void tst_QAtomicInt::copy_constructor()
{
QFETCH(int, value);
QAtomicInt atomic1(value);
- QCOMPARE(atomic1.load(), value);
+ QCOMPARE(atomic1.loadRelaxed(), value);
QAtomicInt atomic2(atomic1);
- QCOMPARE(atomic2.load(), value);
+ QCOMPARE(atomic2.loadRelaxed(), value);
QAtomicInt atomic3 = atomic1;
- QCOMPARE(atomic3.load(), value);
+ QCOMPARE(atomic3.loadRelaxed(), value);
QAtomicInt atomic4(atomic2);
- QCOMPARE(atomic4.load(), value);
+ QCOMPARE(atomic4.loadRelaxed(), value);
QAtomicInt atomic5 = atomic2;
- QCOMPARE(atomic5.load(), value);
+ QCOMPARE(atomic5.loadRelaxed(), value);
}
void tst_QAtomicInt::assignment_operator_data()
@@ -326,13 +276,13 @@ void tst_QAtomicInt::assignment_operator()
{
QAtomicInt atomic1 = value;
atomic1 = newval;
- QCOMPARE(atomic1.load(), newval);
+ QCOMPARE(atomic1.loadRelaxed(), newval);
atomic1 = value;
- QCOMPARE(atomic1.load(), value);
+ QCOMPARE(atomic1.loadRelaxed(), value);
QAtomicInt atomic2 = newval;
atomic1 = atomic2;
- QCOMPARE(atomic1.load(), atomic2.load());
+ QCOMPARE(atomic1.loadRelaxed(), atomic2.loadRelaxed());
}
}
@@ -400,7 +350,7 @@ void tst_QAtomicInt::ref()
QFETCH(int, value);
QAtomicInt x = value;
QTEST(x.ref() ? 1 : 0, "result");
- QTEST(x.load(), "expected");
+ QTEST(x.loadRelaxed(), "expected");
}
void tst_QAtomicInt::deref_data()
@@ -419,7 +369,7 @@ void tst_QAtomicInt::deref()
QFETCH(int, value);
QAtomicInt x = value;
QTEST(x.deref() ? 1 : 0, "result");
- QTEST(x.load(), "expected");
+ QTEST(x.loadRelaxed(), "expected");
}
void tst_QAtomicInt::isTestAndSetNative()
@@ -531,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
@@ -566,7 +515,6 @@ void tst_QAtomicInt::testAndSet()
if (!result)
QCOMPARE(currentval, value);
}
-#endif
}
void tst_QAtomicInt::isFetchAndStoreNative()
@@ -635,25 +583,25 @@ void tst_QAtomicInt::fetchAndStore()
{
QAtomicInt atomic = value;
QCOMPARE(atomic.fetchAndStoreRelaxed(newval), value);
- QCOMPARE(atomic.load(), newval);
+ QCOMPARE(atomic.loadRelaxed(), newval);
}
{
QAtomicInt atomic = value;
QCOMPARE(atomic.fetchAndStoreAcquire(newval), value);
- QCOMPARE(atomic.load(), newval);
+ QCOMPARE(atomic.loadRelaxed(), newval);
}
{
QAtomicInt atomic = value;
QCOMPARE(atomic.fetchAndStoreRelease(newval), value);
- QCOMPARE(atomic.load(), newval);
+ QCOMPARE(atomic.loadRelaxed(), newval);
}
{
QAtomicInt atomic = value;
QCOMPARE(atomic.fetchAndStoreOrdered(newval), value);
- QCOMPARE(atomic.load(), newval);
+ QCOMPARE(atomic.loadRelaxed(), newval);
}
}
@@ -728,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;
@@ -744,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;
@@ -772,28 +718,28 @@ void tst_QAtomicInt::fetchAndAdd()
QAtomicInt atomic = value1;
result = atomic.fetchAndAddRelaxed(value2);
QCOMPARE(result, value1);
- QCOMPARE(atomic.load(), value1 + value2);
+ QCOMPARE(atomic.loadRelaxed(), value1 + value2);
}
{
QAtomicInt atomic = value1;
result = atomic.fetchAndAddAcquire(value2);
QCOMPARE(result, value1);
- QCOMPARE(atomic.load(), value1 + value2);
+ QCOMPARE(atomic.loadRelaxed(), value1 + value2);
}
{
QAtomicInt atomic = value1;
result = atomic.fetchAndAddRelease(value2);
QCOMPARE(result, value1);
- QCOMPARE(atomic.load(), value1 + value2);
+ QCOMPARE(atomic.loadRelaxed(), value1 + value2);
}
{
QAtomicInt atomic = value1;
result = atomic.fetchAndAddOrdered(value2);
QCOMPARE(result, value1);
- QCOMPARE(atomic.load(), value1 + value2);
+ QCOMPARE(atomic.loadRelaxed(), value1 + value2);
}
}
@@ -844,21 +790,24 @@ void tst_QAtomicInt::operators()
QCOMPARE(int(atomic), x);
QCOMPARE(int(atomic), 0x13);
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_CLANG("-Wself-assign-overloaded")
x = (atomic ^= atomic);
QCOMPARE(int(atomic), x);
QCOMPARE(int(atomic), 0);
+QT_WARNING_POP
}
void tst_QAtomicInt::testAndSet_loop()
{
- QTime stopWatch;
+ QElapsedTimer stopWatch;
stopWatch.start();
int iterations = 10000000;
QAtomicInt val=0;
for (int i = 0; i < iterations; ++i) {
- int v = val.load();
+ int v = val.loadRelaxed();
QVERIFY(val.testAndSetRelaxed(v, v+1));
if ((i % 1000) == 999) {
if (stopWatch.elapsed() > 60 * 1000) {
@@ -881,14 +830,14 @@ void tst_QAtomicInt::fetchAndAdd_loop()
QAtomicInt val=0;
for (int i = 0; i < iterations; ++i) {
const int prev = val.fetchAndAddRelaxed(1);
- QCOMPARE(prev, val.load() -1);
+ QCOMPARE(prev, val.loadRelaxed() -1);
}
}
class FetchAndAddThread : public QThread
{
public:
- void run()
+ void run() override
{
for (int i = 0; i < iterations; ++i)
@@ -919,7 +868,7 @@ void tst_QAtomicInt::fetchAndAdd_threadedLoop()
t1.wait();
t2.wait();
- QCOMPARE(val.load(), 0);
+ QCOMPARE(val.loadRelaxed(), 0);
}
QTEST_MAIN(tst_QAtomicInt)
diff --git a/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt
new file mode 100644
index 0000000000..03a6323a1f
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+add_subdirectory(char)
+add_subdirectory(char16_t)
+add_subdirectory(char32_t)
+add_subdirectory(int)
+add_subdirectory(long)
+add_subdirectory(qlonglong)
+add_subdirectory(qptrdiff)
+add_subdirectory(quintptr)
+add_subdirectory(qulonglong)
+add_subdirectory(schar)
+add_subdirectory(short)
+add_subdirectory(uchar)
+add_subdirectory(uint)
+add_subdirectory(ulong)
+add_subdirectory(ushort)
+add_subdirectory(wchar_t)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
new file mode 100644
index 0000000000..882a9298f6
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/char/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=char
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_char
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char/char.pro b/tests/auto/corelib/thread/qatomicinteger/char/char.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/char/char.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt
new file mode 100644
index 0000000000..8e53b59689
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/char16_t/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=char16_t
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_char16_t
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char16_t/char16_t.pro b/tests/auto/corelib/thread/qatomicinteger/char16_t/char16_t.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/char16_t/char16_t.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt
new file mode 100644
index 0000000000..5881d475f4
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/char32_t/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=char32_t
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_char32_t
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/char32_t/char32_t.pro b/tests/auto/corelib/thread/qatomicinteger/char32_t/char32_t.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/char32_t/char32_t.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
new file mode 100644
index 0000000000..0915e77a8d
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/int/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=int
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_int
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/int/int.pro b/tests/auto/corelib/thread/qatomicinteger/int/int.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/int/int.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
new file mode 100644
index 0000000000..adf6638bfa
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/long/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=long
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_long
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/long/long.pro b/tests/auto/corelib/thread/qatomicinteger/long/long.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/long/long.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pri b/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pri
deleted file mode 100644
index f1030d41ef..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pri
+++ /dev/null
@@ -1,11 +0,0 @@
-# Get our build type from the directory name
-TYPE = $$basename(_PRO_FILE_PWD_)
-dn = $$dirname(_PRO_FILE_PWD_)
-FORCE = $$basename(dn)
-suffix = $$TYPE
-
-CONFIG += testcase
-QT = core testlib
-TARGET = tst_qatomicinteger_$$lower($$suffix)
-SOURCES = $$PWD/tst_qatomicinteger.cpp
-DEFINES += QATOMIC_TEST_TYPE=$$TYPE tst_QAtomicIntegerXX=tst_QAtomicInteger_$$suffix
diff --git a/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pro b/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pro
deleted file mode 100644
index 09458bd9c3..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qatomicinteger.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-TEMPLATE=subdirs
-SUBDIRS=\
- char \
- char16_t \
- char32_t \
- int \
- long \
- qlonglong \
- qptrdiff \
- quintptr \
- qulonglong \
- schar \
- short \
- uchar \
- uint \
- ulong \
- ushort \
- wchar_t \
diff --git a/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt
new file mode 100644
index 0000000000..2ec977d7cb
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/qlonglong/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=qlonglong
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_qlonglong
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qlonglong/qlonglong.pro b/tests/auto/corelib/thread/qatomicinteger/qlonglong/qlonglong.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qlonglong/qlonglong.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt
new file mode 100644
index 0000000000..a2450931d5
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=qptrdiff
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_qptrdiff
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/qptrdiff.pro b/tests/auto/corelib/thread/qatomicinteger/qptrdiff/qptrdiff.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qptrdiff/qptrdiff.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt
new file mode 100644
index 0000000000..98302b5d07
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/quintptr/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=quintptr
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_quintptr
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/quintptr/quintptr.pro b/tests/auto/corelib/thread/qatomicinteger/quintptr/quintptr.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/quintptr/quintptr.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt
new file mode 100644
index 0000000000..13acfc3e2b
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/qulonglong/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=qulonglong
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_qulonglong
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/qulonglong/qulonglong.pro b/tests/auto/corelib/thread/qatomicinteger/qulonglong/qulonglong.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/qulonglong/qulonglong.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt
new file mode 100644
index 0000000000..127f752cc2
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/schar/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=schar
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_schar
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/schar/schar.pro b/tests/auto/corelib/thread/qatomicinteger/schar/schar.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/schar/schar.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt
new file mode 100644
index 0000000000..df9d2af4c3
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/short/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=short
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_short
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/short/short.pro b/tests/auto/corelib/thread/qatomicinteger/short/short.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/short/short.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
index 32e5b8ee56..d1a8a8f729 100644
--- a/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
+++ b/tests/auto/corelib/thread/qatomicinteger/tst_qatomicinteger.cpp
@@ -1,62 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifdef QT_ATOMIC_FORCE_CXX11
-// We need to check if this compiler has C++11 atomics and constexpr support.
-// We can't rely on qcompilerdetection.h because it forces all of qglobal.h to
-// be included, which causes qbasicatomic.h to be included too.
-// Incomplete, but ok
-# if defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1500 && (__cplusplus >= 201103L || defined(__INTEL_CXX11_MODE__))
-# elif defined(__clang__) && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
-# if !__has_feature(cxx_constexpr) || !__has_feature(cxx_atomic) || !__has_include(<atomic>)
-# undef QT_ATOMIC_FORCE_CXX11
-# endif
-# elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && (__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
-# elif defined(_MSC_VER)
- // We need MSVC 2015 because of: atomics (2012), constexpr (2015), and unrestricted unions (2015).
- // Support for constexpr is not working completely on MSVC 2015 but it's enough for the test.
-# else
-# undef QT_ATOMIC_FORCE_CXX11
-# endif
-
-# ifndef QT_ATOMIC_FORCE_CXX11
-# undef QATOMIC_TEST_TYPE
-# define QATOMIC_TEST_TYPE unsupported
-# endif
-#endif
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include <QtTest>
+#include <QTest>
#include <QAtomicInt>
#include <limits>
#include <limits.h>
#include <wchar.h>
+#if !defined(Q_ATOMIC_INT8_IS_SUPPORTED)
+# error "QAtomicInteger for 8-bit types must be supported!"
+#endif
+#if !defined(Q_ATOMIC_INT16_IS_SUPPORTED)
+# error "QAtomicInteger for 16-bit types must be supported!"
+#endif
#if !defined(Q_ATOMIC_INT32_IS_SUPPORTED)
# error "QAtomicInteger for 32-bit types must be supported!"
#endif
@@ -65,35 +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
-#ifdef Q_COMPILER_UNICODE_STRINGS
-# define TYPE_SUPPORTED_char32_t 1
-#endif
+#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
-# ifdef Q_COMPILER_UNICODE_STRINGS
-# define TYPE_SUPPORTED_char16_t 1
-# endif
-# 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
@@ -121,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
@@ -131,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
@@ -188,7 +134,7 @@ template <bool> inline void booleanHelper() { }
void tst_QAtomicIntegerXX::static_checks()
{
- Q_STATIC_ASSERT(sizeof(QAtomicInteger<T>) == sizeof(T));
+ static_assert(sizeof(QAtomicInteger<T>) == sizeof(T));
// statements with no effect
(void) QAtomicInteger<T>::isReferenceCountingNative();
@@ -200,17 +146,11 @@ void tst_QAtomicIntegerXX::static_checks()
(void) QAtomicInteger<T>::isFetchAndAddNative();
(void) QAtomicInteger<T>::isFetchAndAddWaitFree();
-#ifdef Q_COMPILER_CONSTEXPR
// this is a compile-time test only
- booleanHelper<QAtomicInteger<T>::isReferenceCountingNative()>();
booleanHelper<QAtomicInteger<T>::isReferenceCountingWaitFree()>();
- booleanHelper<QAtomicInteger<T>::isTestAndSetNative()>();
booleanHelper<QAtomicInteger<T>::isTestAndSetWaitFree()>();
- booleanHelper<QAtomicInteger<T>::isFetchAndStoreNative()>();
booleanHelper<QAtomicInteger<T>::isFetchAndStoreWaitFree()>();
- booleanHelper<QAtomicInteger<T>::isFetchAndAddNative()>();
booleanHelper<QAtomicInteger<T>::isFetchAndAddWaitFree()>();
-#endif
}
void tst_QAtomicIntegerXX::addData()
@@ -270,13 +210,13 @@ void tst_QAtomicIntegerXX::constructor()
QFETCH(LargeInt, value);
QAtomicInteger<T> atomic(value);
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QAtomicInteger<T> atomic2 = value;
- QCOMPARE(atomic2.load(), T(value));
+ QCOMPARE(atomic2.loadRelaxed(), T(value));
- QVERIFY(atomic.load() >= std::numeric_limits<T>::min());
- QVERIFY(atomic.load() <= std::numeric_limits<T>::max());
+ QVERIFY(atomic.loadRelaxed() >= std::numeric_limits<T>::min());
+ QVERIFY(atomic.loadRelaxed() <= std::numeric_limits<T>::max());
}
void tst_QAtomicIntegerXX::copy()
@@ -285,17 +225,17 @@ void tst_QAtomicIntegerXX::copy()
QAtomicInteger<T> atomic(value);
QAtomicInteger<T> copy(atomic);
- QCOMPARE(copy.load(), atomic.load());
+ QCOMPARE(copy.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy2 = atomic;
- QCOMPARE(copy2.load(), atomic.load());
+ QCOMPARE(copy2.loadRelaxed(), atomic.loadRelaxed());
// move
- QAtomicInteger<T> copy3(qMove(copy));
- QCOMPARE(copy3.load(), atomic.load());
+ QAtomicInteger<T> copy3(std::move(copy));
+ QCOMPARE(copy3.loadRelaxed(), atomic.loadRelaxed());
- QAtomicInteger<T> copy4 = qMove(copy2);
- QCOMPARE(copy4.load(), atomic.load());
+ QAtomicInteger<T> copy4 = std::move(copy2);
+ QCOMPARE(copy4.loadRelaxed(), atomic.loadRelaxed());
}
void tst_QAtomicIntegerXX::assign()
@@ -305,24 +245,24 @@ void tst_QAtomicIntegerXX::assign()
QAtomicInteger<T> atomic(value);
QAtomicInteger<T> copy;
copy = atomic;
- QCOMPARE(copy.load(), atomic.load());
+ QCOMPARE(copy.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy2;
copy2 = atomic; // operator=(const QAtomicInteger &)
- QCOMPARE(copy2.load(), atomic.load());
+ QCOMPARE(copy2.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy2bis;
- copy2bis = atomic.load(); // operator=(T)
- QCOMPARE(copy2bis.load(), atomic.load());
+ copy2bis = atomic.loadRelaxed(); // operator=(T)
+ QCOMPARE(copy2bis.loadRelaxed(), atomic.loadRelaxed());
// move
QAtomicInteger<T> copy3;
- copy3 = qMove(copy);
- QCOMPARE(copy3.load(), atomic.load());
+ copy3 = std::move(copy);
+ QCOMPARE(copy3.loadRelaxed(), atomic.loadRelaxed());
QAtomicInteger<T> copy4;
- copy4 = qMove(copy2);
- QCOMPARE(copy4.load(), atomic.load());
+ copy4 = std::move(copy2);
+ QCOMPARE(copy4.loadRelaxed(), atomic.loadRelaxed());
}
void tst_QAtomicIntegerXX::operatorInteger()
@@ -331,7 +271,7 @@ void tst_QAtomicIntegerXX::operatorInteger()
QAtomicInteger<T> atomic(value);
T val2 = atomic;
- QCOMPARE(val2, atomic.load());
+ QCOMPARE(val2, atomic.loadRelaxed());
QCOMPARE(val2, T(value));
}
@@ -346,53 +286,39 @@ void tst_QAtomicIntegerXX::loadAcquireStoreRelease()
QCOMPARE(atomic.loadAcquire(), T(~value));
atomic.storeRelease(value);
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
}
void tst_QAtomicIntegerXX::refDeref()
{
QFETCH(LargeInt, value);
- const bool needToPreventOverflow = TypeIsSigned && value == std::numeric_limits<T>::max();
- const bool needToPreventUnderflow = TypeIsSigned && value == std::numeric_limits<T>::min();
- T nextValue = T(value);
- if (!needToPreventOverflow)
- ++nextValue;
- T prevValue = T(value);
- if (!needToPreventUnderflow)
- --prevValue;
+
+ // 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.load(), nextValue);
+ QCOMPARE(atomic.loadRelaxed(), nextValue);
QCOMPARE(atomic.deref(), (value != 0));
- }
- QCOMPARE(atomic.load(), T(value));
- if (!needToPreventUnderflow) {
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.deref(), (prevValue != 0));
- QCOMPARE(atomic.load(), prevValue);
+ QCOMPARE(atomic.loadRelaxed(), prevValue);
QCOMPARE(atomic.ref(), (value != 0));
- }
- QCOMPARE(atomic.load(), T(value));
+ 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.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
}
void tst_QAtomicIntegerXX::testAndSet()
@@ -402,16 +328,16 @@ void tst_QAtomicIntegerXX::testAndSet()
QAtomicInteger<T> atomic(value);
QVERIFY(atomic.testAndSetRelaxed(value, newValue));
- QCOMPARE(atomic.load(), newValue);
+ QCOMPARE(atomic.loadRelaxed(), newValue);
QVERIFY(!atomic.testAndSetRelaxed(value, newValue));
QVERIFY(atomic.testAndSetRelaxed(newValue, value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QVERIFY(atomic.testAndSetAcquire(value, newValue));
- QCOMPARE(atomic.load(), newValue);
+ QCOMPARE(atomic.loadRelaxed(), newValue);
QVERIFY(!atomic.testAndSetAcquire(value, newValue));
QVERIFY(atomic.testAndSetAcquire(newValue, value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QVERIFY(atomic.testAndSetRelease(value, newValue));
QCOMPARE(atomic.loadAcquire(), newValue);
@@ -434,18 +360,18 @@ void tst_QAtomicIntegerXX::testAndSet3()
QAtomicInteger<T> atomic(value);
QVERIFY(atomic.testAndSetRelaxed(value, newValue, oldValue));
- QCOMPARE(atomic.load(), newValue);
+ QCOMPARE(atomic.loadRelaxed(), newValue);
QVERIFY(!atomic.testAndSetRelaxed(value, newValue, oldValue));
QCOMPARE(oldValue, newValue);
QVERIFY(atomic.testAndSetRelaxed(newValue, value, oldValue));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QVERIFY(atomic.testAndSetAcquire(value, newValue, oldValue));
- QCOMPARE(atomic.load(), newValue);
+ QCOMPARE(atomic.loadRelaxed(), newValue);
QVERIFY(!atomic.testAndSetAcquire(value, newValue, oldValue));
QCOMPARE(oldValue, newValue);
QVERIFY(atomic.testAndSetAcquire(newValue, value, oldValue));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QVERIFY(atomic.testAndSetRelease(value, newValue, oldValue));
QCOMPARE(atomic.loadAcquire(), newValue);
@@ -469,14 +395,14 @@ void tst_QAtomicIntegerXX::fetchAndStore()
QAtomicInteger<T> atomic(value);
QCOMPARE(atomic.fetchAndStoreRelaxed(newValue), T(value));
- QCOMPARE(atomic.load(), newValue);
+ QCOMPARE(atomic.loadRelaxed(), newValue);
QCOMPARE(atomic.fetchAndStoreRelaxed(value), newValue);
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndStoreAcquire(newValue), T(value));
- QCOMPARE(atomic.load(), newValue);
+ QCOMPARE(atomic.loadRelaxed(), newValue);
QCOMPARE(atomic.fetchAndStoreAcquire(value), newValue);
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndStoreRelease(newValue), T(value));
QCOMPARE(atomic.loadAcquire(), newValue);
@@ -494,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.load(), newValue1);
+ QCOMPARE(atomic.loadRelaxed(), newValue1);
QCOMPARE(atomic.fetchAndAddRelaxed(parcel2), newValue1);
- }
- QCOMPARE(atomic.load(), T(value));
- if (!needToPreventUnderflow) {
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAddRelaxed(parcel2), T(value));
- QCOMPARE(atomic.load(), newValue2);
+ QCOMPARE(atomic.loadRelaxed(), newValue2);
QCOMPARE(atomic.fetchAndAddRelaxed(parcel1), newValue2);
- }
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
- if (!needToPreventOverflow) {
QCOMPARE(atomic.fetchAndAddAcquire(parcel1), T(value));
- QCOMPARE(atomic.load(), newValue1);
+ QCOMPARE(atomic.loadRelaxed(), newValue1);
QCOMPARE(atomic.fetchAndAddAcquire(parcel2), newValue1);
- }
- QCOMPARE(atomic.load(), T(value));
- if (!needToPreventUnderflow) {
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAddAcquire(parcel2), T(value));
- QCOMPARE(atomic.load(), newValue2);
+ QCOMPARE(atomic.loadRelaxed(), newValue2);
QCOMPARE(atomic.fetchAndAddAcquire(parcel1), newValue2);
- }
- QCOMPARE(atomic.load(), T(value));
+ 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()
@@ -575,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.load(), newValue1);
+ QCOMPARE(atomic.loadRelaxed(), newValue1);
QCOMPARE(atomic.fetchAndSubRelaxed(parcel2), newValue1);
- }
- QCOMPARE(atomic.load(), T(value));
- if (!needToPreventOverflow) {
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndSubRelaxed(parcel2), T(value));
- QCOMPARE(atomic.load(), newValue2);
+ QCOMPARE(atomic.loadRelaxed(), newValue2);
QCOMPARE(atomic.fetchAndSubRelaxed(parcel1), newValue2);
- }
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
- if (!needToPreventUnderflow) {
QCOMPARE(atomic.fetchAndSubAcquire(parcel1), T(value));
- QCOMPARE(atomic.load(), newValue1);
+ QCOMPARE(atomic.loadRelaxed(), newValue1);
QCOMPARE(atomic.fetchAndSubAcquire(parcel2), newValue1);
- }
- QCOMPARE(atomic.load(), T(value));
- if (!needToPreventOverflow) {
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndSubAcquire(parcel2), T(value));
- QCOMPARE(atomic.load(), newValue2);
+ QCOMPARE(atomic.loadRelaxed(), newValue2);
QCOMPARE(atomic.fetchAndSubAcquire(parcel1), newValue2);
- }
- QCOMPARE(atomic.load(), T(value));
+ 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()
@@ -662,32 +538,32 @@ void tst_QAtomicIntegerXX::fetchAndOr()
QCOMPARE(atomic.fetchAndOrRelaxed(zero), T(value));
QCOMPARE(atomic.fetchAndOrRelaxed(one), T(value));
- QCOMPARE(atomic.load(), T(value | 1));
+ QCOMPARE(atomic.loadRelaxed(), T(value | 1));
QCOMPARE(atomic.fetchAndOrRelaxed(minusOne), T(value | 1));
- QCOMPARE(atomic.load(), minusOne);
+ QCOMPARE(atomic.loadRelaxed(), minusOne);
- atomic.store(value);
+ atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndOrAcquire(zero), T(value));
QCOMPARE(atomic.fetchAndOrAcquire(one), T(value));
- QCOMPARE(atomic.load(), T(value | 1));
+ QCOMPARE(atomic.loadRelaxed(), T(value | 1));
QCOMPARE(atomic.fetchAndOrAcquire(minusOne), T(value | 1));
- QCOMPARE(atomic.load(), minusOne);
+ QCOMPARE(atomic.loadRelaxed(), minusOne);
- atomic.store(value);
+ atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndOrRelease(zero), T(value));
QCOMPARE(atomic.fetchAndOrRelease(one), T(value));
- QCOMPARE(atomic.load(), T(value | 1));
+ QCOMPARE(atomic.loadRelaxed(), T(value | 1));
QCOMPARE(atomic.fetchAndOrRelease(minusOne), T(value | 1));
- QCOMPARE(atomic.load(), minusOne);
+ QCOMPARE(atomic.loadRelaxed(), minusOne);
- atomic.store(value);
+ atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndOrOrdered(zero), T(value));
QCOMPARE(atomic.fetchAndOrOrdered(one), T(value));
- QCOMPARE(atomic.load(), T(value | 1));
+ QCOMPARE(atomic.loadRelaxed(), T(value | 1));
QCOMPARE(atomic.fetchAndOrOrdered(minusOne), T(value | 1));
- QCOMPARE(atomic.load(), minusOne);
+ QCOMPARE(atomic.loadRelaxed(), minusOne);
- atomic.store(value);
+ atomic.storeRelaxed(value);
QCOMPARE(atomic |= zero, T(value));
QCOMPARE(atomic |= one, T(value | 1));
QCOMPARE(atomic |= minusOne, minusOne);
@@ -703,37 +579,37 @@ void tst_QAtomicIntegerXX::fetchAndAnd()
T minusOne = T(~0);
QCOMPARE(atomic.fetchAndAndRelaxed(minusOne), T(value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAndRelaxed(f), T(value));
- QCOMPARE(atomic.load(), T(value & 0xf));
+ QCOMPARE(atomic.loadRelaxed(), T(value & 0xf));
QCOMPARE(atomic.fetchAndAndRelaxed(zero), T(value & 0xf));
- QCOMPARE(atomic.load(), zero);
+ QCOMPARE(atomic.loadRelaxed(), zero);
- atomic.store(value);
+ atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndAndAcquire(minusOne), T(value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAndAcquire(f), T(value));
- QCOMPARE(atomic.load(), T(value & 0xf));
+ QCOMPARE(atomic.loadRelaxed(), T(value & 0xf));
QCOMPARE(atomic.fetchAndAndAcquire(zero), T(value & 0xf));
- QCOMPARE(atomic.load(), zero);
+ QCOMPARE(atomic.loadRelaxed(), zero);
- atomic.store(value);
+ atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndAndRelease(minusOne), T(value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAndRelease(f), T(value));
- QCOMPARE(atomic.load(), T(value & 0xf));
+ QCOMPARE(atomic.loadRelaxed(), T(value & 0xf));
QCOMPARE(atomic.fetchAndAndRelease(zero), T(value & 0xf));
- QCOMPARE(atomic.load(), zero);
+ QCOMPARE(atomic.loadRelaxed(), zero);
- atomic.store(value);
+ atomic.storeRelaxed(value);
QCOMPARE(atomic.fetchAndAndOrdered(minusOne), T(value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndAndOrdered(f), T(value));
- QCOMPARE(atomic.load(), T(value & 0xf));
+ QCOMPARE(atomic.loadRelaxed(), T(value & 0xf));
QCOMPARE(atomic.fetchAndAndOrdered(zero), T(value & 0xf));
- QCOMPARE(atomic.load(), zero);
+ QCOMPARE(atomic.loadRelaxed(), zero);
- atomic.store(value);
+ atomic.storeRelaxed(value);
QCOMPARE(atomic &= minusOne, T(value));
QCOMPARE(atomic &= f, T(value & 0xf));
QCOMPARE(atomic &= zero, zero);
@@ -749,48 +625,48 @@ void tst_QAtomicIntegerXX::fetchAndXor()
T minusOne = T(~0);
QCOMPARE(atomic.fetchAndXorRelaxed(zero), T(value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelaxed(pattern), T(value));
- QCOMPARE(atomic.load(), T(value ^ pattern));
+ QCOMPARE(atomic.loadRelaxed(), T(value ^ pattern));
QCOMPARE(atomic.fetchAndXorRelaxed(pattern), T(value ^ pattern));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelaxed(minusOne), T(value));
- QCOMPARE(atomic.load(), T(~value));
+ QCOMPARE(atomic.loadRelaxed(), T(~value));
QCOMPARE(atomic.fetchAndXorRelaxed(minusOne), T(~value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorAcquire(zero), T(value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorAcquire(pattern), T(value));
- QCOMPARE(atomic.load(), T(value ^ pattern));
+ QCOMPARE(atomic.loadRelaxed(), T(value ^ pattern));
QCOMPARE(atomic.fetchAndXorAcquire(pattern), T(value ^ pattern));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorAcquire(minusOne), T(value));
- QCOMPARE(atomic.load(), T(~value));
+ QCOMPARE(atomic.loadRelaxed(), T(~value));
QCOMPARE(atomic.fetchAndXorAcquire(minusOne), T(~value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelease(zero), T(value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelease(pattern), T(value));
- QCOMPARE(atomic.load(), T(value ^ pattern));
+ QCOMPARE(atomic.loadRelaxed(), T(value ^ pattern));
QCOMPARE(atomic.fetchAndXorRelease(pattern), T(value ^ pattern));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorRelease(minusOne), T(value));
- QCOMPARE(atomic.load(), T(~value));
+ QCOMPARE(atomic.loadRelaxed(), T(~value));
QCOMPARE(atomic.fetchAndXorRelease(minusOne), T(~value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorOrdered(zero), T(value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorOrdered(pattern), T(value));
- QCOMPARE(atomic.load(), T(value ^ pattern));
+ QCOMPARE(atomic.loadRelaxed(), T(value ^ pattern));
QCOMPARE(atomic.fetchAndXorOrdered(pattern), T(value ^ pattern));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic.fetchAndXorOrdered(minusOne), T(value));
- QCOMPARE(atomic.load(), T(~value));
+ QCOMPARE(atomic.loadRelaxed(), T(~value));
QCOMPARE(atomic.fetchAndXorOrdered(minusOne), T(~value));
- QCOMPARE(atomic.load(), T(value));
+ QCOMPARE(atomic.loadRelaxed(), T(value));
QCOMPARE(atomic ^= zero, T(value));
QCOMPARE(atomic ^= pattern, T(value ^ pattern));
@@ -798,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
new file mode 100644
index 0000000000..95d88d31a6
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/uchar/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=uchar
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_uchar
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/uchar/uchar.pro b/tests/auto/corelib/thread/qatomicinteger/uchar/uchar.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/uchar/uchar.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
new file mode 100644
index 0000000000..2ab977ef6a
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/uint/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=uint
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_uint
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/uint/uint.pro b/tests/auto/corelib/thread/qatomicinteger/uint/uint.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/uint/uint.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt
new file mode 100644
index 0000000000..7707bd53b5
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/ulong/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=ulong
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_ulong
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/ulong/ulong.pro b/tests/auto/corelib/thread/qatomicinteger/ulong/ulong.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/ulong/ulong.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt
new file mode 100644
index 0000000000..667e9eade6
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/ushort/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=ushort
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_ushort
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/ushort/ushort.pro b/tests/auto/corelib/thread/qatomicinteger/ushort/ushort.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/ushort/ushort.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt b/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt
new file mode 100644
index 0000000000..0e2d084b58
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicinteger/wchar_t/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ DEFINES
+ QATOMIC_TEST_TYPE=wchar_t
+ tst_QAtomicIntegerXX=tst_QAtomicInteger_wchar_t
+)
diff --git a/tests/auto/corelib/thread/qatomicinteger/wchar_t/wchar_t.pro b/tests/auto/corelib/thread/qatomicinteger/wchar_t/wchar_t.pro
deleted file mode 100644
index 1e97d5cbae..0000000000
--- a/tests/auto/corelib/thread/qatomicinteger/wchar_t/wchar_t.pro
+++ /dev/null
@@ -1 +0,0 @@
-include(../qatomicinteger.pri)
diff --git a/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt b/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
new file mode 100644
index 0000000000..cd8df9db66
--- /dev/null
+++ b/tests/auto/corelib/thread/qatomicpointer/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qatomicpointer.pro b/tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro
deleted file mode 100644
index cce822da6e..0000000000
--- a/tests/auto/corelib/thread/qatomicpointer/qatomicpointer.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qatomicpointer
-QT = core testlib
-SOURCES = tst_qatomicpointer.cpp
diff --git a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
index 0200473cae..347831819e 100644
--- a/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
+++ b/tests/auto/corelib/thread/qatomicpointer/tst_qatomicpointer.cpp
@@ -1,33 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <qatomic.h>
#include <limits.h>
@@ -73,12 +48,12 @@ void tst_QAtomicPointer::warningFreeHelper()
{
qFatal("This code is bogus, and shouldn't be run. We're looking for compiler warnings only.");
- QBasicAtomicPointer<WFHC> p = Q_BASIC_ATOMIC_INITIALIZER(0);
+ QBasicAtomicPointer<WFHC> p = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
- p.load()->bar();
+ p.loadRelaxed()->bar();
- WFHC *expectedValue = 0;
- WFHC *newValue = 0;
+ WFHC *expectedValue = nullptr;
+ WFHC *newValue = nullptr;
qptrdiff valueToAdd = 0;
p.testAndSetRelaxed(expectedValue, newValue);
@@ -108,26 +83,22 @@ void tst_QAtomicPointer::warningFree()
void tst_QAtomicPointer::alignment()
{
-#ifdef Q_ALIGNOF
- // this will cause a build error if the alignment isn't the same
- char dummy[Q_ALIGNOF(QBasicAtomicPointer<void>) == Q_ALIGNOF(void*) ? 1 : -1];
- (void)dummy;
-#endif
+ static_assert(alignof(QBasicAtomicPointer<void>) == alignof(void*));
}
void tst_QAtomicPointer::constructor()
{
void *one = this;
QAtomicPointer<void> atomic1 = one;
- QCOMPARE(atomic1.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), one);
void *two = &one;
QAtomicPointer<void> atomic2 = two;
- QCOMPARE(atomic2.load(), two);
+ QCOMPARE(atomic2.loadRelaxed(), two);
void *three = &two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic3.loadRelaxed(), three);
}
void tst_QAtomicPointer::copy_constructor()
@@ -135,20 +106,20 @@ void tst_QAtomicPointer::copy_constructor()
void *one = this;
QAtomicPointer<void> atomic1 = one;
QAtomicPointer<void> atomic1_copy = atomic1;
- QCOMPARE(atomic1_copy.load(), one);
- QCOMPARE(atomic1_copy.load(), atomic1.load());
+ QCOMPARE(atomic1_copy.loadRelaxed(), one);
+ QCOMPARE(atomic1_copy.loadRelaxed(), atomic1.loadRelaxed());
void *two = &one;
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic2_copy = atomic2;
- QCOMPARE(atomic2_copy.load(), two);
- QCOMPARE(atomic2_copy.load(), atomic2.load());
+ QCOMPARE(atomic2_copy.loadRelaxed(), two);
+ QCOMPARE(atomic2_copy.loadRelaxed(), atomic2.loadRelaxed());
void *three = &two;
QAtomicPointer<void> atomic3 = three;
QAtomicPointer<void> atomic3_copy = atomic3;
- QCOMPARE(atomic3_copy.load(), three);
- QCOMPARE(atomic3_copy.load(), atomic3.load());
+ QCOMPARE(atomic3_copy.loadRelaxed(), three);
+ QCOMPARE(atomic3_copy.loadRelaxed(), atomic3.loadRelaxed());
}
void tst_QAtomicPointer::assignment_operator()
@@ -161,17 +132,17 @@ void tst_QAtomicPointer::assignment_operator()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
atomic1 = two;
atomic2 = three;
atomic3 = one;
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
void tst_QAtomicPointer::isTestAndSetNative()
@@ -234,17 +205,17 @@ void tst_QAtomicPointer::testAndSet()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
QVERIFY(atomic1.testAndSetRelaxed(one, two));
QVERIFY(atomic2.testAndSetRelaxed(two, three));
QVERIFY(atomic3.testAndSetRelaxed(three, one));
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
{
@@ -252,17 +223,17 @@ void tst_QAtomicPointer::testAndSet()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
QVERIFY(atomic1.testAndSetAcquire(one, two));
QVERIFY(atomic2.testAndSetAcquire(two, three));
QVERIFY(atomic3.testAndSetAcquire(three, one));
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
{
@@ -270,17 +241,17 @@ void tst_QAtomicPointer::testAndSet()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
QVERIFY(atomic1.testAndSetRelease(one, two));
QVERIFY(atomic2.testAndSetRelease(two, three));
QVERIFY(atomic3.testAndSetRelease(three, one));
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
{
@@ -288,17 +259,17 @@ void tst_QAtomicPointer::testAndSet()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
QVERIFY(atomic1.testAndSetOrdered(one, two));
QVERIFY(atomic2.testAndSetOrdered(two, three));
QVERIFY(atomic3.testAndSetOrdered(three, one));
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
}
@@ -362,17 +333,17 @@ void tst_QAtomicPointer::fetchAndStore()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
QCOMPARE(atomic1.fetchAndStoreRelaxed(two), one);
QCOMPARE(atomic2.fetchAndStoreRelaxed(three), two);
QCOMPARE(atomic3.fetchAndStoreRelaxed(one), three);
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
{
@@ -380,17 +351,17 @@ void tst_QAtomicPointer::fetchAndStore()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
QCOMPARE(atomic1.fetchAndStoreAcquire(two), one);
QCOMPARE(atomic2.fetchAndStoreAcquire(three), two);
QCOMPARE(atomic3.fetchAndStoreAcquire(one), three);
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
{
@@ -398,17 +369,17 @@ void tst_QAtomicPointer::fetchAndStore()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
QCOMPARE(atomic1.fetchAndStoreRelease(two), one);
QCOMPARE(atomic2.fetchAndStoreRelease(three), two);
QCOMPARE(atomic3.fetchAndStoreRelease(one), three);
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
{
@@ -416,17 +387,17 @@ void tst_QAtomicPointer::fetchAndStore()
QAtomicPointer<void> atomic2 = two;
QAtomicPointer<void> atomic3 = three;
- QCOMPARE(atomic1.load(), one);
- QCOMPARE(atomic2.load(), two);
- QCOMPARE(atomic3.load(), three);
+ QCOMPARE(atomic1.loadRelaxed(), one);
+ QCOMPARE(atomic2.loadRelaxed(), two);
+ QCOMPARE(atomic3.loadRelaxed(), three);
QCOMPARE(atomic1.fetchAndStoreOrdered(two), one);
QCOMPARE(atomic2.fetchAndStoreOrdered(three), two);
QCOMPARE(atomic3.fetchAndStoreOrdered(one), three);
- QCOMPARE(atomic1.load(), two);
- QCOMPARE(atomic2.load(), three);
- QCOMPARE(atomic3.load(), one);
+ QCOMPARE(atomic1.loadRelaxed(), two);
+ QCOMPARE(atomic2.loadRelaxed(), three);
+ QCOMPARE(atomic3.loadRelaxed(), one);
}
}
@@ -530,66 +501,66 @@ void tst_QAtomicPointer::fetchAndAdd()
// cast to void* in order to avoid QCOMPARE to compare string content of the char*
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddRelaxed(valueToAdd)), static_cast<void*>(pc));
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddRelaxed(-valueToAdd)), static_cast<void*>(pc + valueToAdd));
- QCOMPARE(static_cast<void*>(pointer1.load()), static_cast<void*>(pc));
+ QCOMPARE(static_cast<void*>(pointer1.loadRelaxed()), static_cast<void*>(pc));
QAtomicPointer<short> pointer2 = ps;
QCOMPARE(pointer2.fetchAndAddRelaxed(valueToAdd), ps);
QCOMPARE(pointer2.fetchAndAddRelaxed(-valueToAdd), ps + valueToAdd);
- QCOMPARE(pointer2.load(), ps);
+ QCOMPARE(pointer2.loadRelaxed(), ps);
QAtomicPointer<int> pointer3 = pi;
QCOMPARE(pointer3.fetchAndAddRelaxed(valueToAdd), pi);
QCOMPARE(pointer3.fetchAndAddRelaxed(-valueToAdd), pi + valueToAdd);
- QCOMPARE(pointer3.load(), pi);
+ QCOMPARE(pointer3.loadRelaxed(), pi);
}
{
QAtomicPointer<char> pointer1 = pc;
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddAcquire(valueToAdd)), static_cast<void*>(pc));
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddAcquire(-valueToAdd)), static_cast<void*>(pc + valueToAdd));
- QCOMPARE(static_cast<void*>(pointer1.load()), static_cast<void*>(pc));
+ QCOMPARE(static_cast<void*>(pointer1.loadRelaxed()), static_cast<void*>(pc));
QAtomicPointer<short> pointer2 = ps;
QCOMPARE(pointer2.fetchAndAddAcquire(valueToAdd), ps);
QCOMPARE(pointer2.fetchAndAddAcquire(-valueToAdd), ps + valueToAdd);
- QCOMPARE(pointer2.load(), ps);
+ QCOMPARE(pointer2.loadRelaxed(), ps);
QAtomicPointer<int> pointer3 = pi;
QCOMPARE(pointer3.fetchAndAddAcquire(valueToAdd), pi);
QCOMPARE(pointer3.fetchAndAddAcquire(-valueToAdd), pi + valueToAdd);
- QCOMPARE(pointer3.load(), pi);
+ QCOMPARE(pointer3.loadRelaxed(), pi);
}
{
QAtomicPointer<char> pointer1 = pc;
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddRelease(valueToAdd)), static_cast<void*>(pc));
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddRelease(-valueToAdd)), static_cast<void*>(pc + valueToAdd));
- QCOMPARE(static_cast<void*>(pointer1.load()), static_cast<void*>(pc));
+ QCOMPARE(static_cast<void*>(pointer1.loadRelaxed()), static_cast<void*>(pc));
QAtomicPointer<short> pointer2 = ps;
QCOMPARE(pointer2.fetchAndAddRelease(valueToAdd), ps);
QCOMPARE(pointer2.fetchAndAddRelease(-valueToAdd), ps + valueToAdd);
- QCOMPARE(pointer2.load(), ps);
+ QCOMPARE(pointer2.loadRelaxed(), ps);
QAtomicPointer<int> pointer3 = pi;
QCOMPARE(pointer3.fetchAndAddRelease(valueToAdd), pi);
QCOMPARE(pointer3.fetchAndAddRelease(-valueToAdd), pi + valueToAdd);
- QCOMPARE(pointer3.load(), pi);
+ QCOMPARE(pointer3.loadRelaxed(), pi);
}
{
QAtomicPointer<char> pointer1 = pc;
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddOrdered(valueToAdd)), static_cast<void*>(pc));
QCOMPARE(static_cast<void*>(pointer1.fetchAndAddOrdered(-valueToAdd)), static_cast<void*>(pc + valueToAdd));
- QCOMPARE(static_cast<void*>(pointer1.load()), static_cast<void*>(pc));
+ QCOMPARE(static_cast<void*>(pointer1.loadRelaxed()), static_cast<void*>(pc));
QAtomicPointer<short> pointer2 = ps;
QCOMPARE(pointer2.fetchAndAddOrdered(valueToAdd), ps);
QCOMPARE(pointer2.fetchAndAddOrdered(-valueToAdd), ps + valueToAdd);
- QCOMPARE(pointer2.load(), ps);
+ QCOMPARE(pointer2.loadRelaxed(), ps);
QAtomicPointer<int> pointer3 = pi;
QCOMPARE(pointer3.fetchAndAddOrdered(valueToAdd), pi);
QCOMPARE(pointer3.fetchAndAddOrdered(-valueToAdd), pi + valueToAdd);
- QCOMPARE(pointer3.load(), pi);
+ QCOMPARE(pointer3.loadRelaxed(), pi);
}
}
template <typename T> void constAndVolatile_helper()
{
- T *one = 0;
+ T *one = nullptr;
T *two = &one;
T *three = &two;
@@ -598,34 +569,34 @@ template <typename T> void constAndVolatile_helper()
QAtomicPointer<T> atomic2 = two;
QAtomicPointer<T> atomic3 = three;
- QVERIFY(atomic1.load() == one);
- QVERIFY(atomic2.load() == two);
- QVERIFY(atomic3.load() == three);
+ QVERIFY(atomic1.loadRelaxed() == one);
+ QVERIFY(atomic2.loadRelaxed() == two);
+ QVERIFY(atomic3.loadRelaxed() == three);
QVERIFY(atomic1.fetchAndStoreRelaxed(two) == one);
QVERIFY(atomic2.fetchAndStoreRelaxed(three) == two);
QVERIFY(atomic3.fetchAndStoreRelaxed(one) == three);
- QVERIFY(atomic1.load() == two);
- QVERIFY(atomic2.load() == three);
- QVERIFY(atomic3.load() == one);
+ QVERIFY(atomic1.loadRelaxed() == two);
+ QVERIFY(atomic2.loadRelaxed() == three);
+ QVERIFY(atomic3.loadRelaxed() == one);
}
{
QAtomicPointer<T> atomic1 = one;
QAtomicPointer<T> atomic2 = two;
QAtomicPointer<T> atomic3 = three;
- QVERIFY(atomic1.load() == one);
- QVERIFY(atomic2.load() == two);
- QVERIFY(atomic3.load() == three);
+ QVERIFY(atomic1.loadRelaxed() == one);
+ QVERIFY(atomic2.loadRelaxed() == two);
+ QVERIFY(atomic3.loadRelaxed() == three);
QVERIFY(atomic1.testAndSetRelaxed(one, two));
QVERIFY(atomic2.testAndSetRelaxed(two, three));
QVERIFY(atomic3.testAndSetRelaxed(three, one));
- QVERIFY(atomic1.load() == two);
- QVERIFY(atomic2.load() == three);
- QVERIFY(atomic3.load() == one);
+ QVERIFY(atomic1.loadRelaxed() == two);
+ QVERIFY(atomic2.loadRelaxed() == three);
+ QVERIFY(atomic3.loadRelaxed() == one);
}
}
@@ -649,8 +620,8 @@ void tst_QAtomicPointer::forwardDeclared()
// this is just a compilation test
QAtomicPointer<ForwardDeclared> ptr;
ContainsForwardDeclared cfd;
- Q_UNUSED(ptr);
- Q_UNUSED(cfd);
+ Q_UNUSED(ptr)
+ Q_UNUSED(cfd)
QVERIFY(true);
}
@@ -665,7 +636,7 @@ template <typename T> static void operators_helper()
{
// Test that QBasicAtomicPointer also has operator= and cast operators
// We've been using them for QAtomicPointer<T> elsewhere
- QBasicAtomicPointer<T> atomic = Q_BASIC_ATOMIC_INITIALIZER(0);
+ QBasicAtomicPointer<T> atomic = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
atomic = one;
QCOMPARE(Ptr(atomic), one);
}
diff --git a/tests/auto/corelib/thread/qfuture/CMakeLists.txt b/tests/auto/corelib/thread/qfuture/CMakeLists.txt
new file mode 100644
index 0000000000..ba5730d5cf
--- /dev/null
+++ b/tests/auto/corelib/thread/qfuture/CMakeLists.txt
@@ -0,0 +1,27 @@
+# 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
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::TestPrivate
+)
+
+qt_internal_extend_target(tst_qfuture CONDITION MSVC
+ COMPILE_OPTIONS
+ /bigobj
+)
+
+qt_internal_undefine_global_definition(tst_qfuture QT_NO_JAVA_STYLE_ITERATORS)
diff --git a/tests/auto/corelib/thread/qfuture/qfuture.pro b/tests/auto/corelib/thread/qfuture/qfuture.pro
deleted file mode 100644
index b1667760d6..0000000000
--- a/tests/auto/corelib/thread/qfuture/qfuture.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qfuture
-QT = core core-private testlib
-SOURCES = tst_qfuture.cpp
-DEFINES += QT_STRICT_ITERATORS
diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
index b8c82c2ea0..8a6ac6a61d 100644
--- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
+++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
@@ -1,73 +1,191 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <QCoreApplication>
-#include <QDebug>
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
#define QFUTURE_TEST
-#include <QtTest/QtTest>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QSemaphore>
+#include <QTestEventLoop>
+#include <QTimer>
+#include <QSignalSpy>
+#include <QVarLengthArray>
+#include <QSet>
+#include <QList>
+#include <private/qobject_p.h>
+
+#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <qfuture.h>
#include <qfuturewatcher.h>
#include <qresultstore.h>
#include <qthreadpool.h>
#include <qexception.h>
#include <qrandom.h>
+#include <QtConcurrent/qtconcurrentrun.h>
#include <private/qfutureinterface_p.h>
+#include <forward_list>
+#include <list>
+#include <vector>
+#include <memory>
+#include <set>
+
// COM interface macro.
#if defined(Q_OS_WIN) && defined(interface)
# undef interface
#endif
-struct ResultStoreInt : QtPrivate::ResultStoreBase
+using namespace std::chrono_literals;
+static constexpr auto DefaultWaitTime = 2s;
+
+using namespace Qt::StringLiterals;
+
+class SenderObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ void emitNoArg() { emit noArgSignal(); }
+ void emitIntArg(int value) { emit intArgSignal(value); }
+ void emitConstRefArg(const QString &value) { emit constRefArg(value); }
+ void emitMultipleArgs(int value1, double value2, const QString &value3)
+ {
+ emit multipleArgs(value1, value2, value3);
+ }
+ void emitTupleArgSignal(const std::tuple<int, double, QString> &t) { emit tupleArgSignal(t); }
+ void emitMultiArgsWithTupleSignal1(int value, const std::tuple<int, double, QString> &t)
+ {
+ emit multiArgsWithTupleSignal1(value, t);
+ }
+ void emitMultiArgsWithTupleSignal2(const std::tuple<int, double, QString> &t, int value)
+ {
+ emit multiArgsWithTupleSignal2(t, value);
+ }
+ void emitMultiArgsWithPairSignal1(int value, const std::pair<int, double> &p)
+ {
+ emit multiArgsWithPairSignal1(value, p);
+ }
+ void emitMultiArgsWithPairSignal2(const std::pair<int, double> &p, int value)
+ {
+ emit multiArgsWithPairSignal2(p, value);
+ }
+
+ void emitNoArgPrivateSignal() { emit noArgPrivateSignal(QPrivateSignal()); }
+ void emitIntArgPrivateSignal(int value) { emit intArgPrivateSignal(value, QPrivateSignal()); }
+ void emitMultiArgsPrivateSignal(int value1, double value2, const QString &value3)
+ {
+ emit multiArgsPrivateSignal(value1, value2, value3, QPrivateSignal());
+ }
+ void emitTupleArgPrivateSignal(const std::tuple<int, double, QString> &t)
+ {
+ emit tupleArgPrivateSignal(t, QPrivateSignal());
+ }
+ void emitMultiArgsWithTuplePrivateSignal1(int value, const std::tuple<int, double, QString> &t)
+ {
+ emit multiArgsWithTuplePrivateSignal1(value, t, QPrivateSignal());
+ }
+ void emitMultiArgsWithTuplePrivateSignal2(const std::tuple<int, double, QString> &t, int value)
+ {
+ emit multiArgsWithTuplePrivateSignal2(t, value, QPrivateSignal());
+ }
+ void emitMultiArgsWithPairPrivateSignal1(int value, const std::pair<int, double> &p)
+ {
+ emit multiArgsWithPairPrivateSignal1(value, p, QPrivateSignal());
+ }
+ void emitMultiArgsWithPairPrivateSignal2(const std::pair<int, double> &p, int value)
+ {
+ emit multiArgsWithPairPrivateSignal2(p, value, QPrivateSignal());
+ }
+
+signals:
+ void noArgSignal();
+ void intArgSignal(int value);
+ void constRefArg(const QString &value);
+ void multipleArgs(int value1, double value2, const QString &value3);
+ void tupleArgSignal(const std::tuple<int, double, QString> &t);
+ void multiArgsWithTupleSignal1(int value, const std::tuple<int, double, QString> &t);
+ void multiArgsWithTupleSignal2(const std::tuple<int, double, QString> &t, int value);
+ void multiArgsWithPairSignal1(int value, const std::pair<int, double> &p);
+ void multiArgsWithPairSignal2(const std::pair<int, double> &p, int value);
+
+ // Private signals
+ void noArgPrivateSignal(QPrivateSignal);
+ void intArgPrivateSignal(int value, QPrivateSignal);
+ void multiArgsPrivateSignal(int value1, double value2, const QString &value3, QPrivateSignal);
+ void tupleArgPrivateSignal(const std::tuple<int, double, QString> &t, QPrivateSignal);
+ void multiArgsWithTuplePrivateSignal1(int value, const std::tuple<int, double, QString> &t,
+ QPrivateSignal);
+ void multiArgsWithTuplePrivateSignal2(const std::tuple<int, double, QString> &t, int value,
+ QPrivateSignal);
+ void multiArgsWithPairPrivateSignal1(int value, const std::pair<int, double> &p,
+ QPrivateSignal);
+ void multiArgsWithPairPrivateSignal2(const std::pair<int, double> &p, int value,
+ QPrivateSignal);
+};
+
+class LambdaThread : public QThread
{
- ~ResultStoreInt() { clear<int>(); }
+public:
+ LambdaThread(std::function<void ()> fn)
+ :m_fn(fn)
+ {
+
+ }
+
+ void run() override
+ {
+ m_fn();
+ }
+
+private:
+ std::function<void ()> m_fn;
};
+// Emulates QWidget behavior by deleting its children early in the destructor
+// instead of leaving it to ~QObject()
+class FakeQWidget : public QObject
+{
+ Q_OBJECT
+public:
+ ~FakeQWidget() override {
+ auto *d = QObjectPrivate::get(this);
+ d->deleteChildren();
+ }
+};
+
+using UniquePtr = std::unique_ptr<int>;
+
class tst_QFuture: public QObject
{
Q_OBJECT
private slots:
+ void compareCompiles();
void resultStore();
void future();
+ void futureToVoid();
void futureInterface();
void refcounting();
void cancel();
+ void cancelAndFinish();
void statePropagation();
void multipleResults();
void indexedResults();
void progress();
+ void setProgressRange();
+ void progressWithRange();
void progressText();
void resultsAfterFinished();
void resultsAsList();
- void implicitConversions();
void iterators();
+ void iteratorsThread();
+#if QT_DEPRECATED_SINCE(6, 0)
void pause();
+ void suspendCheckPaused();
+#endif
+ void suspend();
void throttling();
void voidConversions();
#ifndef QT_NO_EXCEPTIONS
@@ -75,8 +193,94 @@ private slots:
void nestedExceptions();
#endif
void nonGlobalThreadPool();
+
+ void then();
+ void thenForMoveOnlyTypes();
+ void thenOnCanceledFuture();
+#ifndef QT_NO_EXCEPTIONS
+ void thenOnExceptionFuture();
+ void thenThrows();
+ void onFailed();
+ void onFailedTestCallables();
+ void onFailedForMoveOnlyTypes();
+#endif
+ void onCanceled();
+ void cancelContinuations();
+ void continuationsWithContext_data();
+ void continuationsWithContext();
+ void continuationsWithMoveOnlyLambda();
+#if 0
+ // TODO: enable when QFuture::takeResults() is enabled
+ void takeResults();
+#endif
+ void takeResult();
+ void runAndTake();
+ void resultsReadyAt_data();
+ void resultsReadyAt();
+ void takeResultWorksForTypesWithoutDefaultCtor();
+ void canceledFutureIsNotValid();
+ void signalConnect();
+ void waitForFinished();
+
+ void rejectResultOverwrite_data();
+ void rejectResultOverwrite();
+ void rejectPendingResultOverwrite_data() { rejectResultOverwrite_data(); }
+ void rejectPendingResultOverwrite();
+
+ void createReadyFutures();
+ void continuationsAfterReadyFutures();
+
+ void getFutureInterface();
+ void convertQMetaType();
+
+ void whenAllIterators();
+ void whenAllIteratorsWithCanceled();
+ void whenAllIteratorsWithFailed();
+ void whenAllDifferentTypes();
+ void whenAllDifferentTypesWithCanceled();
+ void whenAllDifferentTypesWithFailed();
+ void whenAnyIterators();
+ void whenAnyIteratorsWithCanceled();
+ void whenAnyIteratorsWithFailed();
+ void whenAnyDifferentTypes();
+ void whenAnyDifferentTypesWithCanceled();
+ void whenAnyDifferentTypesWithFailed();
+
+ void continuationOverride();
+ void continuationsDontLeak();
+ void cancelAfterFinishWithContinuations();
+
+ void unwrap();
+
+private:
+ using size_type = std::vector<int>::size_type;
+
+ static void testSingleResult(const UniquePtr &p);
+ static void testSingleResult(const std::vector<int> &v);
+ template<class T>
+ static void testSingleResult(const T &unknown);
+ template<class T>
+ static void testFutureTaken(QFuture<T> &noMoreFuture);
+ template<class T>
+ static void testTakeResults(QFuture<T> future, size_type resultCount);
};
+class IntResultsCleaner
+{
+public:
+ IntResultsCleaner(QtPrivate::ResultStoreBase &s) : store(s) { }
+ ~IntResultsCleaner() { store.clear<int>(); }
+
+private:
+ QtPrivate::ResultStoreBase &store;
+};
+
+void tst_QFuture::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<QFuture<int>::const_iterator>();
+ QTestPrivate::testEqualityOperatorsCompile<QFuture<QString>::const_iterator>();
+}
+
void tst_QFuture::resultStore()
{
int int0 = 0;
@@ -84,7 +288,9 @@ void tst_QFuture::resultStore()
int int2 = 2;
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
QCOMPARE(store.begin(), store.end());
QCOMPARE(store.resultAt(0), store.end());
QCOMPARE(store.resultAt(1), store.end());
@@ -92,7 +298,9 @@ void tst_QFuture::resultStore()
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResult(1, &int1);
QtPrivate::ResultIteratorBase it = store.begin();
@@ -110,11 +318,13 @@ void tst_QFuture::resultStore()
QVERIFY(it == store.end());
}
- QVector<int> vec0 = QVector<int>() << 2 << 3;
- QVector<int> vec1 = QVector<int>() << 4 << 5;
+ QList<int> vec0 = QList<int>() << 2 << 3;
+ QList<int> vec1 = QList<int>() << 4 << 5;
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(-1, &vec0, 2);
store.addResults(-1, &vec1, 2);
QtPrivate::ResultIteratorBase it = store.begin();
@@ -137,7 +347,9 @@ void tst_QFuture::resultStore()
QCOMPARE(it, store.end());
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec1, 2);
store.addResult(-1, &int1);
@@ -168,7 +380,9 @@ void tst_QFuture::resultStore()
QCOMPARE(store.resultAt(4), store.end());
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(-1, &int1);
@@ -198,7 +412,9 @@ void tst_QFuture::resultStore()
QCOMPARE(store.resultAt(3).value<int>(), int1);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(200, &int1);
@@ -210,7 +426,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(1, &int1);
store.addResult(0, &int0);
store.addResult(-1, &int2);
@@ -221,7 +439,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
QCOMPARE(store.contains(0), false);
QCOMPARE(store.contains(1), false);
QCOMPARE(store.contains(INT_MAX), false);
@@ -229,7 +449,9 @@ void tst_QFuture::resultStore()
{
// Test filter mode, where "gaps" in the result array aren't allowed.
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -263,7 +485,9 @@ void tst_QFuture::resultStore()
{
// test canceled results
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -300,7 +524,9 @@ void tst_QFuture::resultStore()
{
// test addResult return value
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -346,7 +572,9 @@ void tst_QFuture::resultStore()
{
// test resultCount in non-filtered mode. It should always be possible
// to iterate through the results 0 to resultCount.
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(0, &int0);
QCOMPARE(store.count(), 1);
@@ -360,7 +588,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(2, &int0);
QCOMPARE(store.count(), 0);
@@ -372,7 +602,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -384,7 +616,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -392,7 +626,9 @@ void tst_QFuture::resultStore()
QCOMPARE(store.count(), 4);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -404,7 +640,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -417,7 +655,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -427,7 +667,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -440,7 +682,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(1, &int0);
store.addResult(3, &int0);
store.addResults(6, &vec0);
@@ -455,7 +699,9 @@ void tst_QFuture::resultStore()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(1, &int0);
store.addResult(3, &int0);
@@ -483,7 +729,9 @@ void tst_QFuture::resultStore()
QCOMPARE(store.contains(7), false);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addCanceledResult(0);
QCOMPARE(store.contains(0), false);
@@ -519,6 +767,19 @@ void tst_QFuture::future()
QCOMPARE(intFuture2.isFinished(), true);
}
+void tst_QFuture::futureToVoid()
+{
+ QPromise<int> p;
+ QFuture<int> future = p.future();
+
+ p.start();
+ p.setProgressValue(42);
+ p.finish();
+
+ QFuture<void> voidFuture = QFuture<void>(future);
+ QCOMPARE(voidFuture.progressValue(), 42);
+}
+
class IntResult : public QFutureInterface<int>
{
public:
@@ -563,7 +824,7 @@ void tst_QFuture::futureInterface()
{
QFutureInterface<int> i;
i.reportStarted();
- i.reportResult(10);
+ QVERIFY(i.reportResult(10));
future = i.future();
i.reportFinished();
}
@@ -584,7 +845,7 @@ void tst_QFuture::futureInterface()
QCOMPARE(intFuture.isStarted(), true);
QCOMPARE(intFuture.isFinished(), false);
- result.reportFinished(&value);
+ QVERIFY(result.reportFinished(&value));
QCOMPARE(intFuture.isStarted(), true);
QCOMPARE(intFuture.isFinished(), true);
@@ -608,6 +869,36 @@ void tst_QFuture::futureInterface()
VoidResult a;
a.run().waitForFinished();
}
+
+ {
+ QFutureInterface<int> i1;
+ QVERIFY(i1.reportResult(1));
+ QFutureInterface<int> i2;
+ QVERIFY(i2.reportResult(2));
+ swap(i1, i2); // ADL must resolve this
+ QCOMPARE(i1.resultReference(0), 2);
+ QCOMPARE(i2.resultReference(0), 1);
+ }
+
+ {
+ QFutureInterface<int> fi;
+ fi.reportStarted();
+ QVERIFY(!fi.reportResults(QList<int> {}));
+ fi.reportFinished();
+
+ QVERIFY(fi.results().empty());
+ }
+
+ {
+ QFutureInterface<int> fi;
+ fi.reportStarted();
+ QList<int> values = { 1, 2, 3 };
+ QVERIFY(fi.reportResults(values));
+ QVERIFY(!fi.reportResults(QList<int> {}));
+ fi.reportFinished();
+
+ QCOMPARE(fi.results(), values);
+ }
}
template <typename T>
@@ -716,7 +1007,40 @@ void tst_QFuture::cancel()
result = 3;
futureInterface.reportResult(&result);
futureInterface.reportFinished();
- QCOMPARE(f.results(), QList<int>());
+ QVERIFY(f.results().isEmpty());
+ }
+}
+
+void tst_QFuture::cancelAndFinish()
+{
+ {
+ QFutureInterface<void> fi;
+
+ fi.reportStarted();
+ fi.cancelAndFinish();
+
+ QVERIFY(fi.isStarted());
+ QVERIFY(!fi.isRunning());
+ QVERIFY(!fi.isSuspended());
+ QVERIFY(!fi.isSuspending());
+ QVERIFY(fi.isCanceled());
+ QVERIFY(fi.isFinished());
+ }
+
+ // The same with suspended state
+ {
+ QFutureInterface<void> fi;
+
+ fi.reportStarted();
+ fi.setSuspended(true);
+ fi.cancelAndFinish();
+
+ QVERIFY(fi.isStarted());
+ QVERIFY(!fi.isRunning());
+ QVERIFY(!fi.isSuspended());
+ QVERIFY(!fi.isSuspending());
+ QVERIFY(fi.isCanceled());
+ QVERIFY(fi.isFinished());
}
}
@@ -767,15 +1091,15 @@ void tst_QFuture::multipleResults()
int result;
result = 1;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
QCOMPARE(f.resultAt(0), 1);
result = 2;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
QCOMPARE(f.resultAt(1), 2);
result = 3;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
result = 4;
a.reportFinished(&result);
@@ -786,13 +1110,13 @@ void tst_QFuture::multipleResults()
QList<int> fasit = QList<int>() << 1 << 2 << 3 << 4;
{
QList<int> results;
- foreach(int result, f)
+ for (int result : std::as_const(f))
results.append(result);
QCOMPARE(results, fasit);
}
{
QList<int> results;
- foreach(int result, copy)
+ for (int result : std::as_const(copy))
results.append(result);
QCOMPARE(results, fasit);
}
@@ -816,16 +1140,16 @@ void tst_QFuture::indexedResults()
QChar result;
result = 'B';
- Interface.reportResult(&result, 1);
+ QVERIFY(Interface.reportResult(&result, 1));
QCOMPARE(f.resultAt(1), result);
result = 'A';
- Interface.reportResult(&result, 0);
+ QVERIFY(Interface.reportResult(&result, 0));
QCOMPARE(f.resultAt(0), result);
result = 'C';
- Interface.reportResult(&result); // no index
+ QVERIFY(Interface.reportResult(&result)); // no index
QCOMPARE(f.resultAt(2), result);
Interface.reportFinished();
@@ -841,22 +1165,22 @@ void tst_QFuture::indexedResults()
int result;
result = 0;
- Interface.reportResult(&result, 0);
+ QVERIFY(Interface.reportResult(&result, 0));
QVERIFY(f.isResultReadyAt(0));
QCOMPARE(f.resultAt(0), 0);
result = 3;
- Interface.reportResult(&result, 3);
+ QVERIFY(Interface.reportResult(&result, 3));
QVERIFY(f.isResultReadyAt(3));
QCOMPARE(f.resultAt(3), 3);
result = 2;
- Interface.reportResult(&result, 2);
+ QVERIFY(Interface.reportResult(&result, 2));
QVERIFY(f.isResultReadyAt(2));
QCOMPARE(f.resultAt(2), 2);
result = 4;
- Interface.reportResult(&result); // no index
+ QVERIFY(Interface.reportResult(&result)); // no index
QVERIFY(f.isResultReadyAt(4));
QCOMPARE(f.resultAt(4), 4);
@@ -887,6 +1211,55 @@ void tst_QFuture::progress()
QCOMPARE (f.progressValue(), 50);
}
+void tst_QFuture::setProgressRange()
+{
+ QFutureInterface<int> i;
+
+ QCOMPARE(i.progressMinimum(), 0);
+ QCOMPARE(i.progressMaximum(), 0);
+
+ i.setProgressRange(10, 5);
+
+ QCOMPARE(i.progressMinimum(), 10);
+ QCOMPARE(i.progressMaximum(), 10);
+
+ i.setProgressRange(5, 10);
+
+ QCOMPARE(i.progressMinimum(), 5);
+ QCOMPARE(i.progressMaximum(), 10);
+}
+
+void tst_QFuture::progressWithRange()
+{
+ QFutureInterface<int> i;
+ QFuture<int> f;
+
+ i.reportStarted();
+ f = i.future();
+
+ QCOMPARE(i.progressValue(), 0);
+
+ i.setProgressRange(5, 10);
+
+ QCOMPARE(i.progressValue(), 5);
+
+ i.setProgressValue(20);
+
+ QCOMPARE(i.progressValue(), 5);
+
+ i.setProgressValue(9);
+
+ QCOMPARE(i.progressValue(), 9);
+
+ i.setProgressRange(5, 7);
+
+ QCOMPARE(i.progressValue(), 5);
+
+ i.reportFinished();
+
+ QCOMPARE(f.progressValue(), 5);
+}
+
void tst_QFuture::progressText()
{
QFutureInterface<void> i;
@@ -913,7 +1286,7 @@ void tst_QFuture::resultsAfterFinished()
QCOMPARE(f.resultCount(), 0);
result = 1;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
QCOMPARE(f.resultAt(0), 1);
a.reportFinished();
@@ -921,7 +1294,7 @@ void tst_QFuture::resultsAfterFinished()
QCOMPARE(f.resultAt(0), 1);
QCOMPARE(f.resultCount(), 1);
result = 2;
- a.reportResult(&result);
+ QVERIFY(!a.reportResult(&result));
QCOMPARE(f.resultCount(), 1);
}
// cancel it
@@ -934,7 +1307,7 @@ void tst_QFuture::resultsAfterFinished()
QCOMPARE(f.resultCount(), 0);
result = 1;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
QCOMPARE(f.resultAt(0), 1);
QCOMPARE(f.resultCount(), 1);
@@ -944,7 +1317,7 @@ void tst_QFuture::resultsAfterFinished()
QCOMPARE(f.resultCount(), 1);
result = 2;
- a.reportResult(&result);
+ QVERIFY(!a.reportResult(&result));
a.reportFinished();
}
}
@@ -957,9 +1330,9 @@ void tst_QFuture::resultsAsList()
int result;
result = 1;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
result = 2;
- a.reportResult(&result);
+ QVERIFY(a.reportResult(&result));
a.reportFinished();
@@ -967,25 +1340,6 @@ void tst_QFuture::resultsAsList()
QCOMPARE(results, QList<int>() << 1 << 2);
}
-/*
- Test that QFuture<T> can be implicitly converted to T
-*/
-void tst_QFuture::implicitConversions()
-{
- QFutureInterface<QString> iface;
- iface.reportStarted();
-
- QFuture<QString> f(&iface);
-
- const QString input("FooBar 2000");
- iface.reportFinished(&input);
-
- const QString result = f;
- QCOMPARE(result, input);
- QCOMPARE(QString(f), input);
- QCOMPARE(static_cast<QString>(f), input);
-}
-
void tst_QFuture::iterators()
{
{
@@ -1013,16 +1367,16 @@ void tst_QFuture::iterators()
QFuture<int>::const_iterator i1 = f.begin(), i2 = i1 + 1;
QFuture<int>::const_iterator c1 = i1, c2 = c1 + 1;
- QCOMPARE(i1, i1);
- QCOMPARE(i1, c1);
- QCOMPARE(c1, i1);
- QCOMPARE(c1, c1);
- QCOMPARE(i2, i2);
- QCOMPARE(i2, c2);
- QCOMPARE(c2, i2);
- QCOMPARE(c2, c2);
- QCOMPARE(1 + i1, i1 + 1);
- QCOMPARE(1 + c1, c1 + 1);
+ QT_TEST_EQUALITY_OPS(i1, i1, true);
+ QT_TEST_EQUALITY_OPS(i1, c1, true);
+ QT_TEST_EQUALITY_OPS(c1, i1, true);
+ QT_TEST_EQUALITY_OPS(c1, c1, true);
+ QT_TEST_EQUALITY_OPS(i2, i2, true);
+ QT_TEST_EQUALITY_OPS(i2, c2, true);
+ QT_TEST_EQUALITY_OPS(c2, i2, true);
+ QT_TEST_EQUALITY_OPS(c2, c2, true);
+ QT_TEST_EQUALITY_OPS(1 + i1, i1 + 1, true);
+ QT_TEST_EQUALITY_OPS(1 + c1, c1 + 1, true);
QVERIFY(i1 != i2);
QVERIFY(i1 != c2);
@@ -1034,13 +1388,13 @@ void tst_QFuture::iterators()
QVERIFY(c2 != c1);
int x1 = *i1;
- Q_UNUSED(x1);
+ Q_UNUSED(x1)
int x2 = *i2;
- Q_UNUSED(x2);
+ Q_UNUSED(x2)
int y1 = *c1;
- Q_UNUSED(y1);
+ Q_UNUSED(y1)
int y2 = *c2;
- Q_UNUSED(y2);
+ Q_UNUSED(y2)
}
{
@@ -1092,10 +1446,10 @@ void tst_QFuture::iterators()
QCOMPARE(x1, y1);
QCOMPARE(x2, y2);
- int i1Size = i1->size();
- int i2Size = i2->size();
- int c1Size = c1->size();
- int c2Size = c2->size();
+ auto i1Size = i1->size();
+ auto i2Size = i2->size();
+ auto c1Size = c1->size();
+ auto c2Size = c2->size();
QCOMPARE(i1Size, c1Size);
QCOMPARE(i2Size, c2Size);
@@ -1144,6 +1498,53 @@ void tst_QFuture::iterators()
}
}
}
+void tst_QFuture::iteratorsThread()
+{
+ const int expectedResultCount = 10;
+ QFutureInterface<int> futureInterface;
+
+ // Create result producer thread. The results are
+ // produced with delays in order to make the consumer
+ // wait.
+ QSemaphore sem;
+ LambdaThread thread = {[=, &futureInterface, &sem](){
+ for (int i = 1; i <= expectedResultCount; i += 2) {
+ int result = i;
+ futureInterface.reportResult(&result);
+ result = i + 1;
+ futureInterface.reportResult(&result);
+ }
+
+ sem.acquire(2);
+ futureInterface.reportFinished();
+ }};
+
+ futureInterface.reportStarted();
+ QFuture<int> future = futureInterface.future();
+
+ // Iterate over results while the thread is producing them.
+ thread.start();
+ int resultCount = 0;
+ int resultSum = 0;
+ for (int result : future) {
+ sem.release();
+ ++resultCount;
+ resultSum += result;
+ }
+ thread.wait();
+
+ QCOMPARE(resultCount, expectedResultCount);
+ QCOMPARE(resultSum, expectedResultCount * (expectedResultCount + 1) / 2);
+
+ // Reverse iterate
+ resultSum = 0;
+ QFutureIterator<int> it(future);
+ it.toBack();
+ while (it.hasPrevious())
+ resultSum += it.previous();
+
+ QCOMPARE(resultSum, expectedResultCount * (expectedResultCount + 1) / 2);
+}
class SignalSlotObject : public QObject
{
@@ -1190,6 +1591,9 @@ public:
QSet<int> reportedProgress;
};
+#if QT_DEPRECATED_SINCE(6, 0)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
void tst_QFuture::pause()
{
QFutureInterface<void> Interface;
@@ -1210,6 +1614,102 @@ void tst_QFuture::pause()
Interface.reportFinished();
}
+void tst_QFuture::suspendCheckPaused()
+{
+ QFutureInterface<void> interface;
+
+ interface.reportStarted();
+ QFuture<void> f = interface.future();
+ QVERIFY(!f.isSuspended());
+
+ interface.reportSuspended();
+ QVERIFY(!f.isSuspended());
+
+ f.pause();
+ QVERIFY(!f.isSuspended());
+ QVERIFY(f.isPaused());
+
+ // resume when still pausing
+ f.resume();
+ QVERIFY(!f.isSuspended());
+ QVERIFY(!f.isPaused());
+
+ // pause again
+ f.pause();
+ QVERIFY(!f.isSuspended());
+ QVERIFY(f.isPaused());
+
+ interface.reportSuspended();
+ QVERIFY(f.isSuspended());
+ QVERIFY(f.isPaused());
+
+ // resume after suspended
+ f.resume();
+ QVERIFY(!f.isSuspended());
+ QVERIFY(!f.isPaused());
+
+ // pause again and cancel
+ f.pause();
+ interface.reportSuspended();
+
+ interface.reportCanceled();
+ QVERIFY(!f.isSuspended());
+ QVERIFY(!f.isPaused());
+ QVERIFY(f.isCanceled());
+
+ interface.reportFinished();
+}
+
+QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 0)
+
+void tst_QFuture::suspend()
+{
+ QFutureInterface<void> interface;
+
+ interface.reportStarted();
+ QFuture<void> f = interface.future();
+ QVERIFY(!f.isSuspended());
+
+ interface.reportSuspended();
+ QVERIFY(!f.isSuspended());
+ QVERIFY(!f.isSuspending());
+
+ f.suspend();
+ QVERIFY(f.isSuspending());
+ QVERIFY(!f.isSuspended());
+
+ // resume when still suspending
+ f.resume();
+ QVERIFY(!f.isSuspending());
+ QVERIFY(!f.isSuspended());
+
+ // suspend again
+ f.suspend();
+ QVERIFY(f.isSuspending());
+ QVERIFY(!f.isSuspended());
+
+ interface.reportSuspended();
+ QVERIFY(!f.isSuspending());
+ QVERIFY(f.isSuspended());
+
+ // resume after suspended
+ f.resume();
+ QVERIFY(!f.isSuspending());
+ QVERIFY(!f.isSuspended());
+
+ // suspend again and cancel
+ f.suspend();
+ interface.reportSuspended();
+
+ interface.reportCanceled();
+ QVERIFY(!f.isSuspending());
+ QVERIFY(!f.isSuspended());
+ QVERIFY(f.isCanceled());
+
+ interface.reportFinished();
+}
+
class ResultObject : public QObject
{
Q_OBJECT
@@ -1254,12 +1754,10 @@ void tst_QFuture::voidConversions()
QFuture<int> intFuture(&iface);
int value = 10;
- iface.reportFinished(&value);
+ QVERIFY(iface.reportFinished(&value));
QFuture<void> voidFuture(intFuture);
voidFuture = intFuture;
-
- QVERIFY(voidFuture == intFuture);
}
{
@@ -1269,7 +1767,7 @@ void tst_QFuture::voidConversions()
iface.reportStarted();
QFuture<QList<int> > listFuture(&iface);
- iface.reportResult(QList<int>() << 1 << 2 << 3);
+ QVERIFY(iface.reportResult(QList<int>() << 1 << 2 << 3));
voidFuture = listFuture;
}
QCOMPARE(voidFuture.resultCount(), 0);
@@ -1324,6 +1822,23 @@ QFuture<void> createDerivedExceptionFuture()
return f;
}
+struct TestException
+{
+};
+
+QFuture<int> createCustomExceptionFuture()
+{
+ QFutureInterface<int> i;
+ i.reportStarted();
+ QFuture<int> f = i.future();
+ int r = 0;
+ i.reportResult(r);
+ auto exception = std::make_exception_ptr(TestException());
+ i.reportException(exception);
+ i.reportFinished();
+ return f;
+}
+
void tst_QFuture::exceptions()
{
// test throwing from waitForFinished
@@ -1379,7 +1894,7 @@ void tst_QFuture::exceptions()
bool caught = false;
try {
foreach (int e, f.results()) {
- Q_UNUSED(e);
+ Q_UNUSED(e)
QFAIL("did not get exception");
}
} catch (QException &) {
@@ -1408,6 +1923,18 @@ void tst_QFuture::exceptions()
}
QVERIFY(caught);
}
+
+ // Custom exceptions
+ {
+ QFuture<int> f = createCustomExceptionFuture();
+ bool caught = false;
+ try {
+ f.result();
+ } catch (const TestException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ }
}
class MyClass
@@ -1434,7 +1961,7 @@ void tst_QFuture::nestedExceptions()
{
try {
MyClass m;
- Q_UNUSED(m);
+ Q_UNUSED(m)
throw 0;
} catch (int) {}
@@ -1445,7 +1972,7 @@ void tst_QFuture::nestedExceptions()
void tst_QFuture::nonGlobalThreadPool()
{
- static Q_CONSTEXPR int Answer = 42;
+ static constexpr int Answer = 42;
struct UselessTask : QRunnable, QFutureInterface<int>
{
@@ -1462,7 +1989,7 @@ void tst_QFuture::nonGlobalThreadPool()
void run() override
{
const int ms = 100 + (QRandomGenerator::global()->bounded(100) - 100/2);
- QThread::msleep(ms);
+ QThread::sleep(std::chrono::milliseconds{ms});
reportResult(Answer);
reportFinished();
}
@@ -1472,7 +1999,7 @@ void tst_QFuture::nonGlobalThreadPool()
const int numTasks = QThread::idealThreadCount();
- QVector<QFuture<int> > futures;
+ QList<QFuture<int>> futures;
futures.reserve(numTasks);
for (int i = 0; i < numTasks; ++i)
@@ -1490,5 +2017,3355 @@ void tst_QFuture::nonGlobalThreadPool()
}
}
+void tst_QFuture::then()
+{
+ {
+ struct Add
+ {
+
+ static int addTwo(int arg) { return arg + 2; }
+
+ int operator()(int arg) const { return arg + 3; }
+ };
+
+ QFutureInterface<int> promise;
+ QFuture<int> then = promise.future()
+ .then([](int res) { return res + 1; }) // lambda
+ .then(Add::addTwo) // function
+ .then(Add()); // functor
+
+ promise.reportStarted();
+ QVERIFY(!then.isStarted());
+ QVERIFY(!then.isFinished());
+
+ const int result = 0;
+ promise.reportResult(result);
+ promise.reportFinished();
+
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QCOMPARE(then.result(), result + 6);
+ }
+
+ // then() on a ready future
+ {
+ QFutureInterface<int> promise;
+ promise.reportStarted();
+
+ const int result = 0;
+ promise.reportResult(result);
+ promise.reportFinished();
+
+ QFuture<int> then = promise.future()
+ .then([](int res1) { return res1 + 1; })
+ .then([](int res2) { return res2 + 2; })
+ .then([](int res3) { return res3 + 3; });
+
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QCOMPARE(then.result(), result + 6);
+ }
+
+ // Continuation of QFuture<void>
+ {
+ int result = 0;
+ QFutureInterface<void> promise;
+ QFuture<void> then = promise.future()
+ .then([&]() { result += 1; })
+ .then([&]() { result += 2; })
+ .then([&]() { result += 3; });
+
+ promise.reportStarted();
+ QVERIFY(!then.isStarted());
+ QVERIFY(!then.isFinished());
+ promise.reportFinished();
+
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QCOMPARE(result, 6);
+ }
+
+ // Continuation returns QFuture<void>
+ {
+ QFutureInterface<int> promise;
+ int value;
+ QFuture<void> then =
+ promise.future().then([](int res) { return res * 2; }).then([&](int prevResult) {
+ value = prevResult;
+ });
+
+ promise.reportStarted();
+ QVERIFY(!then.isStarted());
+ QVERIFY(!then.isFinished());
+
+ const int result = 5;
+ promise.reportResult(result);
+ promise.reportFinished();
+
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QCOMPARE(value, result * 2);
+ }
+
+ // Continuations taking a QFuture argument.
+ {
+ int value = 0;
+ QFutureInterface<int> promise;
+ QFuture<void> then = promise.future()
+ .then([](QFuture<int> f1) { return f1.result() + 1; })
+ .then([&](QFuture<int> f2) { value = f2.result() + 2; })
+ .then([&](QFuture<void> f3) {
+ QVERIFY(f3.isFinished());
+ value += 3;
+ });
+
+ promise.reportStarted();
+ QVERIFY(!then.isStarted());
+ QVERIFY(!then.isFinished());
+
+ const int result = 0;
+ promise.reportResult(result);
+ promise.reportFinished();
+
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QCOMPARE(value, 6);
+ }
+
+ // Continuations use a new thread
+ {
+ Qt::HANDLE threadId1 = nullptr;
+ Qt::HANDLE threadId2 = nullptr;
+ QFutureInterface<void> promise;
+ QFuture<void> then = promise.future()
+ .then(QtFuture::Launch::Async,
+ [&]() { threadId1 = QThread::currentThreadId(); })
+ .then([&]() { threadId2 = QThread::currentThreadId(); });
+
+ promise.reportStarted();
+ QVERIFY(!then.isStarted());
+ QVERIFY(!then.isFinished());
+
+ promise.reportFinished();
+
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QVERIFY(threadId1 != QThread::currentThreadId());
+ QVERIFY(threadId2 != QThread::currentThreadId());
+ QVERIFY(threadId1 == threadId2);
+ }
+
+ // Continuation inherits the launch policy of its parent (QtFuture::Launch::Sync)
+ {
+ Qt::HANDLE threadId1 = nullptr;
+ Qt::HANDLE threadId2 = nullptr;
+ QFutureInterface<void> promise;
+ QFuture<void> then = promise.future()
+ .then(QtFuture::Launch::Sync,
+ [&]() { threadId1 = QThread::currentThreadId(); })
+ .then(QtFuture::Launch::Inherit,
+ [&]() { threadId2 = QThread::currentThreadId(); });
+
+ promise.reportStarted();
+ QVERIFY(!then.isStarted());
+ QVERIFY(!then.isFinished());
+
+ promise.reportFinished();
+
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QVERIFY(threadId1 == QThread::currentThreadId());
+ QVERIFY(threadId2 == QThread::currentThreadId());
+ QVERIFY(threadId1 == threadId2);
+ }
+
+ // Continuation inherits the launch policy of its parent (QtFuture::Launch::Async)
+ {
+ Qt::HANDLE threadId1 = nullptr;
+ Qt::HANDLE threadId2 = nullptr;
+ QFutureInterface<void> promise;
+ QFuture<void> then = promise.future()
+ .then(QtFuture::Launch::Async,
+ [&]() { threadId1 = QThread::currentThreadId(); })
+ .then(QtFuture::Launch::Inherit,
+ [&]() { threadId2 = QThread::currentThreadId(); });
+
+ promise.reportStarted();
+ QVERIFY(!then.isStarted());
+ QVERIFY(!then.isFinished());
+
+ promise.reportFinished();
+
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QVERIFY(threadId1 != QThread::currentThreadId());
+ QVERIFY(threadId2 != QThread::currentThreadId());
+ }
+
+ // Continuations use a custom thread pool
+ {
+ QFutureInterface<void> promise;
+ QThreadPool pool;
+ QVERIFY(pool.waitForDone(0)); // pool is not busy yet
+ QSemaphore semaphore;
+ QFuture<void> then = promise.future().then(&pool, [&]() { semaphore.acquire(); });
+
+ promise.reportStarted();
+ promise.reportFinished();
+
+ // Make sure the custom thread pool is busy on running the continuation
+ QVERIFY(!pool.waitForDone(0));
+ semaphore.release();
+ then.waitForFinished();
+
+ QVERIFY(then.isStarted());
+ QVERIFY(then.isFinished());
+ QCOMPARE(then.d.threadPool(), &pool);
+ }
+
+ // Continuation inherits parent's thread pool
+ {
+ Qt::HANDLE threadId1 = nullptr;
+ Qt::HANDLE threadId2 = nullptr;
+ QFutureInterface<void> promise;
+
+ QThreadPool pool;
+ QFuture<void> then1 = promise.future().then(&pool, [&]() {
+ threadId1 = QThread::currentThreadId();
+ });
+
+ promise.reportStarted();
+ promise.reportFinished();
+
+ then1.waitForFinished();
+ QVERIFY(pool.waitForDone()); // The pool is not busy after the first continuation is done
+
+ QSemaphore semaphore;
+ QFuture<void> then2 = then1.then(QtFuture::Launch::Inherit, [&]() {
+ semaphore.acquire();
+ threadId2 = QThread::currentThreadId();
+ });
+
+ QVERIFY(!pool.waitForDone(0)); // The pool is busy running the 2nd continuation
+
+ semaphore.release();
+ then2.waitForFinished();
+
+ QVERIFY(then2.isStarted());
+ QVERIFY(then2.isFinished());
+ QCOMPARE(then1.d.threadPool(), then2.d.threadPool());
+ QCOMPARE(then2.d.threadPool(), &pool);
+ 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>
+bool runThenForMoveOnly(Callable &&callable)
+{
+ QFutureInterface<Type> promise;
+ auto future = promise.future();
+
+ auto then = future.then(std::forward<Callable>(callable));
+
+ promise.reportStarted();
+ if constexpr (!std::is_same_v<Type, void>)
+ promise.reportAndMoveResult(std::make_unique<int>(42));
+ promise.reportFinished();
+ then.waitForFinished();
+
+ bool success = true;
+ if constexpr (!std::is_same_v<decltype(then), QFuture<void>>)
+ success &= *then.takeResult() == 42;
+
+ if constexpr (!std::is_same_v<Type, void>)
+ success &= !future.isValid();
+
+ return success;
+}
+
+void tst_QFuture::thenForMoveOnlyTypes()
+{
+ QVERIFY(runThenForMoveOnly<UniquePtr>([](UniquePtr res) { return res; }));
+ QVERIFY(runThenForMoveOnly<UniquePtr>([](UniquePtr res) { Q_UNUSED(res); }));
+ QVERIFY(runThenForMoveOnly<UniquePtr>([](QFuture<UniquePtr> res) { return res.takeResult(); }));
+ QVERIFY(runThenForMoveOnly<void>([] { return std::make_unique<int>(42); }));
+}
+
+template<class T>
+QFuture<T> createCanceledFuture()
+{
+ QFutureInterface<T> promise;
+ promise.reportStarted();
+ promise.reportCanceled();
+ promise.reportFinished();
+ return promise.future();
+}
+
+void tst_QFuture::thenOnCanceledFuture()
+{
+ // Continuations on a canceled future
+ {
+ int thenResult = 0;
+ QFuture<void> then = createCanceledFuture<void>().then([&]() { ++thenResult; }).then([&]() {
+ ++thenResult;
+ });
+
+ QVERIFY(then.isCanceled());
+ QCOMPARE(thenResult, 0);
+ }
+
+ // QFuture gets canceled after continuations are set
+ {
+ QFutureInterface<void> promise;
+
+ int thenResult = 0;
+ QFuture<void> then =
+ promise.future().then([&]() { ++thenResult; }).then([&]() { ++thenResult; });
+
+ promise.reportStarted();
+ promise.reportCanceled();
+ promise.reportFinished();
+
+ QVERIFY(then.isCanceled());
+ QCOMPARE(thenResult, 0);
+ }
+
+ // Same with QtFuture::Launch::Async
+
+ // Continuations on a canceled future
+ {
+ int thenResult = 0;
+ QFuture<void> then = createCanceledFuture<void>()
+ .then(QtFuture::Launch::Async, [&]() { ++thenResult; })
+ .then([&]() { ++thenResult; });
+
+ QVERIFY(then.isCanceled());
+ QCOMPARE(thenResult, 0);
+ }
+
+ // QFuture gets canceled after continuations are set
+ {
+ QFutureInterface<void> promise;
+
+ int thenResult = 0;
+ QFuture<void> then =
+ promise.future().then(QtFuture::Launch::Async, [&]() { ++thenResult; }).then([&]() {
+ ++thenResult;
+ });
+
+ promise.reportStarted();
+ promise.reportCanceled();
+ promise.reportFinished();
+
+ QVERIFY(then.isCanceled());
+ QCOMPARE(thenResult, 0);
+ }
+}
+
+#ifndef QT_NO_EXCEPTIONS
+void tst_QFuture::thenOnExceptionFuture()
+{
+ {
+ QFutureInterface<int> promise;
+
+ int thenResult = 0;
+ QFuture<void> then = promise.future().then([&](int res) { thenResult = res; });
+
+ promise.reportStarted();
+ QException e;
+ promise.reportException(e);
+ promise.reportFinished();
+
+ bool caught = false;
+ try {
+ then.waitForFinished();
+ } catch (QException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ QCOMPARE(thenResult, 0);
+ }
+
+ // Exception handled inside the continuation
+ {
+ QFutureInterface<int> promise;
+
+ bool caught = false;
+ bool caughtByContinuation = false;
+ bool success = false;
+ int thenResult = 0;
+ QFuture<void> then = promise.future()
+ .then([&](QFuture<int> res) {
+ try {
+ thenResult = res.result();
+ } catch (QException &) {
+ caughtByContinuation = true;
+ }
+ })
+ .then([&]() { success = true; });
+
+ promise.reportStarted();
+ QException e;
+ promise.reportException(e);
+ promise.reportFinished();
+
+ try {
+ then.waitForFinished();
+ } catch (QException &) {
+ caught = true;
+ }
+
+ QCOMPARE(thenResult, 0);
+ QVERIFY(!caught);
+ QVERIFY(caughtByContinuation);
+ QVERIFY(success);
+ }
+
+ // Exception future
+ {
+ QFutureInterface<int> promise;
+ promise.reportStarted();
+ QException e;
+ promise.reportException(e);
+ promise.reportFinished();
+
+ int thenResult = 0;
+ QFuture<void> then = promise.future().then([&](int res) { thenResult = res; });
+
+ bool caught = false;
+ try {
+ then.waitForFinished();
+ } catch (QException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ QCOMPARE(thenResult, 0);
+ }
+
+ // Same with QtFuture::Launch::Async
+ {
+ QFutureInterface<int> promise;
+
+ int thenResult = 0;
+ QFuture<void> then =
+ promise.future().then(QtFuture::Launch::Async, [&](int res) { thenResult = res; });
+
+ promise.reportStarted();
+ QException e;
+ promise.reportException(e);
+ promise.reportFinished();
+
+ bool caught = false;
+ try {
+ then.waitForFinished();
+ } catch (QException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ QCOMPARE(thenResult, 0);
+ }
+
+ // Exception future
+ {
+ QFutureInterface<int> promise;
+ promise.reportStarted();
+ QException e;
+ promise.reportException(e);
+ promise.reportFinished();
+
+ int thenResult = 0;
+ QFuture<void> then =
+ promise.future().then(QtFuture::Launch::Async, [&](int res) { thenResult = res; });
+
+ bool caught = false;
+ try {
+ then.waitForFinished();
+ } catch (QException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ QCOMPARE(thenResult, 0);
+ }
+}
+
+template<class Exception, bool hasTestMsg = false>
+QFuture<void> createExceptionContinuation(QtFuture::Launch policy = QtFuture::Launch::Sync)
+{
+ QFutureInterface<void> promise;
+
+ auto then = promise.future().then(policy, [] {
+ if constexpr (hasTestMsg)
+ throw Exception("TEST");
+ else
+ throw Exception();
+ });
+
+ promise.reportStarted();
+ promise.reportFinished();
+
+ return then;
+}
+
+void tst_QFuture::thenThrows()
+{
+ // Continuation throws a QException
+ {
+ auto future = createExceptionContinuation<QException>();
+
+ bool caught = false;
+ try {
+ future.waitForFinished();
+ } catch (const QException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ }
+
+ // Continuation throws an exception derived from QException
+ {
+ auto future = createExceptionContinuation<DerivedException>();
+
+ bool caught = false;
+ try {
+ future.waitForFinished();
+ } catch (const QException &) {
+ caught = true;
+ } catch (const std::exception &) {
+ QFAIL("The exception should be caught by the above catch block.");
+ }
+
+ QVERIFY(caught);
+ }
+
+ // Continuation throws std::exception
+ {
+ auto future = createExceptionContinuation<std::runtime_error, true>();
+
+ bool caught = false;
+ try {
+ future.waitForFinished();
+ } catch (const QException &) {
+ QFAIL("The exception should be caught by the below catch block.");
+ } catch (const std::exception &e) {
+ QCOMPARE(e.what(), "TEST");
+ caught = true;
+ }
+
+ QVERIFY(caught);
+ }
+
+ // Same with QtFuture::Launch::Async
+ {
+ auto future = createExceptionContinuation<QException>(QtFuture::Launch::Async);
+
+ bool caught = false;
+ try {
+ future.waitForFinished();
+ } catch (const QException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ }
+}
+
+void tst_QFuture::onFailed()
+{
+ // Ready exception void future
+ {
+ int checkpoint = 0;
+ auto future = createExceptionFuture().then([&] { checkpoint = 1; }).onFailed([&] {
+ checkpoint = 2;
+ });
+
+ try {
+ future.waitForFinished();
+ } catch (...) {
+ checkpoint = 3;
+ }
+ QCOMPARE(checkpoint, 2);
+ }
+
+ // std::exception handler
+ {
+ QFutureInterface<int> promise;
+
+ int checkpoint = 0;
+ auto then = promise.future()
+ .then([&](int res) {
+ throw std::exception();
+ return res;
+ })
+ .then([&](int res) { return res + 1; })
+ .onFailed([&](const QException &) {
+ checkpoint = 1;
+ return -1;
+ })
+ .onFailed([&](const std::exception &) {
+ checkpoint = 2;
+ return -1;
+ })
+ .onFailed([&] {
+ checkpoint = 3;
+ return -1;
+ });
+
+ promise.reportStarted();
+ promise.reportResult(1);
+ promise.reportFinished();
+
+ int res = 0;
+ try {
+ res = then.result();
+ } catch (...) {
+ checkpoint = 4;
+ }
+ QCOMPARE(checkpoint, 2);
+ QCOMPARE(res, -1);
+ }
+
+ // then() throws an exception derived from QException
+ {
+ QFutureInterface<int> promise;
+
+ int checkpoint = 0;
+ auto then = promise.future()
+ .then([&](int res) {
+ throw DerivedException();
+ return res;
+ })
+ .then([&](int res) { return res + 1; })
+ .onFailed([&](const QException &) {
+ checkpoint = 1;
+ return -1;
+ })
+ .onFailed([&](const std::exception &) {
+ checkpoint = 2;
+ return -1;
+ })
+ .onFailed([&] {
+ checkpoint = 3;
+ return -1;
+ });
+
+ promise.reportStarted();
+ promise.reportResult(1);
+ promise.reportFinished();
+
+ int res = 0;
+ try {
+ res = then.result();
+ } catch (...) {
+ checkpoint = 4;
+ }
+ QCOMPARE(checkpoint, 1);
+ QCOMPARE(res, -1);
+ }
+
+ // then() throws a custom exception
+ {
+ QFutureInterface<int> promise;
+
+ int checkpoint = 0;
+ auto then = promise.future()
+ .then([&](int res) {
+ throw TestException();
+ return res;
+ })
+ .then([&](int res) { return res + 1; })
+ .onFailed([&](const QException &) {
+ checkpoint = 1;
+ return -1;
+ })
+ .onFailed([&](const std::exception &) {
+ checkpoint = 2;
+ return -1;
+ })
+ .onFailed([&] {
+ checkpoint = 3;
+ return -1;
+ });
+
+ promise.reportStarted();
+ promise.reportResult(1);
+ promise.reportFinished();
+
+ int res = 0;
+ try {
+ res = then.result();
+ } catch (...) {
+ checkpoint = 4;
+ }
+ QCOMPARE(checkpoint, 3);
+ QCOMPARE(res, -1);
+ }
+
+ // Custom exception handler
+ {
+ struct TestException
+ {
+ };
+
+ QFutureInterface<int> promise;
+
+ int checkpoint = 0;
+ auto then = promise.future()
+ .then([&](int res) {
+ throw TestException();
+ return res;
+ })
+ .then([&](int res) { return res + 1; })
+ .onFailed([&](const QException &) {
+ checkpoint = 1;
+ return -1;
+ })
+ .onFailed([&](const TestException &) {
+ checkpoint = 2;
+ return -1;
+ })
+ .onFailed([&] {
+ checkpoint = 3;
+ return -1;
+ });
+
+ promise.reportStarted();
+ promise.reportResult(1);
+ promise.reportFinished();
+
+ int res = 0;
+ try {
+ res = then.result();
+ } catch (...) {
+ checkpoint = 4;
+ }
+ QCOMPARE(checkpoint, 2);
+ QCOMPARE(res, -1);
+ }
+
+ // Handle all exceptions
+ {
+ QFutureInterface<int> promise;
+
+ int checkpoint = 0;
+ auto then = promise.future()
+ .then([&](int res) {
+ throw QException();
+ return res;
+ })
+ .then([&](int res) { return res + 1; })
+ .onFailed([&] {
+ checkpoint = 1;
+ return -1;
+ })
+ .onFailed([&](const QException &) {
+ checkpoint = 2;
+ return -1;
+ });
+
+ promise.reportStarted();
+ promise.reportResult(1);
+ promise.reportFinished();
+
+ int res = 0;
+ try {
+ res = then.result();
+ } catch (...) {
+ checkpoint = 3;
+ }
+ QCOMPARE(checkpoint, 1);
+ QCOMPARE(res, -1);
+ }
+
+ // Handler throws exception
+ {
+ QFutureInterface<int> promise;
+
+ int checkpoint = 0;
+ auto then = promise.future()
+ .then([&](int res) {
+ throw QException();
+ return res;
+ })
+ .then([&](int res) { return res + 1; })
+ .onFailed([&](const QException &) {
+ checkpoint = 1;
+ throw QException();
+ return -1;
+ })
+ .onFailed([&] {
+ checkpoint = 2;
+ return -1;
+ });
+
+ promise.reportStarted();
+ promise.reportResult(1);
+ promise.reportFinished();
+
+ int res = 0;
+ try {
+ res = then.result();
+ } catch (...) {
+ checkpoint = 3;
+ }
+ QCOMPARE(checkpoint, 2);
+ QCOMPARE(res, -1);
+ }
+
+ // No handler for exception
+ {
+ QFutureInterface<int> promise;
+
+ int checkpoint = 0;
+ auto then = promise.future()
+ .then([&](int res) {
+ throw QException();
+ return res;
+ })
+ .then([&](int res) { return res + 1; })
+ .onFailed([&](const std::exception &) {
+ checkpoint = 1;
+ throw std::exception();
+ return -1;
+ })
+ .onFailed([&](QException &) {
+ checkpoint = 2;
+ return -1;
+ });
+
+ promise.reportStarted();
+ promise.reportResult(1);
+ promise.reportFinished();
+
+ int res = 0;
+ try {
+ res = then.result();
+ } catch (...) {
+ checkpoint = 3;
+ }
+ QCOMPARE(checkpoint, 3);
+ QCOMPARE(res, 0);
+ }
+
+ // onFailed on a canceled future
+ {
+ auto future = createCanceledFuture<int>()
+ .then([](int) { return 42; })
+ .onCanceled([] { return -1; })
+ .onFailed([] { return -2; });
+ QCOMPARE(future.result(), -1);
+ }
+}
+
+template<class Callable>
+bool runForCallable(Callable &&handler)
+{
+ QFuture<int> future = createExceptionResultFuture()
+ .then([&](int) { return 1; })
+ .onFailed(std::forward<Callable>(handler));
+
+ int res = 0;
+ try {
+ res = future.result();
+ } catch (...) {
+ return false;
+ }
+ return res == -1;
+}
+
+int foo()
+{
+ return -1;
+}
+
+void tst_QFuture::onFailedTestCallables()
+{
+ QVERIFY(runForCallable([&] { return -1; }));
+ QVERIFY(runForCallable(foo));
+ QVERIFY(runForCallable(&foo));
+
+ std::function<int()> func = foo;
+ QVERIFY(runForCallable(func));
+
+ struct Functor1
+ {
+ int operator()() { return -1; }
+ static int foo() { return -1; }
+ };
+ QVERIFY(runForCallable(Functor1()));
+ QVERIFY(runForCallable(Functor1::foo));
+
+ struct Functor2
+ {
+ int operator()() const { return -1; }
+ static int foo() { return -1; }
+ };
+ QVERIFY(runForCallable(Functor2()));
+
+ struct Functor3
+ {
+ int operator()() const noexcept { return -1; }
+ static int foo() { return -1; }
+ };
+ QVERIFY(runForCallable(Functor3()));
+}
+
+template<class Callable>
+bool runOnFailedForMoveOnly(Callable &&callable)
+{
+ QFutureInterface<UniquePtr> promise;
+ auto future = promise.future();
+
+ auto failedFuture = future.onFailed(std::forward<Callable>(callable));
+
+ promise.reportStarted();
+ QException e;
+ promise.reportException(e);
+ promise.reportFinished();
+
+ return *failedFuture.takeResult() == -1;
+}
+
+void tst_QFuture::onFailedForMoveOnlyTypes()
+{
+ QVERIFY(runOnFailedForMoveOnly([](const QException &) { return std::make_unique<int>(-1); }));
+ QVERIFY(runOnFailedForMoveOnly([] { return std::make_unique<int>(-1); }));
+}
+
+#endif // QT_NO_EXCEPTIONS
+
+void tst_QFuture::onCanceled()
+{
+ // Canceled int future
+ {
+ auto future = createCanceledFuture<int>().then([](int) { return 42; }).onCanceled([] {
+ return -1;
+ });
+ QCOMPARE(future.result(), -1);
+ }
+
+ // Canceled void future
+ {
+ int checkpoint = 0;
+ auto future = createCanceledFuture<void>().then([&] { checkpoint = 42; }).onCanceled([&] {
+ checkpoint = -1;
+ });
+ QCOMPARE(checkpoint, -1);
+ }
+
+ // onCanceled propagates result
+ {
+ QFutureInterface<int> promise;
+ auto future =
+ promise.future().then([](int res) { return res; }).onCanceled([] { return -1; });
+
+ promise.reportStarted();
+ promise.reportResult(42);
+ promise.reportFinished();
+ QCOMPARE(future.result(), 42);
+ }
+
+ // onCanceled propagates move-only result
+ {
+ QFutureInterface<UniquePtr> promise;
+ auto future = promise.future().then([](UniquePtr res) { return res; }).onCanceled([] {
+ return std::make_unique<int>(-1);
+ });
+
+ promise.reportStarted();
+ promise.reportAndMoveResult(std::make_unique<int>(42));
+ promise.reportFinished();
+ QCOMPARE(*future.takeResult(), 42);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // onCanceled propagates exceptions
+ {
+ QFutureInterface<int> promise;
+ auto future = promise.future()
+ .then([](int res) {
+ throw std::runtime_error("error");
+ return res;
+ })
+ .onCanceled([] { return 2; })
+ .onFailed([] { return 3; });
+
+ promise.reportStarted();
+ promise.reportResult(1);
+ promise.reportFinished();
+ QCOMPARE(future.result(), 3);
+ }
+
+ // onCanceled throws
+ {
+ auto future = createCanceledFuture<int>()
+ .then([](int) { return 42; })
+ .onCanceled([] {
+ throw std::runtime_error("error");
+ return -1;
+ })
+ .onFailed([] { return -2; });
+
+ QCOMPARE(future.result(), -2);
+ }
+
+#endif // QT_NO_EXCEPTIONS
+}
+
+void tst_QFuture::cancelContinuations()
+{
+ // The chain is cancelled in the middle of execution of continuations
+ {
+ QPromise<int> promise;
+
+ int checkpoint = 0;
+ auto future = promise.future().then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ promise.future().cancel();
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ promise.start();
+ promise.addResult(42);
+ promise.finish();
+
+ QCOMPARE(future.result(), -1);
+ QCOMPARE(checkpoint, 2);
+ }
+
+ // The chain is cancelled before the execution of continuations
+ {
+ auto f = QtFuture::makeReadyValueFuture(42);
+ f.cancel();
+
+ int checkpoint = 0;
+ auto future = f.then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ QCOMPARE(future.result(), -1);
+ QCOMPARE(checkpoint, 0);
+ }
+
+ // The chain is canceled partially, through an intermediate future
+ {
+ QPromise<int> promise;
+
+ int checkpoint = 0;
+ auto intermediate = promise.future().then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ });
+
+ auto future = intermediate.then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ promise.start();
+ promise.addResult(42);
+
+ // This should cancel only the chain starting from intermediate
+ intermediate.cancel();
+
+ promise.finish();
+
+ QCOMPARE(future.result(), -1);
+ QCOMPARE(checkpoint, 1);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // The chain is cancelled in the middle of execution of continuations,
+ // while there's an exception in the chain, which is handled inside
+ // the continuations.
+ {
+ QPromise<int> promise;
+
+ int checkpoint = 0;
+ auto future = promise.future().then([&](int value) {
+ ++checkpoint;
+ throw QException();
+ return value + 1;
+ }).then([&](QFuture<int> future) {
+ try {
+ auto res = future.result();
+ Q_UNUSED(res);
+ } catch (const QException &) {
+ ++checkpoint;
+ }
+ return 2;
+ }).then([&](int value) {
+ ++checkpoint;
+ promise.future().cancel();
+ return value + 1;
+ }).then([&](int value) {
+ ++checkpoint;
+ return value + 1;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ promise.start();
+ promise.addResult(42);
+ promise.finish();
+
+ QCOMPARE(future.result(), -1);
+ QCOMPARE(checkpoint, 3);
+ }
+#endif // QT_NO_EXCEPTIONS
+
+ // Check notifications from QFutureWatcher
+ {
+ QPromise<void> p;
+ auto f = p.future();
+
+ auto f1 = f.then([] {});
+ auto f2 = f1.then([] {});
+
+ QFutureWatcher<void> watcher1, watcher2;
+ int state = 0;
+ QObject::connect(&watcher1, &QFutureWatcher<void>::started, [&] {
+ QCOMPARE(state, 0);
+ ++state;
+ });
+ QObject::connect(&watcher1, &QFutureWatcher<void>::canceled, [&] {
+ QCOMPARE(state, 1);
+ ++state;
+ });
+ QObject::connect(&watcher1, &QFutureWatcher<void>::finished, [&] {
+ QCOMPARE(state, 2);
+ ++state;
+ });
+ QObject::connect(&watcher2, &QFutureWatcher<void>::started, [&] {
+ QCOMPARE(state, 3);
+ ++state;
+ });
+ QObject::connect(&watcher2, &QFutureWatcher<void>::canceled, [&] {
+ QCOMPARE(state, 4);
+ ++state;
+ });
+ QObject::connect(&watcher2, &QFutureWatcher<int>::finished, [&] {
+ QCOMPARE(state, 5);
+ ++state;
+ });
+
+ watcher1.setFuture(f1);
+ watcher2.setFuture(f2);
+
+ p.start();
+ f.cancel();
+ p.finish();
+
+ qApp->processEvents();
+
+ QCOMPARE(state, 6);
+ QVERIFY(watcher1.isFinished());
+ QVERIFY(watcher1.isCanceled());
+ QVERIFY(watcher2.isFinished());
+ QVERIFY(watcher2.isCanceled());
+ }
+
+ // Cancel continuations with context (QTBUG-108790)
+ {
+ // This test should pass with ASan
+ auto future = QtConcurrent::run([] {});
+ future.then(this, [] {});
+ future.waitForFinished();
+ future.cancel();
+ }
+}
+
+void tst_QFuture::continuationsWithContext_data()
+{
+ QTest::addColumn<bool>("inOtherThread");
+ QTest::addRow("in-other-thread") << true;
+ QTest::addRow("in-main-thread-qtbug119406") << false;
+}
+
+void tst_QFuture::continuationsWithContext()
+{
+ QFETCH(bool, inOtherThread);
+
+ auto tstThread = QThread::currentThread();
+ QThread *thread = inOtherThread ? new QThread
+ : tstThread;
+ auto context = new QObject();
+
+ const auto cleanupGuard = qScopeGuard([&] {
+ context->deleteLater();
+ if (thread != tstThread) {
+ thread->quit();
+ thread->wait();
+ delete thread;
+ }
+ });
+
+ if (inOtherThread) {
+ thread->start();
+ context->moveToThread(thread);
+ }
+
+ // .then()
+ {
+ QPromise<int> promise;
+ auto future = promise.future()
+ .then([&](int val) {
+ if (QThread::currentThread() != tstThread)
+ return 0;
+ return val + 1;
+ })
+ .then(context,
+ [&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1;
+ });
+ promise.start();
+ promise.addResult(0);
+ promise.finish();
+ QCOMPARE(future.result(), 3);
+ }
+
+ // .onCanceled
+ {
+ QPromise<int> promise;
+ auto future = promise.future()
+ .onCanceled(context,
+ [&] {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1;
+ });
+ promise.start();
+ promise.future().cancel();
+ promise.finish();
+ QCOMPARE(future.result(), 2);
+ }
+
+ // Cancellation when the context object is destroyed
+ {
+ // Use something like QWidget which deletes its children early, i.e.
+ // before ~QObject() runs. This behavior can lead to side-effects
+ // like QPointers to the parent not being set to nullptr during child
+ // object destruction.
+ QPointer shortLivedContext = new FakeQWidget();
+ shortLivedContext->moveToThread(thread);
+
+ QPromise<int> promise;
+ auto future = promise.future()
+ .then(shortLivedContext, [&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1000;
+ })
+ .onCanceled([&, ptr=QPointer(shortLivedContext)] {
+ if (QThread::currentThread() != thread)
+ return 0;
+ if (ptr)
+ return 1;
+ return 2;
+ });
+ promise.start();
+
+ QMetaObject::invokeMethod(shortLivedContext, [&]() {
+ delete shortLivedContext;
+ }, inOtherThread ? Qt::BlockingQueuedConnection
+ : Qt::DirectConnection);
+
+ promise.finish();
+ QCOMPARE(future.result(), 2);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // .onFaled()
+ {
+ QPromise<void> promise;
+ auto future = promise.future()
+ .then([&] {
+ if (QThread::currentThread() != tstThread)
+ return 0;
+ throw std::runtime_error("error");
+ })
+ .onFailed(context,
+ [&] {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != thread)
+ return 0;
+ return val + 1;
+ });
+ promise.start();
+ promise.finish();
+ QCOMPARE(future.result(), 2);
+ }
+#endif // QT_NO_EXCEPTIONS
+}
+
+void tst_QFuture::continuationsWithMoveOnlyLambda()
+{
+ // .then()
+ {
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeReadyVoidFuture()
+ .then([p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+ // .then() with thread pool
+ {
+ QThreadPool pool;
+
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeReadyVoidFuture()
+ .then(&pool, [p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+ // .then() with context
+ {
+ QObject object;
+
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeReadyVoidFuture()
+ .then(&object, [p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+
+ // .onCanceled()
+ {
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future =
+ createCanceledFuture<int>().onCanceled([p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+
+ // .onCanceled() with context
+ {
+ QObject object;
+
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = createCanceledFuture<int>().onCanceled(
+ &object, [p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // .onFailed()
+ {
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeExceptionalFuture<int>(QException())
+ .onFailed([p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+ // .onFailed() with context
+ {
+ QObject object;
+
+ std::unique_ptr<int> uniquePtr(new int(42));
+ auto future = QtFuture::makeExceptionalFuture<int>(QException())
+ .onFailed(&object, [p = std::move(uniquePtr)] { return *p; });
+ QCOMPARE(future.result(), 42);
+ }
+#endif // QT_NO_EXCEPTIONS
+}
+
+void tst_QFuture::testSingleResult(const UniquePtr &p)
+{
+ QVERIFY(p.get() != nullptr);
+}
+
+void tst_QFuture::testSingleResult(const std::vector<int> &v)
+{
+ QVERIFY(!v.empty());
+}
+
+template<class T>
+void tst_QFuture::testSingleResult(const T &unknown)
+{
+ Q_UNUSED(unknown)
+}
+
+
+template<class T>
+void tst_QFuture::testFutureTaken(QFuture<T> &noMoreFuture)
+{
+ QCOMPARE(noMoreFuture.isValid(), false);
+ QCOMPARE(noMoreFuture.resultCount(), 0);
+ QCOMPARE(noMoreFuture.progressValue(), 0);
+}
+
+template<class T>
+void tst_QFuture::testTakeResults(QFuture<T> future, size_type resultCount)
+{
+ auto copy = future;
+ QVERIFY(future.isFinished());
+ QVERIFY(future.isValid());
+ QCOMPARE(size_type(future.resultCount()), resultCount);
+ QVERIFY(copy.isFinished());
+ QVERIFY(copy.isValid());
+ QCOMPARE(size_type(copy.resultCount()), resultCount);
+
+ auto vec = future.takeResults();
+ QCOMPARE(vec.size(), resultCount);
+
+ for (const auto &r : vec) {
+ testSingleResult(r);
+ if (QTest::currentTestFailed())
+ return;
+ }
+
+ testFutureTaken(future);
+ if (QTest::currentTestFailed())
+ return;
+ testFutureTaken(copy);
+}
+
+#if 0
+void tst_QFuture::takeResults()
+{
+ // Test takeResults() for movable types (whether or not copyable).
+
+ // std::unique_ptr<int> supports only move semantics:
+ QFutureInterface<UniquePtr> moveIface;
+ moveIface.reportStarted();
+
+ // std::vector<int> supports both copy and move:
+ QFutureInterface<std::vector<int>> copyIface;
+ copyIface.reportStarted();
+
+ const int expectedCount = 10;
+
+ for (int i = 0; i < expectedCount; ++i) {
+ QVERIFY(moveIface.reportAndMoveResult(UniquePtr{new int(0b101010)}, i));
+ QVERIFY(copyIface.reportAndMoveResult(std::vector<int>{1,2,3,4,5}, i));
+ }
+
+ moveIface.reportFinished();
+ copyIface.reportFinished();
+
+ testTakeResults(moveIface.future(), size_type(expectedCount));
+ if (QTest::currentTestFailed())
+ return;
+
+ testTakeResults(copyIface.future(), size_type(expectedCount));
+}
+#endif
+
+void tst_QFuture::takeResult()
+{
+ QFutureInterface<UniquePtr> iface;
+ iface.reportStarted();
+ QVERIFY(iface.reportAndMoveResult(UniquePtr{new int(0b101010)}, 0));
+ iface.reportFinished();
+
+ auto future = iface.future();
+ QVERIFY(future.isFinished());
+ QVERIFY(future.isValid());
+ QCOMPARE(future.resultCount(), 1);
+
+ auto result = future.takeResult();
+ testFutureTaken(future);
+ if (QTest::currentTestFailed())
+ return;
+ testSingleResult(result);
+}
+
+void tst_QFuture::runAndTake()
+{
+ // Test if a 'moving' future can be used by
+ // QtConcurrent::run.
+
+ auto rabbit = [](){
+ // Let's wait a bit to give the test below some time
+ // to sync up with us with its watcher.
+ QThread::currentThread()->sleep(std::chrono::milliseconds{100});
+ return UniquePtr(new int(10));
+ };
+
+ QTestEventLoop loop;
+ QFutureWatcher<UniquePtr> watcha;
+ connect(&watcha, &QFutureWatcher<UniquePtr>::finished, [&loop](){
+ loop.exitLoop();
+ });
+
+ auto gotcha = QtConcurrent::run(rabbit);
+ watcha.setFuture(gotcha);
+
+ loop.enterLoop(500ms);
+ if (loop.timeout())
+ QSKIP("Failed to run the task, nothing to test");
+
+ gotcha = watcha.future();
+#if 0
+ // TODO: enable when QFuture::takeResults() is enabled
+ testTakeResults(gotcha, size_type(1));
+#endif
+}
+
+void tst_QFuture::resultsReadyAt_data()
+{
+ QTest::addColumn<bool>("testMove");
+
+ QTest::addRow("reportResult") << false;
+ QTest::addRow("reportAndMoveResult") << true;
+}
+
+void tst_QFuture::resultsReadyAt()
+{
+ QFETCH(const bool, testMove);
+
+ QFutureInterface<int> iface;
+ QFutureWatcher<int> watcher;
+ watcher.setFuture(iface.future());
+
+ QTestEventLoop eventProcessor;
+ connect(&watcher, &QFutureWatcher<int>::finished, &eventProcessor, &QTestEventLoop::exitLoop);
+
+ const int nExpectedResults = 4;
+ int reported = 0;
+ int taken = 0;
+ connect(&watcher, &QFutureWatcher<int>::resultsReadyAt,
+ [&iface, &reported, &taken](int begin, int end)
+ {
+ auto future = iface.future();
+ QVERIFY(end - begin > 0);
+ for (int i = begin; i < end; ++i, ++reported) {
+ QVERIFY(future.isResultReadyAt(i));
+ taken |= 1 << i;
+ }
+ });
+
+ auto report = [&iface, testMove](int index)
+ {
+ int dummyResult = 0b101010;
+ if (testMove)
+ QVERIFY(iface.reportAndMoveResult(std::move(dummyResult), index));
+ else
+ QVERIFY(iface.reportResult(&dummyResult, index));
+ };
+
+ const QSignalSpy readyCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt);
+ QTimer::singleShot(0, [&iface, &report]{
+ // With filter mode == true, the result may go into the pending results.
+ // Reporting it as ready will allow an application to try and access the
+ // result, crashing on invalid (store.end()) iterator dereferenced.
+ iface.setFilterMode(true);
+ iface.reportStarted();
+ report(0);
+ report(1);
+ // This one - should not be reported (it goes into pending):
+ report(3);
+ // Let's close the 'gap' and make them all ready:
+ report(-1);
+ iface.reportFinished();
+ });
+
+ // Run event loop, QCoreApplication::postEvent is in use
+ // in QFutureInterface:
+ 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.size(), 3);
+ QCOMPARE(taken, 0b1111);
+}
+
+template <class T>
+auto makeFutureInterface(T &&result)
+{
+ QFutureInterface<T> f;
+
+ f.reportStarted();
+ f.reportResult(std::forward<T>(result));
+ f.reportFinished();
+
+ return f;
+}
+
+void tst_QFuture::takeResultWorksForTypesWithoutDefaultCtor()
+{
+ struct Foo
+ {
+ Foo() = delete;
+ explicit Foo(int i) : _i(i) {}
+
+ int _i = -1;
+ };
+
+ auto f = makeFutureInterface(Foo(42));
+
+ QCOMPARE(f.takeResult()._i, 42);
+}
+void tst_QFuture::canceledFutureIsNotValid()
+{
+ auto f = makeFutureInterface(42);
+
+ f.cancel();
+
+ QVERIFY(!f.isValid());
+}
+
+void tst_QFuture::signalConnect()
+{
+ const int intValue = 42;
+ const double doubleValue = 42.5;
+ const QString stringValue = "42";
+
+ using TupleType = std::tuple<int, double, QString>;
+ const TupleType tuple(intValue, doubleValue, stringValue);
+
+ using PairType = std::pair<int, double>;
+ const PairType pair(intValue, doubleValue);
+
+ // No arg
+ {
+ SenderObject sender;
+ auto future =
+ QtFuture::connect(&sender, &SenderObject::noArgSignal).then([] { return true; });
+ sender.emitNoArg();
+ QCOMPARE(future.result(), true);
+ }
+
+ // One arg
+ {
+ SenderObject sender;
+ auto future = QtFuture::connect(&sender, &SenderObject::intArgSignal).then([](int value) {
+ return value;
+ });
+ sender.emitIntArg(42);
+ QCOMPARE(future.result(), 42);
+ }
+
+ // Const ref arg
+ {
+ SenderObject sender;
+ auto future =
+ QtFuture::connect(&sender, &SenderObject::constRefArg).then([](QString value) {
+ return value;
+ });
+ sender.emitConstRefArg(QString("42"));
+ QCOMPARE(future.result(), "42");
+ }
+
+ // Multiple args
+ {
+ SenderObject sender;
+ auto future =
+ QtFuture::connect(&sender, &SenderObject::multipleArgs).then([](TupleType values) {
+ return values;
+ });
+ sender.emitMultipleArgs(intValue, doubleValue, stringValue);
+ auto result = future.result();
+ QCOMPARE(result, tuple);
+ }
+
+ // Single std::tuple arg
+ {
+ SenderObject sender;
+ QFuture<TupleType> future = QtFuture::connect(&sender, &SenderObject::tupleArgSignal);
+ sender.emitTupleArgSignal(tuple);
+ auto result = future.result();
+ QCOMPARE(result, tuple);
+ }
+
+ // Multi-args signal(int, std::tuple)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<int, TupleType>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithTupleSignal1);
+ sender.emitMultiArgsWithTupleSignal1(142, tuple);
+ const auto [v, t] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(t, tuple);
+ }
+
+ // Multi-args signal(std::tuple, int)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<TupleType, int>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithTupleSignal2);
+ sender.emitMultiArgsWithTupleSignal2(tuple, 142);
+ const auto [t, v] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(t, tuple);
+ }
+
+ // Multi-args signal(int, std::pair)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<int, PairType>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithPairSignal1);
+ sender.emitMultiArgsWithPairSignal1(142, pair);
+ const auto [v, p] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(p, pair);
+ }
+
+ // Multi-args signal(std::pair, int)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<PairType, int>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithPairSignal2);
+ sender.emitMultiArgsWithPairSignal2(pair, 142);
+ const auto [p, v] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(p, pair);
+ }
+
+ // No arg private signal
+ {
+ SenderObject sender;
+ auto future = QtFuture::connect(&sender, &SenderObject::noArgPrivateSignal).then([] {
+ return true;
+ });
+ sender.emitNoArgPrivateSignal();
+ QCOMPARE(future.result(), true);
+ }
+
+ // One arg private signal
+ {
+ SenderObject sender;
+ auto future =
+ QtFuture::connect(&sender, &SenderObject::intArgPrivateSignal).then([](int value) {
+ return value;
+ });
+ sender.emitIntArgPrivateSignal(42);
+ QCOMPARE(future.result(), 42);
+ }
+
+ // Multi-args private signal
+ {
+ SenderObject sender;
+ auto future = QtFuture::connect(&sender, &SenderObject::multiArgsPrivateSignal)
+ .then([](TupleType values) { return values; });
+ sender.emitMultiArgsPrivateSignal(intValue, doubleValue, stringValue);
+ auto result = future.result();
+ QCOMPARE(result, tuple);
+ }
+
+ // Single std::tuple arg private signal
+ {
+ SenderObject sender;
+ QFuture<TupleType> future =
+ QtFuture::connect(&sender, &SenderObject::tupleArgPrivateSignal);
+ sender.emitTupleArgPrivateSignal(tuple);
+ auto result = future.result();
+ QCOMPARE(result, tuple);
+ }
+
+ // Multi-args private signal(int, std::tuple)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<int, TupleType>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithTuplePrivateSignal1);
+ sender.emitMultiArgsWithTuplePrivateSignal1(142, tuple);
+ const auto [v, t] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(t, tuple);
+ }
+
+ // Multi-args private signal(std::tuple, int)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<TupleType, int>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithTuplePrivateSignal2);
+ sender.emitMultiArgsWithTuplePrivateSignal2(tuple, 142);
+ const auto [t, v] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(t, tuple);
+ }
+
+ // Multi-args private signal(int, std::pair)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<int, PairType>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithPairPrivateSignal1);
+ sender.emitMultiArgsWithPairPrivateSignal1(142, pair);
+ const auto [v, p] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(p, pair);
+ }
+
+ // Multi-args private signal(std::pair, int)
+ {
+ SenderObject sender;
+ QFuture<std::tuple<PairType, int>> future =
+ QtFuture::connect(&sender, &SenderObject::multiArgsWithPairPrivateSignal2);
+ sender.emitMultiArgsWithPairPrivateSignal2(pair, 142);
+ const auto [p, v] = future.result();
+ QCOMPARE(v, 142);
+ QCOMPARE(p, pair);
+ }
+
+ // Sender destroyed
+ {
+ SenderObject *sender = new SenderObject();
+
+ auto future = QtFuture::connect(sender, &SenderObject::intArgSignal);
+
+ QSignalSpy spy(sender, &QObject::destroyed);
+ sender->deleteLater();
+
+ spy.wait();
+
+ QVERIFY(future.isCanceled());
+ QVERIFY(!future.isValid());
+ }
+
+ // Signal emitted, causing Sender to be destroyed
+ {
+ SenderObject *sender = new SenderObject();
+
+ auto future = QtFuture::connect(sender, &SenderObject::intArgSignal);
+ future.then([sender](int) {
+ // Scenario: Sender no longer needed, so it's deleted
+ delete sender;
+ });
+
+ QSignalSpy spy(sender, &SenderObject::destroyed);
+ emit sender->intArgSignal(5);
+ spy.wait();
+
+ QVERIFY(future.isFinished());
+ QVERIFY(!future.isCanceled());
+ QVERIFY(future.isValid());
+ }
+
+ // Connect to nullptr
+ {
+ SenderObject *sender = nullptr;
+ auto future = QtFuture::connect(sender, &SenderObject::intArgSignal);
+ QVERIFY(future.isFinished());
+ QVERIFY(future.isCanceled());
+ QVERIFY(!future.isValid());
+ }
+
+ // Connect to non-signal
+ {
+ SenderObject sender;
+
+#if defined(Q_CC_MSVC_ONLY) && (Q_CC_MSVC < 1940 || __cplusplus < 202002L)
+#define EXPECT_FUTURE_CONNECT_FAIL() QEXPECT_FAIL("", "QTBUG-101761, test fails on Windows/MSVC", Continue)
+#else
+ QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in SenderObject");
+#define EXPECT_FUTURE_CONNECT_FAIL()
+#endif
+
+ auto future = QtFuture::connect(&sender, &SenderObject::emitNoArg);
+ EXPECT_FUTURE_CONNECT_FAIL();
+ QVERIFY(future.isFinished());
+ EXPECT_FUTURE_CONNECT_FAIL();
+ QVERIFY(future.isCanceled());
+ EXPECT_FUTURE_CONNECT_FAIL();
+ QVERIFY(!future.isValid());
+#undef EXPECT_FUTURE_CONNECT_FAIL
+ }
+}
+
+void tst_QFuture::waitForFinished()
+{
+ QFutureInterface<void> fi;
+ auto future = fi.future();
+
+ QScopedPointer<QThread> waitingThread (QThread::create([&] {
+ future.waitForFinished();
+ }));
+
+ waitingThread->start();
+
+ QVERIFY(!waitingThread->wait(200));
+ QVERIFY(!waitingThread->isFinished());
+
+ fi.reportStarted();
+ QVERIFY(!waitingThread->wait(200));
+ QVERIFY(!waitingThread->isFinished());
+
+ fi.reportFinished();
+
+ QVERIFY(waitingThread->wait());
+ QVERIFY(waitingThread->isFinished());
+}
+
+void tst_QFuture::rejectResultOverwrite_data()
+{
+ QTest::addColumn<bool>("filterMode");
+ QTest::addColumn<QList<int>>("initResults");
+
+ QTest::addRow("filter-mode-on-1-result") << true << QList<int>({ 456 });
+ QTest::addRow("filter-mode-on-N-results") << true << QList<int>({ 456, 789 });
+ QTest::addRow("filter-mode-off-1-result") << false << QList<int>({ 456 });
+ QTest::addRow("filter-mode-off-N-results") << false << QList<int>({ 456, 789 });
+}
+
+void tst_QFuture::rejectResultOverwrite()
+{
+ QFETCH(bool, filterMode);
+ QFETCH(QList<int>, initResults);
+
+ QFutureInterface<int> iface;
+ iface.setFilterMode(filterMode);
+ auto f = iface.future();
+ QFutureWatcher<int> watcher;
+ watcher.setFuture(f);
+
+ QTestEventLoop eventProcessor;
+ // control the loop by suspend
+ connect(&watcher, &QFutureWatcher<int>::suspending, &eventProcessor, &QTestEventLoop::exitLoop);
+ // internal machinery always emits resultsReadyAt
+ QSignalSpy resultCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt);
+
+ // init
+ if (initResults.size() == 1)
+ QVERIFY(iface.reportResult(initResults[0]));
+ else
+ QVERIFY(iface.reportResults(initResults));
+ QCOMPARE(f.resultCount(), initResults.size());
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ QCOMPARE(f.results(), initResults);
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ // Run event loop, QCoreApplication::postEvent is in use
+ // in QFutureInterface:
+ eventProcessor.enterLoop(DefaultWaitTime);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.size(), 1);
+ f.resume();
+
+ // overwrite with lvalue
+ {
+ int result = -1;
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(result, 0));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+ // overwrite with rvalue
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(-1, 0));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+ // overwrite with array
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResults(QList<int> { -1, -2, -3 }, 0));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), initResults[0]);
+ }
+
+ // special case: add result by different index, overlapping with the vector
+ if (initResults.size() > 1) {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(-1, 1));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(1), initResults[1]);
+ }
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ eventProcessor.enterLoop(DefaultWaitTime);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.size(), 1);
+ f.resume();
+ QCOMPARE(f.results(), initResults);
+}
+
+void tst_QFuture::rejectPendingResultOverwrite()
+{
+ QFETCH(bool, filterMode);
+ QFETCH(QList<int>, initResults);
+
+ QFutureInterface<int> iface;
+ iface.setFilterMode(filterMode);
+ auto f = iface.future();
+ QFutureWatcher<int> watcher;
+ watcher.setFuture(f);
+
+ QTestEventLoop eventProcessor;
+ // control the loop by suspend
+ connect(&watcher, &QFutureWatcher<int>::suspending, &eventProcessor, &QTestEventLoop::exitLoop);
+ // internal machinery always emits resultsReadyAt
+ QSignalSpy resultCounter(&watcher, &QFutureWatcher<int>::resultsReadyAt);
+
+ // init
+ if (initResults.size() == 1)
+ QVERIFY(iface.reportResult(initResults[0], 1));
+ else
+ QVERIFY(iface.reportResults(initResults, 1));
+ QCOMPARE(f.resultCount(), 0); // not visible yet
+ if (!filterMode) {
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ QCOMPARE(f.results(), initResults);
+
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ // Run event loop, QCoreApplication::postEvent is in use
+ // in QFutureInterface:
+ eventProcessor.enterLoop(DefaultWaitTime);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.size(), 1);
+ f.resume();
+ }
+
+ // overwrite with lvalue
+ {
+ int result = -1;
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(result, 1));
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // overwrite with rvalue
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(-1, 1));
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // overwrite with array
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResults(QList<int> { -1, -2 }, 1));
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ }
+ // special case: add result by different index, overlapping with the vector
+ if (initResults.size() > 1) {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!iface.reportResult(-1, 2));
+ QCOMPARE(f.resultCount(), originalCount);
+ if (!filterMode)
+ QCOMPARE(f.resultAt(2), initResults[1]);
+ }
+
+ if (!filterMode) {
+ QTimer::singleShot(50, [&f]() {
+ f.suspend(); // should exit the loop
+ });
+ eventProcessor.enterLoop(DefaultWaitTime);
+ QVERIFY(!eventProcessor.timeout());
+ QCOMPARE(resultCounter.size(), 1);
+ f.resume();
+ }
+
+ QVERIFY(iface.reportResult(123, 0)); // make results at 0 and 1 accessible
+ QCOMPARE(f.resultCount(), initResults.size() + 1);
+ QCOMPARE(f.resultAt(1), initResults[0]);
+ initResults.prepend(123);
+ QCOMPARE(f.results(), initResults);
+}
+
+void tst_QFuture::createReadyFutures()
+{
+#if QT_DEPRECATED_SINCE(6, 10)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ // using const T &
+ {
+ const int val = 42;
+ QFuture<int> f = QtFuture::makeReadyFuture(val);
+ QCOMPARE(f.result(), val);
+ }
+
+ // using T
+ {
+ int val = 42;
+ QFuture<int> f = QtFuture::makeReadyFuture(val);
+ QCOMPARE(f.result(), val);
+ }
+
+ // using T &&
+ {
+ auto f = QtFuture::makeReadyFuture(std::make_unique<int>(42));
+ QCOMPARE(*f.takeResult(), 42);
+ }
+
+ // using void
+ {
+ auto f = QtFuture::makeReadyFuture();
+ QVERIFY(f.isStarted());
+ QVERIFY(!f.isRunning());
+ QVERIFY(f.isFinished());
+ }
+
+ // using const QList<T> &
+ {
+ const QList<int> values { 1, 2, 3 };
+ auto f = QtFuture::makeReadyFuture(values);
+ QCOMPARE(f.resultCount(), 3);
+ QCOMPARE(f.results(), values);
+ }
+QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 10)
+
+ // test makeReadyValueFuture<T>()
+ {
+ const int val = 42;
+ auto f = QtFuture::makeReadyValueFuture(val);
+ QCOMPARE_EQ(f.result(), val);
+
+ int otherVal = 42;
+ f = QtFuture::makeReadyValueFuture(otherVal);
+ QCOMPARE_EQ(f.result(), otherVal);
+ }
+ {
+ auto f = QtFuture::makeReadyValueFuture(std::make_unique<int>(42));
+ QCOMPARE(*f.takeResult(), 42);
+ }
+ // test makeReadyVoidFuture()
+ {
+ auto f = QtFuture::makeReadyVoidFuture();
+ QVERIFY(f.isStarted());
+ QVERIFY(!f.isRunning());
+ QVERIFY(f.isFinished());
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // using QException
+ {
+ QException e;
+ auto f = QtFuture::makeExceptionalFuture<int>(e);
+ bool caught = false;
+ try {
+ f.result();
+ } catch (QException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ }
+
+ // using std::exception_ptr and QFuture<void>
+ {
+ auto exception = std::make_exception_ptr(TestException());
+ auto f = QtFuture::makeExceptionalFuture(exception);
+ bool caught = false;
+ try {
+ f.waitForFinished();
+ } catch (TestException &) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ }
+#endif
+
+ // testing makeReadyRangeFuture with various containers
+ {
+ const QList<int> expectedResult{1, 2, 3};
+
+ const QList<int> list{1, 2, 3};
+ auto f = QtFuture::makeReadyRangeFuture(list);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ QVarLengthArray<int> varArray{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(varArray);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ std::vector<int> vec{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(std::move(vec));
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ f = QtFuture::makeReadyRangeFuture(std::array<int, 3>{1, 2, 3});
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ f = QtFuture::makeReadyRangeFuture(std::list<int>{1, 2, 3});
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ std::forward_list<int> fwdlist{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(fwdlist);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ const QSet<int> qset{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(qset);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ auto result = f.results();
+ std::sort(result.begin(), result.end());
+ QCOMPARE_EQ(result, expectedResult);
+
+ const QMap<QString, int> qmap{
+ {"one", 1},
+ {"two", 2},
+ {"three", 3}
+ };
+ f = QtFuture::makeReadyRangeFuture(qmap);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ result = f.results();
+ std::sort(result.begin(), result.end());
+ QCOMPARE_EQ(result, expectedResult);
+
+ std::set<int> stdset{1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(stdset);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ result = f.results();
+ std::sort(result.begin(), result.end());
+ QCOMPARE_EQ(result, expectedResult);
+
+ // testing ValueType[N] overload
+ const int c_array[] = {1, 2, 3};
+ f = QtFuture::makeReadyRangeFuture(c_array);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ f = QtFuture::makeReadyRangeFuture({1, 2, 3});
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+ }
+ // testing makeReadyRangeFuture with a more complex underlying type
+ {
+ QObject obj1;
+ QObject obj2;
+ QObject obj3;
+
+ const QList<QObject*> expectedResult{&obj1, &obj2, &obj3};
+
+ const QList<QObject*> list{&obj1, &obj2, &obj3};
+ auto f = QtFuture::makeReadyRangeFuture(list);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ std::list<QObject*> stdlist{&obj1, &obj2, &obj3};
+ f = QtFuture::makeReadyRangeFuture(std::move(stdlist));
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+
+ QObject* const c_array[] = {&obj1, &obj2, &obj3};
+ f = QtFuture::makeReadyRangeFuture(c_array);
+ QCOMPARE_EQ(f.resultCount(), 3);
+ QCOMPARE_EQ(f.results(), expectedResult);
+ }
+}
+
+void tst_QFuture::continuationsAfterReadyFutures()
+{
+ // continuations without a context
+ {
+ QFuture<int> f = QtFuture::makeReadyValueFuture(42)
+ .then([](int val) {
+ return val + 10;
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 52);
+ }
+ {
+ auto rangeF = QtFuture::makeReadyRangeFuture({1, 2, 3});
+ QFuture<int> f = rangeF
+ .then([vals = rangeF.results()](auto) {
+ return vals.last();
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 3);
+ }
+ {
+ QFuture<int> f = QtFuture::makeReadyVoidFuture()
+ .then([]() {
+ return 1;
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 1);
+ }
+#ifndef QT_NO_EXCEPTIONS
+ {
+ QException e;
+ QFuture<int> f = QtFuture::makeExceptionalFuture<int>(e)
+ .then([](int) {
+ return 1;
+ })
+ .onCanceled([]() {
+ return -1;
+ })
+ .onFailed([](const QException &) {
+ return -2;
+ });
+ QCOMPARE(f.result(), -2);
+ }
+#endif
+
+ // continuations with a context
+ QObject context;
+ {
+ QFuture<int> f = QtFuture::makeReadyValueFuture(42)
+ .then(&context, [](int val) {
+ return val + 10;
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 52);
+ }
+ {
+ auto rangeF = QtFuture::makeReadyRangeFuture({1, 2, 3});
+ QFuture<int> f = rangeF
+ .then(&context, [vals = rangeF.results()](auto) {
+ return vals.last();
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 3);
+ }
+ {
+ QFuture<int> f = QtFuture::makeReadyVoidFuture()
+ .then(&context, []() {
+ return 1;
+ })
+ .onCanceled([]() {
+ return -1;
+ });
+ QCOMPARE(f.result(), 1);
+ }
+#ifndef QT_NO_EXCEPTIONS
+ {
+ QException e;
+ QFuture<int> f = QtFuture::makeExceptionalFuture<int>(e)
+ .then(&context, [](int) {
+ return 1;
+ })
+ .onCanceled([]() {
+ return -1;
+ })
+ .onFailed([](const QException &) {
+ return -2;
+ });
+ QCOMPARE(f.result(), -2);
+ }
+#endif
+}
+
+void tst_QFuture::getFutureInterface()
+{
+ const int val = 42;
+ QFuture<int> f = QtFuture::makeReadyValueFuture(val);
+
+ auto interface = QFutureInterfaceBase::get(f);
+ QCOMPARE(interface.resultCount(), 1);
+}
+
+void tst_QFuture::convertQMetaType()
+{
+ const auto intType = QMetaType::fromType<QFuture<int>>();
+ const auto voidType = QMetaType::fromType<QFuture<void>>();
+
+ QVERIFY(QMetaType::canConvert(intType, voidType));
+
+ const int val = 42;
+ QFuture<int> f = QtFuture::makeReadyValueFuture(val);
+ auto variant = QVariant::fromValue(f);
+ QVERIFY(variant.convert(voidType));
+
+ const auto voidFuture = variant.value<QFuture<void>>();
+ QVERIFY(voidFuture.isValid());
+ QVERIFY(voidFuture.isFinished());
+}
+
+template<class OutputContainer>
+void testWhenAllIterators()
+{
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QPromise<int> p2;
+ QList<QFuture<int>> futures = { p0.future(), p1.future(), p2.future() };
+
+ bool finished = false;
+ QFuture<OutputContainer> whenAll;
+ if constexpr (std::is_same_v<QList<QFuture<int>>, OutputContainer>)
+ whenAll = QtFuture::whenAll(futures.begin(), futures.end());
+ else
+ whenAll = QtFuture::whenAll<OutputContainer>(futures.begin(), futures.end());
+ whenAll.then([&](const OutputContainer &output) {
+ QCOMPARE(output.size(), 3u);
+ QCOMPARE(output[0].result(), 0);
+ QCOMPARE(output[1].result(), 1);
+ QCOMPARE(output[2].result(), 2);
+ finished = true;
+ });
+ QVERIFY(whenAll.isRunning());
+
+ p0.start();
+ p0.addResult(0);
+ p0.finish();
+ QVERIFY(whenAll.isRunning());
+
+ p2.start();
+ p2.addResult(2);
+ p2.finish();
+ QVERIFY(whenAll.isRunning());
+
+ p1.start();
+ p1.addResult(1);
+ p1.finish();
+ QVERIFY(!whenAll.isRunning());
+ QVERIFY(finished);
+
+ // Try with empty sequence
+ QFuture<OutputContainer> whenAllEmpty;
+ if constexpr (std::is_same_v<QList<QFuture<int>>, OutputContainer>)
+ whenAllEmpty = QtFuture::whenAll(futures.end(), futures.end());
+ else
+ whenAllEmpty = QtFuture::whenAll<OutputContainer>(futures.end(), futures.end());
+ QVERIFY(whenAllEmpty.isStarted());
+ QVERIFY(whenAllEmpty.isFinished());
+ QVERIFY(whenAllEmpty.result().empty());
+}
+
+void tst_QFuture::whenAllIterators()
+{
+ // Try with different output containers
+ testWhenAllIterators<QList<QFuture<int>>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllIterators() with QList failed!");
+
+ testWhenAllIterators<std::vector<QFuture<int>>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllIterators() with std::vector failed!");
+
+ testWhenAllIterators<QVarLengthArray<QFuture<int>>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllIterators() with QVarLengthArray failed!");
+}
+
+void tst_QFuture::whenAllIteratorsWithCanceled()
+{
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QList<QFuture<int>> futures = { p0.future(), p1.future() };
+ bool finished = false;
+ auto whenAll = QtFuture::whenAll(futures.begin(), futures.end())
+ .then([&](const QList<QFuture<int>> &results) {
+ QCOMPARE(results.size(), 2);
+ QVERIFY(results[0].isCanceled());
+ QVERIFY(!results[1].isCanceled());
+ QCOMPARE(results[1].result(), 1);
+ finished = true;
+ });
+
+ p0.start();
+ p0.future().cancel();
+ p0.finish();
+ QVERIFY(!finished);
+
+ p1.start();
+ p1.addResult(1);
+ p1.finish();
+ QVERIFY(finished);
+}
+
+void tst_QFuture::whenAllIteratorsWithFailed()
+{
+#ifndef QT_NO_EXCEPTIONS
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QList<QFuture<int>> futures = { p0.future(), p1.future() };
+ bool finished = false;
+ auto whenAll = QtFuture::whenAll(futures.begin(), futures.end())
+ .then([&](QList<QFuture<int>> results) {
+ QCOMPARE(results.size(), 2);
+ QCOMPARE(results[1].result(), 1);
+ // A shorter way of handling the exception
+ results[0].onFailed([&](const QException &) {
+ finished = true;
+ return 0;
+ });
+ });
+
+ p0.start();
+ p0.setException(QException());
+ p0.finish();
+ QVERIFY(!finished);
+
+ p1.start();
+ p1.addResult(1);
+ p1.finish();
+ QVERIFY(finished);
+#else
+ QSKIP("Exceptions are disabled, skipping the test");
+#endif
+}
+
+// A helper for std::visit, see https://en.cppreference.com/w/cpp/utility/variant/visit
+template<class... Ts>
+struct overloaded : public Ts...
+{
+ using Ts::operator()...;
+};
+
+// explicit deduction guide
+template<class... Ts>
+overloaded(Ts...)->overloaded<Ts...>;
+
+template<class OutputContainer>
+void testWhenAllDifferentTypes()
+{
+ QPromise<int> pInt1;
+ QPromise<int> pInt2;
+ QPromise<void> pVoid;
+
+ using Futures = std::variant<QFuture<int>, QFuture<int>, QFuture<void>>;
+
+ QFuture<OutputContainer> whenAll;
+ if constexpr (std::is_same_v<QList<Futures>, OutputContainer>) {
+ whenAll = QtFuture::whenAll(pInt1.future(), pInt2.future(), pVoid.future());
+ } else {
+ whenAll =
+ QtFuture::whenAll<OutputContainer>(pInt1.future(), pInt2.future(), pVoid.future());
+ }
+
+ int sumOfInts = 0;
+ whenAll.then([&](const OutputContainer &results) {
+ for (auto future : results) {
+ std::visit(overloaded {
+ [&](const QFuture<int> &f) {
+ QVERIFY(f.isFinished());
+ sumOfInts += f.result();
+ },
+ [](const QFuture<void> &f) { QVERIFY(f.isFinished()); },
+ },
+ future);
+ }
+ });
+
+ pVoid.start();
+ pVoid.finish();
+ QVERIFY(whenAll.isRunning());
+
+ pInt2.start();
+ pInt2.addResult(2);
+ pInt2.finish();
+ QVERIFY(whenAll.isRunning());
+ QCOMPARE(sumOfInts, 0);
+
+ pInt1.start();
+ pInt1.addResult(1);
+ pInt1.finish();
+ QVERIFY(!whenAll.isRunning());
+ QCOMPARE(sumOfInts, 3);
+}
+
+void tst_QFuture::whenAllDifferentTypes()
+{
+#ifdef Q_OS_VXWORKS
+ QSKIP("std::variant implementation on VxWorks 24.03 is broken and doesn't work with duplicated types");
+#endif
+ using Futures = std::variant<QFuture<int>, QFuture<int>, QFuture<void>>;
+ testWhenAllDifferentTypes<QList<Futures>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllDifferentTypes() with QList failed!");
+
+ testWhenAllDifferentTypes<std::vector<Futures>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllDifferentTypes() with std::vector failed!");
+
+ testWhenAllDifferentTypes<QVarLengthArray<Futures>>();
+ if (QTest::currentTestFailed())
+ QSKIP("testWhenAllDifferentTypes() with QVarLengthArray failed!");
+}
+
+void tst_QFuture::whenAllDifferentTypesWithCanceled()
+{
+ QPromise<int> pInt;
+ QPromise<QString> pString;
+
+ const QString someValue = u"some value"_s;
+
+ bool finished = false;
+ using Futures = std::variant<QFuture<int>, QFuture<QString>>;
+ auto whenAll = QtFuture::whenAll(pInt.future(), pString.future())
+ .then([&](const QList<Futures> &results) {
+ finished = true;
+ for (auto future : results) {
+ std::visit(overloaded {
+ [](const QFuture<int> &f) {
+ QVERIFY(f.isFinished());
+ QVERIFY(f.isCanceled());
+ },
+ [&](const QFuture<QString> &f) {
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), someValue);
+ },
+ },
+ future);
+ }
+ });
+
+ pString.start();
+ pString.addResult(someValue);
+ pString.finish();
+ QVERIFY(!finished);
+
+ pInt.start();
+ pInt.future().cancel();
+ pInt.finish();
+ QVERIFY(finished);
+}
+
+void tst_QFuture::whenAllDifferentTypesWithFailed()
+{
+#ifndef QT_NO_EXCEPTIONS
+ QPromise<int> pInt;
+ QPromise<QString> pString;
+
+ const QString someValue = u"some value"_s;
+
+ bool finished = false;
+ using Futures = std::variant<QFuture<int>, QFuture<QString>>;
+ auto whenAll = QtFuture::whenAll(pInt.future(), pString.future())
+ .then([&](const QList<Futures> &results) {
+ finished = true;
+ for (auto future : results) {
+ std::visit(overloaded {
+ [](QFuture<int> f) {
+ QVERIFY(f.isFinished());
+ bool failed = false;
+ // A shorter way of handling the exception
+ f.onFailed([&](const QException &) {
+ failed = true;
+ return -1;
+ });
+ QVERIFY(failed);
+ },
+ [&](const QFuture<QString> &f) {
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), someValue);
+ },
+ },
+ future);
+ }
+ });
+
+ pInt.start();
+ pInt.setException(QException());
+ pInt.finish();
+ QVERIFY(!finished);
+
+ pString.start();
+ pString.addResult(someValue);
+ pString.finish();
+ QVERIFY(finished);
+#else
+ QSKIP("Exceptions are disabled, skipping the test")
+#endif
+}
+
+void tst_QFuture::whenAnyIterators()
+{
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QPromise<int> p2;
+ QList<QFuture<int>> futures = { p0.future(), p1.future(), p2.future() };
+
+ auto whenAny = QtFuture::whenAny(futures.begin(), futures.end());
+ int count = 0;
+ whenAny.then([&](const QtFuture::WhenAnyResult<int> &result) {
+ QCOMPARE(result.index, 1);
+ QCOMPARE(result.future.result(), 1);
+ QVERIFY(!futures[0].isFinished());
+ QVERIFY(futures[1].isFinished());
+ QVERIFY(!futures[2].isFinished());
+ ++count;
+ });
+
+ p0.start();
+ p1.start();
+ p2.start();
+ p0.addResult(0);
+ p1.addResult(1);
+ p2.addResult(2);
+ QVERIFY(!whenAny.isFinished());
+ QCOMPARE(count, 0);
+
+ p1.finish();
+ QVERIFY(whenAny.isFinished());
+ QCOMPARE(count, 1);
+
+ p0.finish();
+ QCOMPARE(count, 1);
+
+ p2.finish();
+ QCOMPARE(count, 1);
+
+ auto whenAnyEmpty = QtFuture::whenAny(futures.end(), futures.end());
+ QVERIFY(whenAnyEmpty.isStarted());
+ QVERIFY(whenAnyEmpty.isFinished());
+ QCOMPARE(whenAnyEmpty.result().index, -1);
+ auto whenAnyEmptyResult = whenAnyEmpty.result().future;
+ QVERIFY(whenAnyEmptyResult.isStarted());
+ QVERIFY(whenAnyEmptyResult.isFinished());
+ QVERIFY(whenAnyEmptyResult.isCanceled());
+}
+
+void tst_QFuture::whenAnyIteratorsWithCanceled()
+{
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QList<QFuture<int>> futures = { p0.future(), p1.future() };
+ int count = 0;
+ auto whenAny = QtFuture::whenAny(futures.begin(), futures.end())
+ .then([&](const QtFuture::WhenAnyResult<int> &result) {
+ QCOMPARE(result.index, 1);
+ QVERIFY(result.future.isCanceled());
+ QVERIFY(!futures[0].isFinished());
+ QVERIFY(futures[1].isFinished());
+ ++count;
+ });
+
+ p1.start();
+ p1.future().cancel();
+ p1.finish();
+ QVERIFY(whenAny.isFinished());
+ QCOMPARE(count, 1);
+
+ p0.start();
+ p0.addResult(0);
+ p0.finish();
+ QCOMPARE(count, 1);
+}
+
+void tst_QFuture::whenAnyIteratorsWithFailed()
+{
+#ifndef QT_NO_EXCEPTIONS
+ QPromise<int> p0;
+ QPromise<int> p1;
+ QList<QFuture<int>> futures = { p0.future(), p1.future() };
+ int count = 0;
+ auto whenAny = QtFuture::whenAny(futures.begin(), futures.end())
+ .then([&](QtFuture::WhenAnyResult<int> result) {
+ QCOMPARE(result.index, 1);
+ QVERIFY(p1.future().isFinished());
+ QVERIFY(!p0.future().isFinished());
+ // A shorter way of handling the exception
+ result.future.onFailed([&](const QException &) {
+ ++count;
+ return 0;
+ });
+ });
+
+ p1.start();
+ p1.setException(QException());
+ p1.finish();
+ QCOMPARE(count, 1);
+
+ p0.start();
+ p0.addResult(0);
+ p0.finish();
+ QCOMPARE(count, 1);
+#else
+ QSKIP("Exceptions are disabled, skipping the test")
+#endif
+}
+
+void tst_QFuture::whenAnyDifferentTypes()
+{
+#ifdef Q_OS_VXWORKS
+ QSKIP("std::variant implementation on VxWorks 24.03 is broken and doesn't work with duplicated types");
+#endif
+ QPromise<int> pInt1;
+ QPromise<int> pInt2;
+ QPromise<void> pVoid;
+
+ auto whenAny = QtFuture::whenAny(pInt1.future(), pInt2.future(), pVoid.future());
+ int count = 0;
+ whenAny.then([&](const std::variant<QFuture<int>, QFuture<int>, QFuture<void>> &result) {
+ QCOMPARE(result.index(), 1u);
+ std::visit(overloaded { [&](const QFuture<int> &future) {
+ QVERIFY(future.isFinished());
+ QCOMPARE(future.result(), 2);
+ ++count;
+ },
+ [](auto) { QFAIL("The wrong future completed."); }
+ },
+ result);
+ });
+
+ pInt2.start();
+ pInt1.start();
+ pVoid.start();
+ pInt1.addResult(1);
+ pInt2.addResult(2);
+
+ QVERIFY(!whenAny.isFinished());
+ QCOMPARE(count, 0);
+
+ pInt2.finish();
+ QVERIFY(whenAny.isFinished());
+ QCOMPARE(count, 1);
+
+ pInt1.finish();
+ QCOMPARE(count, 1);
+
+ pVoid.finish();
+ QCOMPARE(count, 1);
+}
+
+void tst_QFuture::whenAnyDifferentTypesWithCanceled()
+{
+ QPromise<int> pInt;
+ QPromise<void> pVoid;
+
+ int count = 0;
+ auto whenAny = QtFuture::whenAny(pInt.future(), pVoid.future())
+ .then([&](const std::variant<QFuture<int>, QFuture<void>> &result) {
+ QCOMPARE(result.index(), 0u);
+ std::visit(overloaded { [&](const QFuture<int> &future) {
+ QVERIFY(future.isFinished());
+ QVERIFY(future.isCanceled());
+ ++count;
+ },
+ [](auto) {
+ QFAIL("The wrong future completed.");
+ }
+ },
+ result);
+ });
+
+ pInt.start();
+ pInt.future().cancel();
+ pInt.finish();
+ QCOMPARE(count, 1);
+
+ pVoid.start();
+ pVoid.finish();
+ QCOMPARE(count, 1);
+}
+
+void tst_QFuture::whenAnyDifferentTypesWithFailed()
+{
+#ifndef QT_NO_EXCEPTIONS
+ QPromise<int> pInt;
+ QPromise<void> pVoid;
+
+ int count = 0;
+ auto whenAny = QtFuture::whenAny(pInt.future(), pVoid.future())
+ .then([&](const std::variant<QFuture<int>, QFuture<void>> &result) {
+ QCOMPARE(result.index(), 0u);
+ std::visit(overloaded { [&](QFuture<int> future) {
+ QVERIFY(future.isFinished());
+ // A shorter way of handling the exception
+ future.onFailed([&](const QException &) {
+ ++count;
+ return -1;
+ });
+ },
+ [](auto) {
+ QFAIL("The wrong future completed.");
+ }
+ },
+ result);
+ });
+
+ pInt.start();
+ pInt.setException(QException());
+ pInt.finish();
+ QCOMPARE(count, 1);
+
+ pVoid.start();
+ pVoid.finish();
+ QCOMPARE(count, 1);
+#else
+ QSKIP("Exceptions are disabled, skipping the test")
+#endif
+}
+
+void tst_QFuture::continuationOverride()
+{
+ QPromise<int> p;
+ bool firstExecuted = false;
+ bool secondExecuted = false;
+
+ QTest::ignoreMessage(QtWarningMsg,
+ "Adding a continuation to a future which already has a continuation. "
+ "The existing continuation is overwritten.");
+
+ QFuture<int> f1 = p.future();
+ f1.then([&firstExecuted](int) {
+ firstExecuted = true;
+ });
+
+ QFuture<int> f2 = p.future();
+ f2.then([&secondExecuted](int) {
+ secondExecuted = true;
+ });
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ QVERIFY(p.future().isFinished());
+ QVERIFY(!firstExecuted);
+ QVERIFY(secondExecuted);
+}
+
+struct InstanceCounter
+{
+ InstanceCounter() { ++count; }
+ InstanceCounter(const InstanceCounter &) { ++count; }
+ ~InstanceCounter() { --count; }
+ static int count;
+};
+int InstanceCounter::count = 0;
+
+void tst_QFuture::continuationsDontLeak()
+{
+ {
+ // QFuture isn't started and isn't finished (has no state)
+ QPromise<InstanceCounter> promise;
+ auto future = promise.future();
+
+ bool continuationIsRun = false;
+ future.then([future, &continuationIsRun](InstanceCounter) { continuationIsRun = true; });
+
+ promise.addResult(InstanceCounter {});
+
+ QVERIFY(!continuationIsRun);
+ }
+ QCOMPARE(InstanceCounter::count, 0);
+
+ {
+ // QFuture is started, but not finished
+ QPromise<InstanceCounter> promise;
+ auto future = promise.future();
+
+ bool continuationIsRun = false;
+ future.then([future, &continuationIsRun](InstanceCounter) { continuationIsRun = true; });
+
+ promise.start();
+ promise.addResult(InstanceCounter {});
+
+ QVERIFY(!continuationIsRun);
+ }
+ QCOMPARE(InstanceCounter::count, 0);
+
+ {
+ // QFuture is started and finished, the continuation is run
+ QPromise<InstanceCounter> promise;
+ auto future = promise.future();
+
+ bool continuationIsRun = false;
+ future.then([future, &continuationIsRun](InstanceCounter) {
+ QVERIFY(future.isFinished());
+ continuationIsRun = true;
+ });
+
+ promise.start();
+ promise.addResult(InstanceCounter {});
+ promise.finish();
+
+ QVERIFY(continuationIsRun);
+ }
+ QCOMPARE(InstanceCounter::count, 0);
+
+ {
+ // QTBUG-116731: Must pass with ASan enabled
+ bool continuationIsRun = false;
+ auto f = QtFuture::makeReadyValueFuture(42);
+ QtFuture::whenAll(f).then([&](auto) { continuationIsRun = true; });
+ QVERIFY(continuationIsRun);
+ }
+
+ {
+ // QTBUG-116731: Must pass with ASan enabled
+ bool continuationIsRun = false;
+ auto f = QtFuture::makeReadyValueFuture(42);
+ QList fs{f};
+ QtFuture::whenAll(fs.begin(), fs.end()).then([&](auto) { continuationIsRun = true; });
+ QVERIFY(continuationIsRun);
+ }
+
+ {
+ // QTBUG-116731: Must pass with ASan enabled
+ bool continuationIsRun = false;
+ auto f = QtFuture::makeReadyValueFuture(42);
+ QtFuture::whenAny(f).then([&](auto) { continuationIsRun = true; });
+ QVERIFY(continuationIsRun);
+ }
+
+ {
+ // QTBUG-116731: Must pass with ASan enabled
+ bool continuationIsRun = false;
+ auto f = QtFuture::makeReadyValueFuture(42);
+ QList fs{f};
+ QtFuture::whenAny(fs.begin(), fs.end()).then([&](auto) { continuationIsRun = true; });
+ QVERIFY(continuationIsRun);
+ }
+}
+
+// This test checks that we do not get use-after-free
+void tst_QFuture::cancelAfterFinishWithContinuations()
+{
+ QFuture<void> future;
+ bool continuationIsRun = false;
+ bool cancelCalled = false;
+ {
+ QPromise<void> promise;
+ future = promise.future();
+
+ future.then([&continuationIsRun]() {
+ continuationIsRun = true;
+ }).onCanceled([&cancelCalled]() {
+ cancelCalled = true;
+ });
+
+ promise.start();
+ promise.finish();
+ }
+
+ QVERIFY(continuationIsRun);
+ future.cancel();
+ QVERIFY(!cancelCalled);
+}
+
+void tst_QFuture::unwrap()
+{
+ // The nested future succeeds
+ {
+ QPromise<int> p;
+ QFuture<QFuture<int>> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ return nested;
+ });
+
+ QFuture<int> unwrapped = f.unwrap();
+ QVERIFY(!unwrapped.isStarted());
+ QVERIFY(!unwrapped.isFinished());
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ unwrapped.waitForFinished();
+
+ QVERIFY(unwrapped.isStarted());
+ QVERIFY(unwrapped.isFinished());
+ QCOMPARE(unwrapped.result(), 43);
+ }
+
+ // The nested future succeeds with multiple results
+ {
+ QPromise<int> p;
+ QFuture<QFuture<int>> f = p.future().then([] (int value) {
+ QPromise<int> nested;
+ nested.start();
+ nested.addResult(++value);
+ nested.addResult(++value);
+ nested.addResult(++value);
+ nested.finish();
+ return nested.future();
+ });
+
+ QFuture<int> unwrapped = f.unwrap();
+ QVERIFY(!unwrapped.isStarted());
+ QVERIFY(!unwrapped.isFinished());
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(unwrapped.isStarted());
+ QVERIFY(unwrapped.isFinished());
+ QCOMPARE(unwrapped.results(), QList<int>() << 43 << 44 << 45);
+ }
+
+ // The chain is canceled, check that unwrap() propagates the cancellation.
+ {
+ QPromise<int> p;
+ QFuture<int> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ return nested;
+ }).unwrap().then([] (int result) {
+ return result;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ p.start();
+ p.future().cancel();
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(f.isStarted());
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), -1);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // The chain has an exception, check that unwrap() propagates it.
+ {
+ QPromise<int> p;
+ QFuture<int> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ return nested;
+ }).unwrap().then([] (int result) {
+ return result;
+ }).onFailed([] (QException &) {
+ return -1;
+ });
+
+ p.start();
+ p.setException(QException());
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(f.isStarted());
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), -1);
+ }
+
+#endif // QT_NO_EXCEPTIONS
+
+ // The nested future is canceled
+ {
+ QPromise<int> p;
+ QFuture<int> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ nested.cancel();
+ return nested;
+ }).unwrap().then([] (int result) {
+ return result;
+ }).onCanceled([] {
+ return -1;
+ });
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(f.isStarted());
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), -1);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // The nested future fails with an exception
+ {
+ QPromise<int> p;
+ QFuture<int> f = p.future().then([] (int value) {
+ QFuture<int> nested = QtConcurrent::run([value] {
+ throw QException();
+ return value + 1;
+ });
+ return nested;
+ }).unwrap().then([] (int result) {
+ return result;
+ }).onFailed([] (QException &) {
+ return -1;
+ });
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ f.waitForFinished();
+
+ QVERIFY(f.isStarted());
+ QVERIFY(f.isFinished());
+ QCOMPARE(f.result(), -1);
+ }
+#endif // QT_NO_EXCEPTIONS
+
+ // Check that continuations are called in the right order
+ {
+ QPromise<void> p;
+
+ std::atomic<bool> firstThenInvoked = false;
+ std::atomic<bool> secondThenInvoked = false;
+ std::atomic<bool> nestedThenInvoked = false;
+ auto f = p.future().then([&] {
+ if (!firstThenInvoked && !secondThenInvoked && !nestedThenInvoked)
+ firstThenInvoked = true;
+ QFuture<void> nested = QtConcurrent::run([&] {
+ QVERIFY(firstThenInvoked);
+ QVERIFY(!nestedThenInvoked);
+ QVERIFY(!secondThenInvoked);
+ nestedThenInvoked = true;
+ });
+ return nested;
+ }).unwrap().then([&] {
+ QVERIFY(firstThenInvoked);
+ QVERIFY(nestedThenInvoked);
+ QVERIFY(!secondThenInvoked);
+ secondThenInvoked = true;
+ });
+
+ QVERIFY(!firstThenInvoked);
+ QVERIFY(!nestedThenInvoked);
+ QVERIFY(!secondThenInvoked);
+
+ p.start();
+ p.finish();
+
+ f.waitForFinished();
+
+ if (QTest::currentTestFailed())
+ return;
+
+ QVERIFY(firstThenInvoked);
+ QVERIFY(nestedThenInvoked);
+ QVERIFY(secondThenInvoked);
+ }
+
+ // Unwrap multiple nested futures
+ {
+ QPromise<int> p;
+ QFuture<QFuture<QFuture<int>>> f = p.future().then([] (int value) {
+ QFuture<QFuture<int>> nested = QtConcurrent::run([value] {
+ QFuture<int> doubleNested = QtConcurrent::run([value] {
+ return value + 1;
+ });
+ return doubleNested;
+ });
+ return nested;
+ });
+
+ QFuture<int> unwrapped = f.unwrap();
+ QVERIFY(!unwrapped.isStarted());
+ QVERIFY(!unwrapped.isFinished());
+
+ p.start();
+ p.addResult(42);
+ p.finish();
+
+ unwrapped.waitForFinished();
+
+ QVERIFY(unwrapped.isStarted());
+ QVERIFY(unwrapped.isFinished());
+ QCOMPARE(unwrapped.result(), 43);
+ }
+
+ // Unwrap multiple nested void futures
+ {
+ QPromise<void> p;
+ std::atomic<bool> nestedInvoked = false;
+ std::atomic<bool> doubleNestedInvoked = false;
+ QFuture<QFuture<QFuture<void>>> f = p.future().then([&] {
+ QFuture<QFuture<void>> nested = QtConcurrent::run([&] {
+ QFuture<void> doubleNested = QtConcurrent::run([&] {
+ doubleNestedInvoked = true;
+ });
+ nestedInvoked = true;
+ return doubleNested;
+ });
+ return nested;
+ });
+
+ QFuture<void> unwrapped = f.unwrap();
+ QVERIFY(!nestedInvoked);
+ QVERIFY(!doubleNestedInvoked);
+ QVERIFY(!unwrapped.isStarted());
+ QVERIFY(!unwrapped.isFinished());
+
+ p.start();
+ p.finish();
+
+ unwrapped.waitForFinished();
+
+ QVERIFY(unwrapped.isStarted());
+ QVERIFY(unwrapped.isFinished());
+ QVERIFY(nestedInvoked);
+ QVERIFY(doubleNestedInvoked);
+ }
+}
+
QTEST_MAIN(tst_QFuture)
#include "tst_qfuture.moc"
diff --git a/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt b/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
new file mode 100644
index 0000000000..c0f4561c51
--- /dev/null
+++ b/tests/auto/corelib/thread/qfuturesynchronizer/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qfuturesynchronizer.pro b/tests/auto/corelib/thread/qfuturesynchronizer/qfuturesynchronizer.pro
deleted file mode 100644
index 0d20117ed0..0000000000
--- a/tests/auto/corelib/thread/qfuturesynchronizer/qfuturesynchronizer.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qfuturesynchronizer
-QT = core testlib
-SOURCES = tst_qfuturesynchronizer.cpp
diff --git a/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp b/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp
index 2a8a340925..62ad4f872a 100644
--- a/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp
+++ b/tests/auto/corelib/thread/qfuturesynchronizer/tst_qfuturesynchronizer.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QtCore/qfuturesynchronizer.h>
#include <QtCore/qfuture.h>
@@ -38,6 +13,7 @@ class tst_QFutureSynchronizer : public QObject
private Q_SLOTS:
void construction();
+ void setFutureAliasingExistingMember();
void addFuture();
void cancelOnWait();
void clearFutures();
@@ -58,6 +34,38 @@ void tst_QFutureSynchronizer::construction()
QCOMPARE(synchronizerWithFuture.futures().size(), 1);
}
+void tst_QFutureSynchronizer::setFutureAliasingExistingMember()
+{
+ //
+ // GIVEN: a QFutureSynchronizer with one QFuture:
+ //
+ QFutureSynchronizer synchronizer(QtFuture::makeReadyValueFuture(42));
+
+ //
+ // WHEN: calling setFuture() with an alias of the QFuture already in `synchronizer`:
+ //
+ for (int i = 0; i < 2; ++i) {
+ // The next line triggers -Wdangling-reference, but it's a FP because
+ // of implicit sharing. We cannot keep a copy of synchronizer.futures()
+ // around to avoid the warning, as the extra copy would cause a detach()
+ // of m_futures inside setFuture() with the consequence that `f` no longer
+ // aliases an element in m_futures, which is the goal of this test.
+QT_WARNING_PUSH
+#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 1301
+QT_WARNING_DISABLE_GCC("-Wdangling-reference")
+#endif
+ const auto &f = synchronizer.futures().constFirst();
+QT_WARNING_POP
+ synchronizer.setFuture(f);
+ }
+
+ //
+ // THEN: it didn't crash
+ //
+ QCOMPARE(synchronizer.futures().size(), 1);
+ QCOMPARE(synchronizer.futures().constFirst().result(), 42);
+}
+
void tst_QFutureSynchronizer::addFuture()
{
QFutureSynchronizer<void> synchronizer;
@@ -107,7 +115,7 @@ void tst_QFutureSynchronizer::futures()
synchronizer.addFuture(future);
}
- QCOMPARE(futures, synchronizer.futures());
+ QCOMPARE(futures.size(), synchronizer.futures().size());
}
void tst_QFutureSynchronizer::setFuture()
@@ -122,7 +130,6 @@ void tst_QFutureSynchronizer::setFuture()
QFuture<void> future;
synchronizer.setFuture(future);
QCOMPARE(synchronizer.futures().size(), 1);
- QCOMPARE(synchronizer.futures().first(), future);
}
void tst_QFutureSynchronizer::waitForFinished()
diff --git a/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt b/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt
new file mode 100644
index 0000000000..65417199a3
--- /dev/null
+++ b/tests/auto/corelib/thread/qfuturewatcher/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ LIBRARIES
+ Qt::Concurrent
+ Qt::CorePrivate
+)
diff --git a/tests/auto/corelib/thread/qfuturewatcher/qfuturewatcher.pro b/tests/auto/corelib/thread/qfuturewatcher/qfuturewatcher.pro
deleted file mode 100644
index d0e8b4c982..0000000000
--- a/tests/auto/corelib/thread/qfuturewatcher/qfuturewatcher.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qfuturewatcher
-QT = core core-private testlib concurrent
-SOURCES = tst_qfuturewatcher.cpp
diff --git a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
index b2ef516b4e..40aa89ded4 100644
--- a/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
+++ b/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp
@@ -1,40 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QDebug>
-#include <QtTest/QtTest>
+#include <QElapsedTimer>
+#include <QSignalSpy>
#include <QtConcurrent>
#include <private/qfutureinterface_p.h>
using namespace QtConcurrent;
+using namespace std::chrono_literals;
-#include <QtTest/QtTest>
+#include <QTest>
//#define PRINT
@@ -45,8 +22,11 @@ private slots:
void startFinish();
void progressValueChanged();
void canceled();
+ void cancelAndFinish_data();
+ void cancelAndFinish();
void resultAt();
void resultReadyAt();
+ void orderedResultReadyAt();
void futureSignals();
void watchFinishedFuture();
void watchCanceledFuture();
@@ -56,14 +36,20 @@ private slots:
void sharedFutureInterface();
void changeFuture();
void cancelEvents();
+#if QT_DEPRECATED_SINCE(6, 0)
void pauseEvents();
- void finishedState();
+ void pausedSuspendedOrder();
+#endif
+ void suspendEvents();
+ void suspended();
+ void suspendedEventsOrder();
void throttling();
void incrementalMapResults();
void incrementalFilterResults();
void qfutureSynchronizer();
void warnRace();
void matchFlags();
+ void checkStateConsistency();
};
void sleeper()
@@ -75,20 +61,31 @@ void tst_QFutureWatcher::startFinish()
{
QFutureWatcher<void> futureWatcher;
- QSignalSpy startedSpy(&futureWatcher, &QFutureWatcher<void>::started);
- QSignalSpy finishedSpy(&futureWatcher, &QFutureWatcher<void>::finished);
-
- QVERIFY(startedSpy.isValid());
- QVERIFY(finishedSpy.isValid());
+ int startedCount = 0;
+ int finishedCount = 0;
+ QObject::connect(&futureWatcher, &QFutureWatcher<void>::started,
+ [&startedCount, &finishedCount](){
+ ++startedCount;
+ QCOMPARE(startedCount, 1);
+ QCOMPARE(finishedCount, 0);
+ });
+ QObject::connect(&futureWatcher, &QFutureWatcher<void>::finished,
+ [&startedCount, &finishedCount](){
+ ++finishedCount;
+ QCOMPARE(startedCount, 1);
+ QCOMPARE(finishedCount, 1);
+ });
futureWatcher.setFuture(QtConcurrent::run(sleeper));
- QVERIFY(startedSpy.wait());
- QCOMPARE(startedSpy.count(), 1);
- QCOMPARE(finishedSpy.count(), 0);
futureWatcher.future().waitForFinished();
- QVERIFY(finishedSpy.wait());
- QCOMPARE(startedSpy.count(), 1);
- QCOMPARE(finishedSpy.count(), 1);
+
+ // waitForFinished() may unblock before asynchronous
+ // started() and finished() signals are delivered to the main thread.
+ // prosessEvents() should empty the pending queue.
+ qApp->processEvents();
+
+ QCOMPARE(startedCount, 1);
+ QCOMPARE(finishedCount, 1);
}
void mapSleeper(int &)
@@ -96,9 +93,9 @@ void mapSleeper(int &)
QTest::qSleep(100);
}
-QSet<int> progressValues;
-QSet<QString> progressTexts;
-QMutex mutex;
+static QSet<int> progressValues;
+static QSet<QString> progressTexts;
+static QMutex mutex;
class ProgressObject : public QObject
{
Q_OBJECT
@@ -173,7 +170,7 @@ class CancelObject : public QObject
Q_OBJECT
public:
bool wasCanceled;
- CancelObject() : wasCanceled(false) {};
+ CancelObject() : wasCanceled(false) {}
public slots:
void cancel();
};
@@ -211,12 +208,48 @@ void tst_QFutureWatcher::canceled()
future.waitForFinished();
}
-class IntTask : public RunFunctionTask<int>
+void tst_QFutureWatcher::cancelAndFinish_data()
+{
+ QTest::addColumn<bool>("isCanceled");
+ QTest::addColumn<bool>("isFinished");
+
+ QTest::addRow("running") << false << false;
+ QTest::addRow("canceled") << true << false;
+ QTest::addRow("finished") << false << true;
+ QTest::addRow("canceledAndFinished") << true << true;
+}
+
+void tst_QFutureWatcher::cancelAndFinish()
+{
+ QFETCH(bool, isCanceled);
+ QFETCH(bool, isFinished);
+
+ QFutureInterface<void> fi;
+ QFutureWatcher<void> futureWatcher;
+ QSignalSpy finishedSpy(&futureWatcher, &QFutureWatcher<void>::finished);
+ QSignalSpy canceledSpy(&futureWatcher, &QFutureWatcher<void>::canceled);
+ futureWatcher.setFuture(fi.future());
+
+ fi.reportStarted();
+
+ if (isCanceled)
+ fi.cancel();
+ if (isFinished)
+ fi.reportFinished();
+
+ fi.cancelAndFinish();
+
+ // The signals should be emitted only once
+ QTRY_COMPARE(canceledSpy.size(), 1);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+}
+
+class IntTask : public RunFunctionTaskBase<int>
{
public:
- void runFunctor()
+ void runFunctor() override
{
- result = 10;
+ promise.reportResult(10);
}
};
@@ -248,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
@@ -318,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);
}
}
@@ -371,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()
@@ -405,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()
@@ -431,28 +486,28 @@ void tst_QFutureWatcher::disconnectRunningFuture()
const int result = 10;
a.reportResult(&result);
QVERIFY(resultReadySpy.wait());
- QCOMPARE(resultReadySpy.count(), 1);
+ QCOMPARE(resultReadySpy.size(), 1);
delete watcher;
a.reportResult(&result);
QTest::qWait(10);
- QCOMPARE(resultReadySpy.count(), 1);
+ QCOMPARE(resultReadySpy.size(), 1);
a.reportFinished(&result);
QTest::qWait(10);
- QCOMPARE(finishedSpy.count(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
}
const int maxProgress = 100000;
-class ProgressEmitterTask : public RunFunctionTask<void>
+class ProgressEmitterTask : public RunFunctionTaskBase<void>
{
public:
- void runFunctor()
+ void runFunctor() override
{
- setProgressRange(0, maxProgress);
+ promise.setProgressRange(0, maxProgress);
for (int p = 0; p <= maxProgress; ++p)
- setProgressValue(p);
+ promise.setProgressValue(p);
}
};
@@ -469,30 +524,31 @@ void tst_QFutureWatcher::tooMuchProgress()
QObject::connect(&f, SIGNAL(progressValueChanged(int)), &o, SLOT(registerProgress(int)));
f.setFuture((new ProgressEmitterTask())->start());
- QTestEventLoop::instance().enterLoop(5);
+ // Android reports ca. 10k progressValueChanged per second
+ QTestEventLoop::instance().enterLoop(15);
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(progressValues.contains(maxProgress));
}
template <typename T>
-class ProgressTextTask : public RunFunctionTask<T>
+class ProgressTextTask : public RunFunctionTaskBase<T>
{
public:
- void runFunctor()
+ void runFunctor() override
{
- this->setProgressValueAndText(1, QLatin1String("Foo 1"));
+ this->promise.setProgressValueAndText(1, QLatin1String("Foo 1"));
- while (this->isProgressUpdateNeeded() == false)
+ while (this->promise.isProgressUpdateNeeded() == false)
QTest::qSleep(1);
- this->setProgressValueAndText(2, QLatin1String("Foo 2"));
+ this->promise.setProgressValueAndText(2, QLatin1String("Foo 2"));
- while (this->isProgressUpdateNeeded() == false)
+ while (this->promise.isProgressUpdateNeeded() == false)
QTest::qSleep(1);
- this->setProgressValueAndText(3, QLatin1String("Foo 3"));
+ this->promise.setProgressValueAndText(3, QLatin1String("Foo 3"));
- while (this->isProgressUpdateNeeded() == false)
+ while (this->promise.isProgressUpdateNeeded() == false)
QTest::qSleep(1);
- this->setProgressValueAndText(4, QLatin1String("Foo 4"));
+ this->promise.setProgressValueAndText(4, QLatin1String("Foo 4"));
}
};
@@ -545,12 +601,21 @@ void callInterface(T &obj)
obj.isFinished();
obj.isRunning();
obj.isCanceled();
- obj.isPaused();
+ obj.isSuspended();
+ obj.isSuspending();
obj.cancel();
- obj.pause();
+ obj.suspend();
obj.resume();
+ obj.toggleSuspended();
+#if QT_DEPRECATED_SINCE(6, 0)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ obj.isPaused();
+ obj.pause();
obj.togglePaused();
+QT_WARNING_POP
+#endif
obj.waitForFinished();
const T& objConst = obj;
@@ -563,7 +628,14 @@ void callInterface(T &obj)
objConst.isFinished();
objConst.isRunning();
objConst.isCanceled();
+ objConst.isSuspending();
+ objConst.isSuspended();
+#if QT_DEPRECATED_SINCE(6, 0)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
objConst.isPaused();
+QT_WARNING_POP
+#endif
}
template <typename T>
@@ -624,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
@@ -659,9 +731,12 @@ void tst_QFutureWatcher::cancelEvents()
QVERIFY(finishedSpy.wait());
- QCOMPARE(resultReadySpy.count(), 0);
+ QCOMPARE(resultReadySpy.size(), 0);
}
+#if QT_DEPRECATED_SINCE(6, 0)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
// Tests that events from paused futures are saved and
// delivered on resume.
void tst_QFutureWatcher::pauseEvents()
@@ -677,18 +752,22 @@ void tst_QFutureWatcher::pauseEvents()
QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
QVERIFY(resultReadySpy.isValid());
+ QSignalSpy pauseSpy(&watcher, &QFutureWatcher<int>::paused);
+ QVERIFY(pauseSpy.isValid());
+
watcher.setFuture(iface.future());
watcher.pause();
+ QTRY_COMPARE(pauseSpy.size(), 1);
+
int value = 0;
iface.reportFinished(&value);
- QTest::qWait(10);
- QCOMPARE(resultReadySpy.count(), 0);
+ // A result is reported, although the watcher is paused.
+ // The corresponding event should be also reported.
+ QTRY_COMPARE(resultReadySpy.size(), 1);
watcher.resume();
- QTRY_VERIFY2(!resultReadySpy.isEmpty(), "Result didn't arrive");
- QCOMPARE(resultReadySpy.count(), 1);
}
{
QFutureInterface<int> iface;
@@ -714,32 +793,209 @@ void tst_QFutureWatcher::pauseEvents()
a.resume(); // should give us no results.
QTest::qWait(10);
- QCOMPARE(resultReadySpy.count(), 0);
+ QCOMPARE(resultReadySpy.size(), 0);
}
}
-// Test that the finished state for the watcher gets
-// set when the finished event is delivered.
-// This means it will lag the finished state for the future,
-// but makes it more useful.
-void tst_QFutureWatcher::finishedState()
+void tst_QFutureWatcher::pausedSuspendedOrder()
{
- QFutureInterface<int> iface;
+ QFutureInterface<void> iface;
iface.reportStarted();
- QFuture<int> future = iface.future();
+
+ QFutureWatcher<void> watcher;
+
+ QSignalSpy pausedSpy(&watcher, &QFutureWatcher<void>::paused);
+ QVERIFY(pausedSpy.isValid());
+
+ QSignalSpy suspendedSpy(&watcher, &QFutureWatcher<void>::suspended);
+ QVERIFY(suspendedSpy.isValid());
+
+ bool pausedBeforeSuspended = false;
+ bool notSuspendedBeforePaused = false;
+ connect(&watcher, &QFutureWatcher<void>::paused,
+ [&] { notSuspendedBeforePaused = (suspendedSpy.size() == 0); });
+ connect(&watcher, &QFutureWatcher<void>::suspended,
+ [&] { 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.size(), 0);
+ QCOMPARE(suspendedSpy.size(), 0);
+
+ iface.setPaused(true);
+ iface.reportSuspended();
+
+ QTRY_COMPARE(suspendedSpy.size(), 1);
+ QCOMPARE(pausedSpy.size(), 1);
+ QVERIFY(notSuspendedBeforePaused);
+ QVERIFY(pausedBeforeSuspended);
+
+ iface.reportFinished();
+}
+QT_WARNING_POP
+#endif // QT_DEPRECATED_SINCE(6, 0)
+
+// Tests that events from suspended futures are saved and
+// delivered on resume.
+void tst_QFutureWatcher::suspendEvents()
+{
+ {
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+ connect(&watcher, &QFutureWatcher<int>::resultReadyAt, &object,
+ &SignalSlotObject::resultReadyAt);
+ QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
+ QVERIFY(resultReadySpy.isValid());
+
+ QSignalSpy suspendingSpy(&watcher, &QFutureWatcher<int>::suspending);
+ QVERIFY(suspendingSpy.isValid());
+
+ watcher.setFuture(iface.future());
+ watcher.suspend();
+
+ 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.size(), 1);
+
+ watcher.resume();
+ }
+ {
+ QFutureInterface<int> iface;
+ iface.reportStarted();
+
+ QFuture<int> a = iface.future();
+
+ QFutureWatcher<int> watcher;
+
+ SignalSlotObject object;
+ connect(&watcher, &QFutureWatcher<int>::resultReadyAt, &object,
+ &SignalSlotObject::resultReadyAt);
+ QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
+ QVERIFY(resultReadySpy.isValid());
+
+ watcher.setFuture(a);
+ a.suspend();
+
+ int value = 0;
+ iface.reportFinished(&value);
+
+ QFuture<int> b;
+ watcher.setFuture(b); // If we watch b instead, resuming a
+ a.resume(); // should give us no results.
+
+ QTest::qWait(10);
+ QCOMPARE(resultReadySpy.size(), 0);
+ }
+}
+
+void tst_QFutureWatcher::suspended()
+{
QFutureWatcher<int> watcher;
- QSignalSpy startedSpy(&watcher, &QFutureWatcher<int>::started);
+ QSignalSpy resultReadySpy(&watcher, &QFutureWatcher<int>::resultReadyAt);
+#if QT_DEPRECATED_SINCE(6, 0)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ QSignalSpy pausedSpy(&watcher, &QFutureWatcher<int>::paused);
+QT_WARNING_POP
+#endif
+ QSignalSpy suspendingSpy(&watcher, &QFutureWatcher<int>::suspending);
+ QSignalSpy suspendedSpy(&watcher, &QFutureWatcher<int>::suspended);
QSignalSpy finishedSpy(&watcher, &QFutureWatcher<int>::finished);
+ const int numValues = 25;
+ std::vector<int> values(numValues, 0);
+ std::atomic_int count = 0;
+
+ QThreadPool pool;
+ pool.setMaxThreadCount(3);
+
+ QFuture<int> future = QtConcurrent::mapped(&pool, values, [&](int value) {
+ ++count;
+ // Sleep, to make sure not all threads will start at once.
+ QThread::sleep(50ms);
+ return value;
+ });
watcher.setFuture(future);
- QVERIFY(startedSpy.wait());
- iface.reportFinished();
- QVERIFY(future.isFinished());
- QVERIFY(!watcher.isFinished());
+ // Allow some threads to start before suspending.
+ QThread::sleep(200ms);
- QVERIFY(finishedSpy.wait());
- QVERIFY(watcher.isFinished());
+ watcher.suspend();
+ watcher.suspend();
+ 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.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.size();
+ QCOMPARE(resultReadyAfterPaused, count);
+
+ // Make sure no more results are reported before resuming.
+ QThread::sleep(200ms);
+ QCOMPARE(resultReadyAfterPaused, resultReadySpy.size());
+ resultReadySpy.clear();
+
+ watcher.resume();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+
+ // Make sure that no more suspended() signals have been emitted.
+ QCOMPARE(suspendedSpy.size(), 1);
+
+ // Make sure the rest of results were reported after resume.
+ QCOMPARE(resultReadySpy.size(), numValues - resultReadyAfterPaused);
+}
+
+void tst_QFutureWatcher::suspendedEventsOrder()
+{
+ QFutureInterface<void> iface;
+ iface.reportStarted();
+
+ QFutureWatcher<void> watcher;
+
+ QSignalSpy suspendingSpy(&watcher, &QFutureWatcher<void>::suspending);
+ QVERIFY(suspendingSpy.isValid());
+
+ QSignalSpy suspendedSpy(&watcher, &QFutureWatcher<void>::suspended);
+ QVERIFY(suspendedSpy.isValid());
+
+ bool suspendingBeforeSuspended = false;
+ bool notSuspendedBeforeSuspending = false;
+ connect(&watcher, &QFutureWatcher<void>::suspending,
+ [&] { notSuspendedBeforeSuspending = (suspendedSpy.size() == 0); });
+ connect(&watcher, &QFutureWatcher<void>::suspended,
+ [&] { 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.size(), 0);
+ QCOMPARE(suspendedSpy.size(), 0);
+
+ iface.setSuspended(true);
+ iface.reportSuspended();
+
+ QTRY_COMPARE(suspendedSpy.size(), 1);
+ QCOMPARE(suspendingSpy.size(), 1);
+ QVERIFY(notSuspendedBeforeSuspending);
+ QVERIFY(suspendingBeforeSuspended);
+
+ iface.reportFinished();
}
/*
@@ -765,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());
@@ -878,7 +1134,7 @@ void tst_QFutureWatcher::incrementalFilterResults()
void tst_QFutureWatcher::qfutureSynchronizer()
{
int taskCount = 1000;
- QTime t;
+ QElapsedTimer t;
t.start();
{
@@ -907,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
@@ -936,6 +1192,55 @@ void tst_QFutureWatcher::matchFlags()
QCOMPARE(watcher.isFinished(), future.isFinished());
}
+void tst_QFutureWatcher::checkStateConsistency()
+{
+#define CHECK_FAIL(state) \
+ do { \
+ if (QTest::currentTestFailed()) \
+ QFAIL("checkState() failed, QFutureWatcher has inconistent state after " state "!"); \
+ } while (false)
+
+ QFutureWatcher<void> futureWatcher;
+
+ auto checkState = [&futureWatcher] {
+ QCOMPARE(futureWatcher.isStarted(), futureWatcher.future().isStarted());
+ QCOMPARE(futureWatcher.isRunning(), futureWatcher.future().isRunning());
+ QCOMPARE(futureWatcher.isCanceled(), futureWatcher.future().isCanceled());
+ QCOMPARE(futureWatcher.isSuspended(), futureWatcher.future().isSuspended());
+ QCOMPARE(futureWatcher.isSuspending(), futureWatcher.future().isSuspending());
+ QCOMPARE(futureWatcher.isFinished(), futureWatcher.future().isFinished());
+ };
+
+ checkState();
+ CHECK_FAIL("default-constructing");
+
+ QFutureInterface<void> fi;
+ futureWatcher.setFuture(fi.future());
+ checkState();
+ CHECK_FAIL("setting future");
+
+ fi.reportStarted();
+ checkState();
+ CHECK_FAIL("starting");
+
+ fi.future().suspend();
+ checkState();
+ CHECK_FAIL("suspending");
+
+ fi.reportSuspended();
+ checkState();
+ CHECK_FAIL("suspended");
+
+ fi.reportCanceled();
+ checkState();
+ CHECK_FAIL("canceling");
+
+ fi.reportFinished();
+ checkState();
+ CHECK_FAIL("finishing");
+
+#undef CHECK_FAIL
+}
QTEST_MAIN(tst_QFutureWatcher)
#include "tst_qfuturewatcher.moc"
diff --git a/tests/auto/corelib/thread/qmutex/CMakeLists.txt b/tests/auto/corelib/thread/qmutex/CMakeLists.txt
new file mode 100644
index 0000000000..5a92a2bffd
--- /dev/null
+++ b/tests/auto/corelib/thread/qmutex/CMakeLists.txt
@@ -0,0 +1,19 @@
+# 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
+ LIBRARIES
+ Qt::CorePrivate
+)
diff --git a/tests/auto/corelib/thread/qmutex/qmutex.pro b/tests/auto/corelib/thread/qmutex/qmutex.pro
deleted file mode 100644
index cb9d364b71..0000000000
--- a/tests/auto/corelib/thread/qmutex/qmutex.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qmutex
-QT = core testlib
-SOURCES = tst_qmutex.cpp
-win32:QT += core-private
diff --git a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
index 7fb9a861d7..4753444ab9 100644
--- a/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
+++ b/tests/auto/corelib/thread/qmutex/tst_qmutex.cpp
@@ -1,40 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QSemaphore>
#include <qatomic.h>
#include <qcoreapplication.h>
#include <qelapsedtimer.h>
#include <qmutex.h>
#include <qthread.h>
+#include <qvarlengtharray.h>
#include <qwaitcondition.h>
+#include <private/qvolatile_p.h>
+
+using namespace std::chrono_literals;
class tst_QMutex : public QObject
{
@@ -46,11 +26,9 @@ public:
Milliseconds,
Seconds,
};
- Q_ENUM(TimeUnit);
+ Q_ENUM(TimeUnit)
private slots:
- void convertToMilliseconds_data();
- void convertToMilliseconds();
void tryLock_non_recursive();
void try_lock_for_non_recursive();
void try_lock_until_non_recursive();
@@ -68,10 +46,11 @@ private slots:
static const int iterations = 100;
-QAtomicInt lockCount(0);
-QMutex normalMutex, recursiveMutex(QMutex::Recursive);
-QSemaphore testsTurn;
-QSemaphore threadsTurn;
+static QAtomicInt lockCount(0);
+static QMutex normalMutex;
+static QRecursiveMutex recursiveMutex;
+static QSemaphore testsTurn;
+static QSemaphore threadsTurn;
/*
Depending on the OS, tryWaits may return early than expected because of the
@@ -82,128 +61,22 @@ QSemaphore threadsTurn;
enum {
#ifdef Q_OS_WIN
systemTimersResolution = 16,
+#elif defined(Q_OS_QNX)
+ systemTimersResolution = 10,
#else
systemTimersResolution = 1,
#endif
waitTime = 100
};
-#if QT_HAS_INCLUDE(<chrono>)
-static Q_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 !QT_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 !QT_HAS_INCLUDE(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
- QFETCH(TimeUnit, unit);
- QFETCH(double, doubleValue);
- QFETCH(qint64, intValue);
- QFETCH(qint64, expected);
-
- Q_CONSTEXPR qint64 maxShort = std::numeric_limits<short>::max();
- Q_CONSTEXPR qint64 maxInt = std::numeric_limits<int>::max();
- Q_CONSTEXPR qint64 maxUInt = std::numeric_limits<uint>::max();
-
- switch (unit) {
-#define CASE(Unit, Period) \
- case TimeUnit::Unit: \
- DO(double, Period, doubleValue); \
- if (intValue < maxShort) \
- DO(short, Period, short(intValue)); \
- if (intValue < maxInt) \
- DO(int, Period, int(intValue)); \
- DO(qint64, Period, intValue); \
- if (intValue >= 0) { \
- if (intValue < maxUInt) \
- DO(uint, Period, uint(intValue)); \
- DO(quint64, Period, quint64(intValue)); \
- } \
- break
-#define DO(Rep, Period, val) \
- do { \
- const std::chrono::duration<Rep, Period> wait((val)); \
- QCOMPARE(QMutex::convertToMilliseconds(wait), expected); \
- } while (0)
-
- CASE(Nanoseconds, std::nano);
- CASE(Microseconds, std::micro);
- CASE(Milliseconds, std::milli);
- CASE(Seconds, std::ratio<1>);
-#undef DO
-#undef CASE
- }
-#endif
-}
+static constexpr std::chrono::milliseconds waitTimeAsDuration(waitTime);
void tst_QMutex::tryLock_non_recursive()
{
class Thread : public QThread
{
public:
- void run()
+ void run() override
{
testsTurn.release();
@@ -226,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();
@@ -252,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));
@@ -263,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();
@@ -274,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
@@ -323,14 +196,12 @@ void tst_QMutex::tryLock_non_recursive()
thread.wait();
}
-void tst_QMutex::try_lock_for_non_recursive() {
-#if !QT_HAS_INCLUDE(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
+void tst_QMutex::try_lock_for_non_recursive()
+{
class Thread : public QThread
{
public:
- void run()
+ void run() override
{
testsTurn.release();
@@ -353,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();
@@ -379,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));
@@ -390,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();
@@ -441,25 +312,21 @@ 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 !QT_HAS_INCLUDE(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
class Thread : public QThread
{
public:
- void run()
+ void run() override
{
const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution);
testsTurn.release();
@@ -482,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();
@@ -508,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));
@@ -519,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();
@@ -570,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()
@@ -585,7 +451,7 @@ void tst_QMutex::tryLock_recursive()
class Thread : public QThread
{
public:
- void run()
+ void run() override
{
testsTurn.release();
@@ -608,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));
@@ -633,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));
@@ -706,15 +572,11 @@ void tst_QMutex::tryLock_recursive()
void tst_QMutex::try_lock_for_recursive()
{
-#if !QT_HAS_INCLUDE(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
class Thread : public QThread
{
public:
- void run()
+ void run() override
{
- const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution);
testsTurn.release();
threadsTurn.acquire();
@@ -736,14 +598,14 @@ void tst_QMutex::try_lock_for_recursive()
QElapsedTimer timer;
timer.start();
QVERIFY(!recursiveMutex.try_lock_for(waitTimeAsDuration));
- QVERIFY(timer.elapsed() >= waitTime - systemTimersResolution);
+ QCOMPARE_GE(timer.elapsed(), waitTime - systemTimersResolution);
QVERIFY(!recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
testsTurn.release();
threadsTurn.acquire();
timer.start();
QVERIFY(recursiveMutex.try_lock_for(waitTimeAsDuration));
- QVERIFY(timer.elapsed() <= waitTime + systemTimersResolution);
+ QCOMPARE_LE(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(recursiveMutex.try_lock_for(waitTimeAsDuration));
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
@@ -761,7 +623,7 @@ void tst_QMutex::try_lock_for_recursive()
threadsTurn.acquire();
timer.start();
QVERIFY(recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
- QVERIFY(timer.elapsed() < waitTime + systemTimersResolution);
+ QCOMPARE_LT(timer.elapsed(), waitTime + systemTimersResolution);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(recursiveMutex.try_lock_for(std::chrono::milliseconds::zero()));
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
@@ -830,18 +692,14 @@ void tst_QMutex::try_lock_for_recursive()
testsTurn.acquire();
threadsTurn.release();
thread.wait();
-#endif
}
void tst_QMutex::try_lock_until_recursive()
{
-#if !QT_HAS_INCLUDE(<chrono>)
- QSKIP("This test requires <chrono>");
-#else
class Thread : public QThread
{
public:
- void run()
+ void run() override
{
const std::chrono::milliseconds systemTimersResolutionAsDuration(systemTimersResolution);
testsTurn.release();
@@ -864,14 +722,14 @@ void tst_QMutex::try_lock_until_recursive()
threadsTurn.acquire();
auto endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(!recursiveMutex.try_lock_until(endTimePoint));
- QVERIFY(std::chrono::steady_clock::now() >= endTimePoint - systemTimersResolutionAsDuration);
+ QCOMPARE_GE(std::chrono::steady_clock::now(), endTimePoint - systemTimersResolutionAsDuration);
QVERIFY(!recursiveMutex.try_lock());
testsTurn.release();
threadsTurn.acquire();
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(recursiveMutex.try_lock_until(endTimePoint));
- QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration);
+ QCOMPARE_LE(std::chrono::steady_clock::now(), endTimePoint + systemTimersResolutionAsDuration);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(recursiveMutex.try_lock_until(endTimePoint));
@@ -890,7 +748,7 @@ void tst_QMutex::try_lock_until_recursive()
threadsTurn.acquire();
endTimePoint = std::chrono::steady_clock::now() + waitTimeAsDuration;
QVERIFY(recursiveMutex.try_lock_until(std::chrono::steady_clock::now()));
- QVERIFY(std::chrono::steady_clock::now() <= endTimePoint + systemTimersResolutionAsDuration);
+ QCOMPARE_LE(std::chrono::steady_clock::now(), endTimePoint + systemTimersResolutionAsDuration);
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
QVERIFY(recursiveMutex.try_lock_until(std::chrono::steady_clock::now()));
QVERIFY(lockCount.testAndSetRelaxed(1, 2));
@@ -959,7 +817,6 @@ void tst_QMutex::try_lock_until_recursive()
testsTurn.acquire();
threadsTurn.release();
thread.wait();
-#endif
}
class mutex_Thread : public QThread
@@ -972,7 +829,7 @@ public:
inline mutex_Thread(QMutex &m) : test_mutex(m) { }
- void run()
+ void run() override
{
test_mutex.lock();
@@ -993,11 +850,11 @@ public:
QMutex mutex;
QWaitCondition cond;
- QMutex &test_mutex;
+ QRecursiveMutex &test_mutex;
- inline rmutex_Thread(QMutex &m) : test_mutex(m) { }
+ inline rmutex_Thread(QRecursiveMutex &m) : test_mutex(m) { }
- void run()
+ void run() override
{
test_mutex.lock();
test_mutex.lock();
@@ -1024,7 +881,7 @@ void tst_QMutex::lock_unlock_locked_tryLock()
QMutex mutex;
mutex_Thread thread(mutex);
- QMutex rmutex(QMutex::Recursive);
+ QRecursiveMutex rmutex;
rmutex_Thread rthread(rmutex);
for (int i = 0; i < iterations; ++i) {
@@ -1085,8 +942,8 @@ void tst_QMutex::lock_unlock_locked_tryLock()
}
}
-enum { one_minute = 6 * 1000, //not really one minute, but else it is too long.
- threadCount = 10 };
+constexpr int one_minute = 6 * 1000; // not really one minute, but else it is too long.
+constexpr int threadCount = 10;
class StressTestThread : public QThread
{
@@ -1101,7 +958,7 @@ public:
t.start();
QThread::start();
}
- void run()
+ void run() override
{
while (t.elapsed() < one_minute) {
mutex.lock();
@@ -1132,7 +989,7 @@ void tst_QMutex::stressTest()
for (int i = 1; i < threadCount; ++i)
QVERIFY(threads[i].wait(10000));
QCOMPARE(StressTestThread::errorCount, 0);
- qDebug("locked %d times", int(StressTestThread::lockCount.load()));
+ qDebug("locked %d times", int(StressTestThread::lockCount.loadRelaxed()));
}
class TryLockRaceThread : public QThread
@@ -1140,7 +997,7 @@ class TryLockRaceThread : public QThread
public:
static QMutex mutex;
- void run()
+ void run() override
{
QElapsedTimer t;
t.start();
@@ -1186,12 +1043,13 @@ void tst_QMutex::tryLockDeadlock()
struct TrylockThread : QThread {
TrylockThread(QMutex &mut) : mut(mut) {}
QMutex &mut;
- void run() {
+ void run() override
+ {
for (int i = 0; i < 100000; ++i) {
if (mut.tryLock(0)) {
- if ((++tryLockDeadlockCounter) != 1)
+ if (QtPrivate::volatilePreIncrement(tryLockDeadlockCounter) != 1)
++tryLockDeadlockFailureCount;
- if ((--tryLockDeadlockCounter) != 0)
+ if (QtPrivate::volatilePreDecrement(tryLockDeadlockCounter) != 0)
++tryLockDeadlockFailureCount;
mut.unlock();
}
@@ -1208,9 +1066,9 @@ void tst_QMutex::tryLockDeadlock()
for (int i = 0; i < 100000; ++i) {
mut.lock();
- if ((++tryLockDeadlockCounter) != 1)
+ if (QtPrivate::volatilePreIncrement(tryLockDeadlockCounter) != 1)
++tryLockDeadlockFailureCount;
- if ((--tryLockDeadlockCounter) != 0)
+ if (QtPrivate::volatilePreDecrement(tryLockDeadlockCounter) != 0)
++tryLockDeadlockFailureCount;
mut.unlock();
}
@@ -1239,7 +1097,8 @@ void tst_QMutex::tryLockNegative()
QMutex &mut;
int timeout;
int tryLockResult;
- void run() {
+ void run() override
+ {
tryLockResult = mut.tryLock(timeout);
mut.unlock();
}
@@ -1281,33 +1140,33 @@ public:
t.start();
QThread::start();
}
- void run()
+ void run() override
{
quint64 i = 0;
while (t.elapsed() < one_minute) {
i++;
- uint nb = (i * 9 + lockCount.load() * 13) % threadCount;
+ uint nb = (i * 9 + uint(lockCount.loadRelaxed()) * 13) % threadCount;
QMutexLocker locker(&mutex[nb]);
- if (sentinel[nb].load()) errorCount.ref();
+ if (sentinel[nb].loadRelaxed()) errorCount.ref();
if (sentinel[nb].fetchAndAddRelaxed(5)) errorCount.ref();
if (!sentinel[nb].testAndSetRelaxed(5, 0)) errorCount.ref();
- if (sentinel[nb].load()) errorCount.ref();
+ if (sentinel[nb].loadRelaxed()) errorCount.ref();
lockCount.ref();
- nb = (nb * 17 + i * 5 + lockCount.load() * 3) % threadCount;
+ nb = (nb * 17 + i * 5 + uint(lockCount.loadRelaxed()) * 3) % threadCount;
if (mutex[nb].tryLock()) {
- if (sentinel[nb].load()) errorCount.ref();
+ if (sentinel[nb].loadRelaxed()) errorCount.ref();
if (sentinel[nb].fetchAndAddRelaxed(16)) errorCount.ref();
if (!sentinel[nb].testAndSetRelaxed(16, 0)) errorCount.ref();
- if (sentinel[nb].load()) errorCount.ref();
+ if (sentinel[nb].loadRelaxed()) errorCount.ref();
lockCount.ref();
mutex[nb].unlock();
}
- nb = (nb * 15 + i * 47 + lockCount.load() * 31) % threadCount;
+ nb = (nb * 15 + i * 47 + uint(lockCount.loadRelaxed()) * 31) % threadCount;
if (mutex[nb].tryLock(2)) {
- if (sentinel[nb].load()) errorCount.ref();
+ if (sentinel[nb].loadRelaxed()) errorCount.ref();
if (sentinel[nb].fetchAndAddRelaxed(53)) errorCount.ref();
if (!sentinel[nb].testAndSetRelaxed(53, 0)) errorCount.ref();
- if (sentinel[nb].load()) errorCount.ref();
+ if (sentinel[nb].loadRelaxed()) errorCount.ref();
lockCount.ref();
mutex[nb].unlock();
}
@@ -1321,14 +1180,15 @@ 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));
- qDebug("locked %d times", MoreStressTestThread::lockCount.load());
- QCOMPARE(MoreStressTestThread::errorCount.load(), 0);
+ 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
new file mode 100644
index 0000000000..7b2d6dc1c2
--- /dev/null
+++ b/tests/auto/corelib/thread/qmutexlocker/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qmutexlocker.pro b/tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro
deleted file mode 100644
index 76ec0471ca..0000000000
--- a/tests/auto/corelib/thread/qmutexlocker/qmutexlocker.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qmutexlocker
-QT = core testlib
-SOURCES = tst_qmutexlocker.cpp
diff --git a/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
index 67db5d1458..6ccab04c27 100644
--- a/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
+++ b/tests/auto/corelib/thread/qmutexlocker/tst_qmutexlocker.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QCoreApplication>
#include <QMutexLocker>
@@ -36,7 +11,7 @@
class tst_QMutexLockerThread : public QThread
{
public:
- QMutex mutex;
+ QRecursiveMutex mutex;
QSemaphore semaphore, testSemaphore;
void waitForTest()
@@ -45,10 +20,6 @@ public:
testSemaphore.acquire();
}
- tst_QMutexLockerThread()
- : mutex(QMutex::Recursive)
- {
- }
};
class tst_QMutexLocker : public QObject
@@ -71,6 +42,7 @@ private slots:
void scopeTest();
void unlockAndRelockTest();
void lockerStateTest();
+ void moveSemantics();
};
void tst_QMutexLocker::scopeTest()
@@ -78,12 +50,13 @@ void tst_QMutexLocker::scopeTest()
class ScopeTestThread : public tst_QMutexLockerThread
{
public:
- void run()
+ void run() override
{
waitForTest();
{
QMutexLocker locker(&mutex);
+ QVERIFY(locker.isLocked());
waitForTest();
}
@@ -114,7 +87,7 @@ void tst_QMutexLocker::scopeTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
@@ -123,19 +96,26 @@ void tst_QMutexLocker::unlockAndRelockTest()
class UnlockAndRelockThread : public tst_QMutexLockerThread
{
public:
- void run()
+ void run() override
{
QMutexLocker locker(&mutex);
+ QVERIFY(locker.isLocked());
waitForTest();
+ QVERIFY(locker.isLocked());
locker.unlock();
+ QVERIFY(!locker.isLocked());
waitForTest();
+ QVERIFY(!locker.isLocked());
locker.relock();
+ QVERIFY(locker.isLocked());
waitForTest();
+
+ QVERIFY(locker.isLocked());
}
};
@@ -161,7 +141,7 @@ void tst_QMutexLocker::unlockAndRelockTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
void tst_QMutexLocker::lockerStateTest()
@@ -169,14 +149,17 @@ void tst_QMutexLocker::lockerStateTest()
class LockerStateThread : public tst_QMutexLockerThread
{
public:
- void run()
+ void run() override
{
{
QMutexLocker locker(&mutex);
- locker.relock();
+ QVERIFY(locker.isLocked());
+
locker.unlock();
+ QVERIFY(!locker.isLocked());
waitForTest();
+ QVERIFY(!locker.isLocked());
}
waitForTest();
@@ -201,7 +184,73 @@ void tst_QMutexLocker::lockerStateTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
+}
+
+void tst_QMutexLocker::moveSemantics()
+{
+ {
+ QMutexLocker<QMutex> locker(nullptr);
+ QVERIFY(!locker.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+
+ QMutexLocker locker2(std::move(locker));
+ QVERIFY(!locker.isLocked());
+ QVERIFY(!locker2.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+ QCOMPARE(locker2.mutex(), nullptr);
+ }
+
+ QMutex mutex;
+
+ {
+ QMutexLocker locker(&mutex);
+ QVERIFY(locker.isLocked());
+ QCOMPARE(locker.mutex(), &mutex);
+ QVERIFY(!mutex.tryLock());
+
+ QMutexLocker locker2(std::move(locker));
+ QVERIFY(!locker.isLocked());
+ QVERIFY(locker2.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+ QCOMPARE(locker2.mutex(), &mutex);
+ QVERIFY(!mutex.tryLock());
+ }
+
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
+
+ {
+ QMutex mutex;
+ QMutexLocker locker(&mutex);
+ QVERIFY(locker.isLocked());
+ QCOMPARE(locker.mutex(), &mutex);
+ QVERIFY(!mutex.tryLock());
+
+ locker.unlock();
+ QVERIFY(!locker.isLocked());
+ QCOMPARE(locker.mutex(), &mutex);
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
+
+ QMutexLocker locker2(std::move(locker));
+ QVERIFY(!locker.isLocked());
+ QVERIFY(!locker2.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+ QCOMPARE(locker2.mutex(), &mutex);
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
+
+ locker2.relock();
+ QVERIFY(!locker.isLocked());
+ QVERIFY(locker2.isLocked());
+ QCOMPARE(locker.mutex(), nullptr);
+ QCOMPARE(locker2.mutex(), &mutex);
+ QVERIFY(!mutex.tryLock());
+ }
+
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
}
QTEST_MAIN(tst_QMutexLocker)
diff --git a/tests/auto/corelib/thread/qpromise/CMakeLists.txt b/tests/auto/corelib/thread/qpromise/CMakeLists.txt
new file mode 100644
index 0000000000..c1ca30b34a
--- /dev/null
+++ b/tests/auto/corelib/thread/qpromise/CMakeLists.txt
@@ -0,0 +1,19 @@
+# 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
+ LIBRARIES
+ Qt::CorePrivate
+)
diff --git a/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
new file mode 100644
index 0000000000..2cef0dca95
--- /dev/null
+++ b/tests/auto/corelib/thread/qpromise/snippet_qpromise.cpp
@@ -0,0 +1,158 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+// Note: this file is published under a license that is different from a default
+// test sources license. This is intentional to comply with default
+// snippet license.
+
+#include <QCoreApplication>
+#include <QTest>
+
+#include <qfuture.h>
+#include <qfuturewatcher.h>
+#include <qpromise.h>
+#include <qscopedpointer.h>
+#include <qsharedpointer.h>
+
+class snippet_QPromise
+{
+public:
+ static void basicExample();
+ static void multithreadExample();
+ static void suspendExample();
+};
+
+void snippet_QPromise::basicExample()
+{
+#if QT_CONFIG(cxx11_future)
+//! [basic]
+ QPromise<int> promise;
+ QFuture<int> future = promise.future();
+
+ QScopedPointer<QThread> thread(QThread::create([] (QPromise<int> promise) {
+ promise.start(); // notifies QFuture that the computation is started
+ promise.addResult(42);
+ promise.finish(); // notifies QFuture that the computation is finished
+ }, std::move(promise)));
+ thread->start();
+
+ future.waitForFinished(); // blocks until QPromise::finish is called
+ future.result(); // returns 42
+//! [basic]
+
+ QCOMPARE(future.result(), 42);
+ thread->wait();
+#endif
+}
+
+void snippet_QPromise::multithreadExample()
+{
+#if QT_CONFIG(cxx11_future)
+//! [multithread_init]
+ QSharedPointer<QPromise<int>> sharedPromise(new QPromise<int>());
+ QFuture<int> future = sharedPromise->future();
+
+ // ...
+
+ sharedPromise->start();
+//! [multithread_init]
+
+//! [multithread_main]
+ // here, QPromise is shared between threads via a smart pointer
+ QScopedPointer<QThread> threads[] = {
+ QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) {
+ sharedPromise->addResult(0, 0); // adds value 0 by index 0
+ }, sharedPromise)),
+ QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) {
+ sharedPromise->addResult(-1, 1); // adds value -1 by index 1
+ }, sharedPromise)),
+ QScopedPointer<QThread>(QThread::create([] (auto sharedPromise) {
+ sharedPromise->addResult(-2, 2); // adds value -2 by index 2
+ }, sharedPromise)),
+ // ...
+ };
+ // start all threads
+ for (auto& t : threads)
+ t->start();
+
+ // ...
+
+ future.resultAt(0); // waits until result at index 0 becomes available. returns value 0
+ future.resultAt(1); // waits until result at index 1 becomes available. returns value -1
+ future.resultAt(2); // waits until result at index 2 becomes available. returns value -2
+//! [multithread_main]
+
+ QCOMPARE(future.resultAt(0), 0);
+ QCOMPARE(future.resultAt(1), -1);
+ QCOMPARE(future.resultAt(2), -2);
+
+ for (auto& t : threads)
+ t->wait();
+//! [multithread_cleanup]
+ sharedPromise->finish();
+//! [multithread_cleanup]
+#endif
+}
+
+void snippet_QPromise::suspendExample()
+{
+#if QT_CONFIG(cxx11_future)
+//! [suspend_start]
+ // Create promise and future
+ QPromise<int> promise;
+ QFuture<int> future = promise.future();
+
+ promise.start();
+ // Start a computation thread that supports suspension and cancellation
+ QScopedPointer<QThread> thread(QThread::create([] (QPromise<int> promise) {
+ for (int i = 0; i < 100; ++i) {
+ promise.addResult(i);
+ promise.suspendIfRequested(); // support suspension
+ if (promise.isCanceled()) // support cancellation
+ break;
+ }
+ promise.finish();
+ }, std::move(promise)));
+ thread->start();
+//! [suspend_start]
+
+//! [suspend_suspend]
+ future.suspend();
+//! [suspend_suspend]
+
+ // wait in calling thread until future.isSuspended() becomes true or do
+ // something meanwhile
+ while (!future.isSuspended()) {
+ QThread::msleep(50);
+ }
+
+//! [suspend_intermediateResults]
+ future.resultCount(); // returns some number between 0 and 100
+ for (int i = 0; i < future.resultCount(); ++i) {
+ // process results available before suspension
+ }
+//! [suspend_intermediateResults]
+
+ // at least one result is available due to the logic inside a thread
+ QVERIFY(future.resultCount() > 0);
+ QVERIFY(future.resultCount() <= 100);
+ for (int i = 0; i < future.resultCount(); ++i) {
+ QCOMPARE(future.resultAt(i), i);
+ }
+
+//! [suspend_end]
+ future.resume(); // resumes computation, this call will unblock the promise
+ // alternatively, call future.cancel() to stop the computation
+
+ future.waitForFinished();
+ future.results(); // returns all computation results - array of values from 0 to 99
+//! [suspend_end]
+
+ thread->wait();
+
+ QCOMPARE(future.resultCount(), 100);
+ QList<int> expected(100);
+ std::iota(expected.begin(), expected.end(), 0);
+ QCOMPARE(future.results(), expected);
+#endif
+}
diff --git a/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
new file mode 100644
index 0000000000..2c12e41c93
--- /dev/null
+++ b/tests/auto/corelib/thread/qpromise/tst_qpromise.cpp
@@ -0,0 +1,784 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QCoreApplication>
+#include <QDebug>
+
+#define QPROMISE_TEST
+
+#include <QTest>
+#include <qfuture.h>
+#include <qfuturewatcher.h>
+#include <qpromise.h>
+
+#include <algorithm>
+#include <memory>
+#include <chrono>
+
+using namespace std::chrono_literals;
+
+class tst_QPromise : public QObject
+{
+ Q_OBJECT
+private slots:
+ // simple test cases
+ void promise();
+ void futureFromPromise();
+ void addResult();
+ void addResultWithBracedInitializer();
+ void addResultOutOfOrder();
+#ifndef QT_NO_EXCEPTIONS
+ void setException();
+#endif
+ void cancel();
+ void progress();
+
+ // complicated test cases
+ void addInThread();
+ void addInThreadMoveOnlyObject(); // separate test case - QTBUG-84736
+ void reportFromMultipleThreads();
+ void reportFromMultipleThreadsByMovedPromise();
+ void doNotCancelWhenFinished();
+#ifndef QT_NO_EXCEPTIONS
+ void cancelWhenDestroyed();
+#endif
+ void cancelWhenReassigned();
+ void cancelWhenDestroyedWithoutStarting();
+ void cancelWhenDestroyedRunsContinuations();
+ void cancelWhenDestroyedWithFailureHandler(); // QTBUG-114606
+ void continuationsRunWhenFinished();
+ void finishWhenSwapped();
+ void cancelWhenMoved();
+ void waitUntilResumed();
+ void waitUntilCanceled();
+
+ // snippets (external):
+ void snippet_basicExample();
+ void snippet_multithreadExample();
+ void snippet_suspendExample();
+};
+
+struct TrivialType { int field = 0; };
+struct CopyOnlyType {
+ constexpr CopyOnlyType(int field = 0) noexcept : field(field) {}
+ CopyOnlyType(const CopyOnlyType &) = default;
+ CopyOnlyType& operator=(const CopyOnlyType &) = default;
+ ~CopyOnlyType() = default;
+
+ int field;
+};
+struct MoveOnlyType {
+ Q_DISABLE_COPY(MoveOnlyType)
+ constexpr MoveOnlyType(int field = 0) noexcept : field(field) {}
+ MoveOnlyType(MoveOnlyType &&) = default;
+ MoveOnlyType& operator=(MoveOnlyType &&) = default;
+ ~MoveOnlyType() = default;
+
+ int field;
+};
+bool operator==(const CopyOnlyType &a, const CopyOnlyType &b) { return a.field == b.field; }
+bool operator==(const MoveOnlyType &a, const MoveOnlyType &b) { return a.field == b.field; }
+
+// A wrapper for a test function, calls the function, if it fails, reports failure
+#define RUN_TEST_FUNC(test, ...) \
+do { \
+ test(__VA_ARGS__); \
+ if (QTest::currentTestFailed()) \
+ QFAIL("Test case " #test "(" #__VA_ARGS__ ") failed"); \
+} while (false)
+
+#if QT_CONFIG(cxx11_future)
+// std::thread-like wrapper that ensures that the thread is joined at the end of
+// a scope to prevent potential std::terminate
+struct ThreadWrapper
+{
+ std::unique_ptr<QThread> t;
+ template<typename Function>
+ ThreadWrapper(Function &&f) : t(QThread::create(std::forward<Function>(f)))
+ {
+ t->start();
+ }
+ void join() { t->wait(); }
+ ~ThreadWrapper()
+ {
+ t->wait();
+ }
+};
+#endif
+
+void tst_QPromise::promise()
+{
+ const auto testCanCreatePromise = [] (auto promise) {
+ promise.start();
+ promise.suspendIfRequested(); // should not block on its own
+ promise.finish();
+ };
+
+ RUN_TEST_FUNC(testCanCreatePromise, QPromise<void>());
+ RUN_TEST_FUNC(testCanCreatePromise, QPromise<int>());
+ RUN_TEST_FUNC(testCanCreatePromise, QPromise<QList<float>>());
+ RUN_TEST_FUNC(testCanCreatePromise, QPromise<TrivialType>());
+ RUN_TEST_FUNC(testCanCreatePromise, QPromise<CopyOnlyType>());
+ RUN_TEST_FUNC(testCanCreatePromise, QPromise<MoveOnlyType>());
+}
+
+void tst_QPromise::futureFromPromise()
+{
+ const auto testCanCreateFutureFromPromise = [] (auto promise) {
+ auto future = promise.future();
+ QVERIFY(!future.isValid());
+
+ promise.start();
+ QCOMPARE(future.isStarted(), true);
+ QVERIFY(future.isValid());
+
+ promise.finish();
+ QCOMPARE(future.isFinished(), true);
+ QVERIFY(future.isValid());
+
+ future.waitForFinished();
+ };
+
+ RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<void>());
+ RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<double>());
+ RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<QList<int>>());
+ RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<TrivialType>());
+ RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<CopyOnlyType>());
+ RUN_TEST_FUNC(testCanCreateFutureFromPromise, QPromise<MoveOnlyType>());
+}
+
+void tst_QPromise::addResult()
+{
+ QPromise<int> promise;
+ auto f = promise.future();
+
+ // add as lvalue
+ int resultAt0 = 456;
+ {
+ QVERIFY(promise.addResult(resultAt0));
+ QCOMPARE(f.resultCount(), 1);
+ QCOMPARE(f.result(), resultAt0);
+ QCOMPARE(f.resultAt(0), resultAt0);
+ }
+ // add as rvalue
+ {
+ int result = 789;
+ QVERIFY(promise.addResult(789));
+ QCOMPARE(f.resultCount(), 2);
+ QCOMPARE(f.resultAt(1), result);
+ }
+ // add at position
+ {
+ int result = 56238;
+ QVERIFY(promise.addResult(result, 2));
+ QCOMPARE(f.resultCount(), 3);
+ QCOMPARE(f.resultAt(2), result);
+ }
+ // add multiple results in one go:
+ {
+ QList results = {42, 4242, 424242};
+ QVERIFY(promise.addResults(results));
+ QCOMPARE(f.resultCount(), 6);
+ QCOMPARE(f.resultAt(3), 42);
+ QCOMPARE(f.resultAt(4), 4242);
+ QCOMPARE(f.resultAt(5), 424242);
+ }
+ // add as lvalue at position and overwrite
+ {
+ int result = -1;
+ const auto originalCount = f.resultCount();
+ QVERIFY(!promise.addResult(result, 0));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), resultAt0); // overwrite does not work
+ }
+ // add as rvalue at position and overwrite
+ {
+ const auto originalCount = f.resultCount();
+ QVERIFY(!promise.addResult(-1, 0));
+ QCOMPARE(f.resultCount(), originalCount);
+ QCOMPARE(f.resultAt(0), resultAt0); // overwrite does not work
+ }
+}
+
+void tst_QPromise::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
+ const auto compareResults = [] (const auto &future, auto expected) {
+ QCOMPARE(future.resultCount(), expected.size());
+ // index based loop
+ for (int i = 0; i < future.resultCount(); ++i)
+ QCOMPARE(future.resultAt(i), expected.at(i));
+ // iterator based loop
+ QVERIFY(std::equal(future.begin(), future.end(), expected.begin()));
+ };
+
+ // out of order results without a gap
+ {
+ QPromise<int> promise;
+ auto f = promise.future();
+ QVERIFY(promise.addResult(456, 1));
+ QCOMPARE(f.resultCount(), 0);
+ QVERIFY(promise.addResult(123, 0));
+
+ QList<int> expected({123, 456});
+ RUN_TEST_FUNC(compareResults, f, expected);
+ QCOMPARE(f.results(), expected);
+ }
+
+ // out of order results with a gap that is closed "later"
+ {
+ QPromise<int> promise;
+ auto f = promise.future();
+ QVERIFY(promise.addResult(0, 0));
+ QVERIFY(promise.addResult(1, 1));
+ QVERIFY(promise.addResult(3, 3)); // intentional gap here
+
+ QList<int> expectedWhenGapExists({0, 1});
+ RUN_TEST_FUNC(compareResults, f, expectedWhenGapExists);
+ QCOMPARE(f.resultAt(3), 3);
+
+ QList<int> expectedWhenNoGap({0, 1, 2, 3});
+ QVERIFY(promise.addResult(2, 2)); // fill a gap with a value
+ RUN_TEST_FUNC(compareResults, f, expectedWhenNoGap);
+ QCOMPARE(f.results(), expectedWhenNoGap);
+ }
+}
+
+#ifndef QT_NO_EXCEPTIONS
+void tst_QPromise::setException()
+{
+ struct TestException {}; // custom exception class
+ const auto testExceptionCaught = [] (auto promise, const auto& exception) {
+ auto f = promise.future();
+ promise.start();
+ promise.setException(exception);
+ promise.finish();
+
+ bool caught = false;
+ try {
+ f.waitForFinished();
+ } catch (const QException&) {
+ caught = true;
+ } catch (const TestException&) {
+ caught = true;
+ }
+ QVERIFY(caught);
+ };
+
+ RUN_TEST_FUNC(testExceptionCaught, QPromise<void>(), QException());
+ RUN_TEST_FUNC(testExceptionCaught, QPromise<int>(), QException());
+ RUN_TEST_FUNC(testExceptionCaught, QPromise<void>(),
+ std::make_exception_ptr(TestException()));
+ RUN_TEST_FUNC(testExceptionCaught, QPromise<int>(),
+ std::make_exception_ptr(TestException()));
+ RUN_TEST_FUNC(testExceptionCaught, QPromise<CopyOnlyType>(),
+ std::make_exception_ptr(TestException()));
+ RUN_TEST_FUNC(testExceptionCaught, QPromise<MoveOnlyType>(),
+ std::make_exception_ptr(TestException()));
+}
+#endif
+
+void tst_QPromise::cancel()
+{
+ const auto testCancel = [] (auto promise) {
+ auto f = promise.future();
+ f.cancel();
+ QCOMPARE(promise.isCanceled(), true);
+ };
+
+ testCancel(QPromise<void>());
+ testCancel(QPromise<int>());
+ testCancel(QPromise<CopyOnlyType>());
+ testCancel(QPromise<MoveOnlyType>());
+}
+
+void tst_QPromise::progress()
+{
+ const auto testProgress = [] (auto promise) {
+ auto f = promise.future();
+
+ promise.setProgressRange(0, 2);
+ QCOMPARE(f.progressMinimum(), 0);
+ QCOMPARE(f.progressMaximum(), 2);
+
+ QCOMPARE(f.progressValue(), 0);
+ promise.setProgressValue(1);
+ QCOMPARE(f.progressValue(), 1);
+ promise.setProgressValue(0); // decrement
+ QCOMPARE(f.progressValue(), 1);
+ promise.setProgressValue(10); // out of range
+ QCOMPARE(f.progressValue(), 1);
+
+ promise.setProgressRange(0, 100);
+ promise.setProgressValueAndText(50, u8"50%");
+ QCOMPARE(f.progressValue(), 50);
+ QCOMPARE(f.progressText(), u8"50%");
+ };
+
+ RUN_TEST_FUNC(testProgress, QPromise<void>());
+ RUN_TEST_FUNC(testProgress, QPromise<int>());
+ RUN_TEST_FUNC(testProgress, QPromise<CopyOnlyType>());
+ RUN_TEST_FUNC(testProgress, QPromise<MoveOnlyType>());
+}
+
+void tst_QPromise::addInThread()
+{
+#if QT_CONFIG(cxx11_future)
+ const auto testAddResult = [] (auto promise, const auto &result) {
+ promise.start();
+ auto f = promise.future();
+ // move construct QPromise
+ ThreadWrapper thr([p = std::move(promise), &result] () mutable {
+ p.addResult(result);
+ });
+ // Waits for result first
+ QCOMPARE(f.result(), result);
+ QCOMPARE(f.resultAt(0), result);
+ };
+
+ RUN_TEST_FUNC(testAddResult, QPromise<int>(), 42);
+ RUN_TEST_FUNC(testAddResult, QPromise<QString>(), u8"42");
+ RUN_TEST_FUNC(testAddResult, QPromise<CopyOnlyType>(), CopyOnlyType{99});
+#endif
+}
+
+void tst_QPromise::addInThreadMoveOnlyObject()
+{
+#if QT_CONFIG(cxx11_future)
+ QPromise<MoveOnlyType> promise;
+ promise.start();
+ auto f = promise.future();
+
+ ThreadWrapper thr([p = std::move(promise)] () mutable {
+ p.addResult(MoveOnlyType{-11});
+ });
+
+ // Iterators wait for result first
+ for (auto& result : f)
+ QCOMPARE(result, MoveOnlyType{-11});
+#endif
+}
+
+void tst_QPromise::reportFromMultipleThreads()
+{
+#if QT_CONFIG(cxx11_future)
+ QPromise<int> promise;
+ auto f = promise.future();
+ promise.start();
+
+ ThreadWrapper threads[] = {
+ ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
+ ThreadWrapper([&promise] () mutable { promise.addResult(43); }),
+ ThreadWrapper([&promise] () mutable { promise.addResult(44); }),
+ };
+ for (auto& t : threads)
+ t.join();
+ promise.finish();
+
+ QList<int> expected = {42, 43, 44};
+ for (auto actual : f.results()) {
+ QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end());
+ expected.removeOne(actual);
+ }
+#endif
+}
+
+void tst_QPromise::reportFromMultipleThreadsByMovedPromise()
+{
+#if QT_CONFIG(cxx11_future)
+ QPromise<int> initialPromise;
+ auto f = initialPromise.future();
+ {
+ // Move QPromise into local scope: local QPromise (as being
+ // move-constructed) must be able to set results, QFuture must still
+ // hold correct references to results.
+ auto promise = std::move(initialPromise);
+ promise.start();
+ ThreadWrapper threads[] = {
+ ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
+ ThreadWrapper([&promise] () mutable { promise.addResult(43); }),
+ ThreadWrapper([&promise] () mutable { promise.addResult(44); }),
+ };
+ for (auto& t : threads)
+ t.join();
+ promise.finish();
+ }
+
+ QCOMPARE(f.isFinished(), true);
+ QCOMPARE(f.isValid(), true);
+
+ QList<int> expected = {42, 43, 44};
+ for (auto actual : f.results()) {
+ QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end());
+ expected.removeOne(actual);
+ }
+#endif
+}
+
+void tst_QPromise::doNotCancelWhenFinished()
+{
+#if QT_CONFIG(cxx11_future)
+ const auto testFinishedPromise = [] (auto promise) {
+ auto f = promise.future();
+ promise.start();
+
+ // Finish QPromise inside thread, destructor must not call cancel()
+ ThreadWrapper([p = std::move(promise)] () mutable { p.finish(); }).join();
+
+ f.waitForFinished();
+
+ QCOMPARE(f.isFinished(), true);
+ QCOMPARE(f.isCanceled(), false);
+ };
+
+ RUN_TEST_FUNC(testFinishedPromise, QPromise<void>());
+ RUN_TEST_FUNC(testFinishedPromise, QPromise<int>());
+ RUN_TEST_FUNC(testFinishedPromise, QPromise<QString>());
+ RUN_TEST_FUNC(testFinishedPromise, QPromise<CopyOnlyType>());
+ RUN_TEST_FUNC(testFinishedPromise, QPromise<MoveOnlyType>());
+#endif
+}
+
+#ifndef QT_NO_EXCEPTIONS
+void tst_QPromise::cancelWhenDestroyed()
+{
+#if QT_CONFIG(cxx11_future)
+ QPromise<int> initialPromise;
+ auto f = initialPromise.future();
+
+ try {
+ // Move QPromise to local scope. On destruction, it must call cancel().
+ auto promise = std::move(initialPromise);
+ promise.start();
+ ThreadWrapper threads[] = {
+ ThreadWrapper([&promise] () mutable { promise.addResult(42); }),
+ ThreadWrapper([&promise] () mutable { promise.addResult(43); }),
+ ThreadWrapper([&promise] () mutable { promise.addResult(44); }),
+ };
+ for (auto& t : threads)
+ t.join();
+ throw "Throw in the middle, we lose our promise here, finish() not called!";
+ promise.finish();
+ } catch (...) {}
+
+ QCOMPARE(f.isFinished(), true);
+ QCOMPARE(f.isCanceled(), true);
+
+ // Results are still available despite throw
+ QList<int> expected = {42, 43, 44};
+ for (auto actual : f.results()) {
+ QVERIFY(std::find(expected.begin(), expected.end(), actual) != expected.end());
+ expected.removeOne(actual);
+ }
+#endif
+}
+#endif
+
+void tst_QPromise::cancelWhenReassigned()
+{
+#if QT_CONFIG(cxx11_future)
+ QPromise<int> promise;
+ auto f = promise.future();
+ promise.start();
+
+ ThreadWrapper thr([p = std::move(promise)] () mutable {
+ QThread::sleep(100ms);
+ p = QPromise<int>(); // assign new promise, old must be correctly destroyed
+ });
+
+ f.waitForFinished(); // wait for the old promise
+
+ QCOMPARE(f.isFinished(), true);
+ QCOMPARE(f.isCanceled(), true);
+#endif
+}
+
+template <typename T>
+static inline void testCancelWhenDestroyedWithoutStarting()
+{
+ QFuture<T> future;
+ {
+ QPromise<T> promise;
+ future = promise.future();
+ }
+ future.waitForFinished();
+ QVERIFY(!future.isStarted());
+ QVERIFY(future.isCanceled());
+ QVERIFY(future.isFinished());
+}
+
+void tst_QPromise::cancelWhenDestroyedWithoutStarting()
+{
+ testCancelWhenDestroyedWithoutStarting<void>();
+ testCancelWhenDestroyedWithoutStarting<int>();
+ testCancelWhenDestroyedWithoutStarting<CopyOnlyType>();
+ testCancelWhenDestroyedWithoutStarting<MoveOnlyType>();
+}
+
+template <typename T>
+static inline void testCancelWhenDestroyedRunsContinuations()
+{
+ QFuture<T> future;
+ bool onCanceledCalled = false;
+ bool thenCalled = false;
+ {
+ QPromise<T> promise;
+ future = promise.future();
+ future.then([&] (auto&&) {
+ thenCalled = true;
+ }).onCanceled([&] () {
+ onCanceledCalled = true;
+ });
+ }
+ QVERIFY(future.isFinished());
+ QVERIFY(!thenCalled);
+ QVERIFY(onCanceledCalled);
+}
+
+void tst_QPromise::cancelWhenDestroyedRunsContinuations()
+{
+ testCancelWhenDestroyedRunsContinuations<void>();
+ testCancelWhenDestroyedRunsContinuations<int>();
+ testCancelWhenDestroyedRunsContinuations<CopyOnlyType>();
+ testCancelWhenDestroyedRunsContinuations<MoveOnlyType>();
+}
+
+template <typename T>
+static inline void testCancelWhenDestroyedWithFailureHandler()
+{
+ QFuture<T> future;
+ bool onFailedCalled = false;
+ bool thenCalled = false;
+ {
+ QPromise<T> promise;
+ future = promise.future();
+ future
+ .onFailed([&] () {
+ onFailedCalled = true;
+ if constexpr (!std::is_same_v<void, T>)
+ return T{};
+ })
+ .then([&] (auto&&) {
+ thenCalled = true;
+ });
+ }
+ QVERIFY(future.isFinished());
+ QVERIFY(!onFailedCalled);
+ QVERIFY(!thenCalled);
+}
+
+void tst_QPromise::cancelWhenDestroyedWithFailureHandler()
+{
+#ifndef QT_NO_EXCEPTIONS
+ testCancelWhenDestroyedWithFailureHandler<void>();
+ testCancelWhenDestroyedWithFailureHandler<int>();
+ testCancelWhenDestroyedWithFailureHandler<CopyOnlyType>();
+ testCancelWhenDestroyedWithFailureHandler<MoveOnlyType>();
+#else
+ QSKIP("Exceptions are disabled, skipping the test");
+#endif
+}
+
+template <typename T>
+static inline void testContinuationsRunWhenFinished()
+{
+ QPromise<T> promise;
+ QFuture<T> future = promise.future();
+
+ bool thenCalled = false;
+ future.then([&] (auto&&) {
+ thenCalled = true;
+ });
+
+ promise.start();
+ if constexpr (!std::is_void_v<T>) {
+ promise.addResult(T{});
+ }
+ promise.finish();
+
+ QVERIFY(thenCalled);
+}
+
+void tst_QPromise::continuationsRunWhenFinished()
+{
+ testContinuationsRunWhenFinished<void>();
+ testContinuationsRunWhenFinished<int>();
+ testContinuationsRunWhenFinished<CopyOnlyType>();
+ testContinuationsRunWhenFinished<MoveOnlyType>();
+}
+
+void tst_QPromise::finishWhenSwapped()
+{
+#if QT_CONFIG(cxx11_future)
+ QPromise<int> promise1;
+ auto f1 = promise1.future();
+ promise1.start();
+
+ QPromise<int> promise2;
+ auto f2 = promise2.future();
+ promise2.start();
+
+ ThreadWrapper thr([&promise1, &promise2] () mutable {
+ QThread::sleep(100ms);
+ promise1.addResult(0);
+ promise2.addResult(1);
+ swap(promise1, promise2); // ADL must resolve this
+ promise1.addResult(2);
+ promise2.addResult(3);
+ promise1.finish(); // this finish is for future #2
+ promise2.finish(); // this finish is for future #1
+ });
+
+ f1.waitForFinished();
+ f2.waitForFinished();
+
+ // Future #1 and #2 are finished inside thread
+ QCOMPARE(f1.isFinished(), true);
+ QCOMPARE(f1.isCanceled(), false);
+
+ QCOMPARE(f2.isFinished(), true);
+ QCOMPARE(f2.isCanceled(), false);
+
+ QCOMPARE(f1.resultAt(0), 0);
+ QCOMPARE(f1.resultAt(1), 3);
+
+ QCOMPARE(f2.resultAt(0), 1);
+ QCOMPARE(f2.resultAt(1), 2);
+#endif
+}
+
+template <typename T>
+void testCancelWhenMoved()
+{
+#if QT_CONFIG(cxx11_future)
+ QPromise<T> promise1;
+ auto f1 = promise1.future();
+ promise1.start();
+
+ 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::sleep(100ms);
+ p1 = std::move(p2);
+ p1.finish(); // this finish is for future #2
+ });
+
+ f1.waitForFinished();
+ f2.waitForFinished();
+
+ // Future #1 is implicitly cancelled inside thread
+ QCOMPARE(f1.isFinished(), true);
+ QCOMPARE(f1.isCanceled(), true);
+
+ // Future #2 is explicitly finished inside thread
+ QCOMPARE(f2.isFinished(), true);
+ QCOMPARE(f2.isCanceled(), false);
+#endif
+}
+
+void tst_QPromise::cancelWhenMoved()
+{
+ testCancelWhenMoved<void>();
+ testCancelWhenMoved<int>();
+ testCancelWhenMoved<CopyOnlyType>();
+ testCancelWhenMoved<MoveOnlyType>();
+}
+
+void tst_QPromise::waitUntilResumed()
+{
+#if !QT_CONFIG(cxx11_future)
+ QSKIP("This test requires QThread::create");
+#else
+ QPromise<int> promise;
+ promise.start();
+ auto f = promise.future();
+ f.suspend();
+
+ ThreadWrapper thr([p = std::move(promise)] () mutable {
+ p.suspendIfRequested();
+ p.addResult(42); // result added after suspend
+ p.finish();
+ });
+
+ while (!f.isSuspended()) { // busy wait until worker thread suspends
+ QCOMPARE(f.isFinished(), false); // exit condition in case of failure
+ QThread::sleep(50ms); // allow another thread to actually carry on
+ }
+
+ f.resume();
+ f.waitForFinished();
+
+ QCOMPARE(f.resultCount(), 1);
+ QCOMPARE(f.result(), 42);
+#endif
+}
+
+void tst_QPromise::waitUntilCanceled()
+{
+#if QT_CONFIG(cxx11_future)
+ QPromise<int> promise;
+ promise.start();
+ auto f = promise.future();
+ f.suspend();
+
+ ThreadWrapper thr([p = std::move(promise)] () mutable {
+ p.suspendIfRequested();
+ p.addResult(42); // result not added due to QFuture::cancel()
+ p.finish();
+ });
+
+ while (!f.isSuspended()) { // busy wait until worker thread suspends
+ QCOMPARE(f.isFinished(), false); // exit condition in case of failure
+ QThread::sleep(50ms); // allow another thread to actually carry on
+ }
+
+ f.cancel();
+ f.waitForFinished();
+
+ QCOMPARE(f.resultCount(), 0);
+#endif
+}
+
+// Below is a quick and dirty hack to make snippets a part of a test suite
+#include "snippet_qpromise.cpp"
+void tst_QPromise::snippet_basicExample()
+{
+ snippet_QPromise::basicExample();
+}
+
+void tst_QPromise::snippet_multithreadExample()
+{
+ snippet_QPromise::multithreadExample();
+}
+
+void tst_QPromise::snippet_suspendExample()
+{
+ snippet_QPromise::suspendExample();
+}
+
+QTEST_MAIN(tst_QPromise)
+#include "tst_qpromise.moc"
diff --git a/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt b/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
new file mode 100644
index 0000000000..7e80a4df81
--- /dev/null
+++ b/tests/auto/corelib/thread/qreadlocker/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qreadlocker.pro b/tests/auto/corelib/thread/qreadlocker/qreadlocker.pro
deleted file mode 100644
index ba46786750..0000000000
--- a/tests/auto/corelib/thread/qreadlocker/qreadlocker.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qreadlocker
-QT = core testlib
-SOURCES = tst_qreadlocker.cpp
diff --git a/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
index 7d1b3709c2..e21f80c05d 100644
--- a/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
+++ b/tests/auto/corelib/thread/qreadlocker/tst_qreadlocker.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QCoreApplication>
#include <QReadLocker>
@@ -73,7 +48,7 @@ void tst_QReadLocker::scopeTest()
class ScopeTestThread : public tst_QReadLockerThread
{
public:
- void run()
+ void run() override
{
waitForTest();
@@ -109,7 +84,7 @@ void tst_QReadLocker::scopeTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
@@ -118,7 +93,7 @@ void tst_QReadLocker::unlockAndRelockTest()
class UnlockAndRelockThread : public tst_QReadLockerThread
{
public:
- void run()
+ void run() override
{
QReadLocker locker(&lock);
@@ -156,7 +131,7 @@ void tst_QReadLocker::unlockAndRelockTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
void tst_QReadLocker::lockerStateTest()
@@ -164,7 +139,7 @@ void tst_QReadLocker::lockerStateTest()
class LockerStateThread : public tst_QReadLockerThread
{
public:
- void run()
+ void run() override
{
{
QReadLocker locker(&lock);
@@ -196,7 +171,7 @@ void tst_QReadLocker::lockerStateTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
QTEST_MAIN(tst_QReadLocker)
diff --git a/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt b/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt
new file mode 100644
index 0000000000..5ed3012e04
--- /dev/null
+++ b/tests/auto/corelib/thread/qreadwritelock/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qreadwritelock Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qreadwritelock LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qreadwritelock
+ SOURCES
+ tst_qreadwritelock.cpp
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::TestPrivate
+)
diff --git a/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro b/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro
deleted file mode 100644
index c108c3d8af..0000000000
--- a/tests/auto/corelib/thread/qreadwritelock/qreadwritelock.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qreadwritelock
-QT = core testlib
-SOURCES = tst_qreadwritelock.cpp
diff --git a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
index 8e97229752..86dfa5faff 100644
--- a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
+++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp
@@ -1,49 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QSemaphore>
#include <qcoreapplication.h>
#include <qreadwritelock.h>
+#include <qelapsedtimer.h>
#include <qmutex.h>
#include <qthread.h>
#include <qwaitcondition.h>
+#include <private/qemulationdetector_p.h>
+#include <private/qvolatile_p.h>
#ifdef Q_OS_UNIX
#include <unistd.h>
#endif
-#if defined(Q_OS_WIN)
-# include <qt_windows.h>
-# ifndef Q_OS_WINRT
-# define sleep(X) Sleep(X)
-# else
-# define sleep(X) WaitForSingleObjectEx(GetCurrentThread(), X, FALSE);
-# endif
-#endif
//on solaris, threads that loop on the release bool variable
//needs to sleep more than 1 usec.
@@ -55,6 +26,8 @@
#include <stdio.h>
+using namespace std::chrono_literals;
+
class tst_QReadWriteLock : public QObject
{
Q_OBJECT
@@ -187,10 +160,10 @@ void tst_QReadWriteLock::readWriteLockUnlockLoop()
}
-QAtomicInt lockCount(0);
-QReadWriteLock readWriteLock;
-QSemaphore testsTurn;
-QSemaphore threadsTurn;
+static QAtomicInt lockCount(0);
+static QReadWriteLock readWriteLock;
+static QSemaphore testsTurn;
+static QSemaphore threadsTurn;
void tst_QReadWriteLock::tryReadLock()
@@ -217,7 +190,7 @@ void tst_QReadWriteLock::tryReadLock()
class Thread : public QThread
{
public:
- void run()
+ void run() override
{
testsTurn.release();
@@ -237,7 +210,7 @@ void tst_QReadWriteLock::tryReadLock()
testsTurn.release();
threadsTurn.acquire();
- QTime timer;
+ QElapsedTimer timer;
timer.start();
QVERIFY(!readWriteLock.tryLockForRead(1000));
QVERIFY(timer.elapsed() >= 1000);
@@ -334,7 +307,7 @@ void tst_QReadWriteLock::tryWriteLock()
{
public:
Thread() : failureCount(0) { }
- void run()
+ void run() override
{
testsTurn.release();
@@ -406,8 +379,8 @@ void tst_QReadWriteLock::tryWriteLock()
}
}
-bool threadDone;
-QAtomicInt release;
+static bool threadDone;
+static QAtomicInt release;
/*
write-lock
@@ -419,7 +392,7 @@ class WriteLockThread : public QThread
public:
QReadWriteLock &testRwlock;
inline WriteLockThread(QReadWriteLock &l) : testRwlock(l) { }
- void run()
+ void run() override
{
testRwlock.lockForWrite();
testRwlock.unlock();
@@ -437,7 +410,7 @@ class ReadLockThread : public QThread
public:
QReadWriteLock &testRwlock;
inline ReadLockThread(QReadWriteLock &l) : testRwlock(l) { }
- void run()
+ void run() override
{
testRwlock.lockForRead();
testRwlock.unlock();
@@ -454,10 +427,10 @@ class WriteLockReleasableThread : public QThread
public:
QReadWriteLock &testRwlock;
inline WriteLockReleasableThread(QReadWriteLock &l) : testRwlock(l) { }
- void run()
+ void run() override
{
testRwlock.lockForWrite();
- while(release.load()==false) {
+ while (release.loadRelaxed() == false) {
RWTESTSLEEP
}
testRwlock.unlock();
@@ -474,10 +447,10 @@ class ReadLockReleasableThread : public QThread
public:
QReadWriteLock &testRwlock;
inline ReadLockReleasableThread(QReadWriteLock &l) : testRwlock(l) { }
- void run()
+ void run() override
{
testRwlock.lockForRead();
- while(release.load()==false) {
+ while (release.loadRelaxed() == false) {
RWTESTSLEEP
}
testRwlock.unlock();
@@ -497,10 +470,10 @@ class ReadLockLoopThread : public QThread
public:
QReadWriteLock &testRwlock;
int runTime;
- int holdTime;
- int waitTime;
+ std::chrono::milliseconds holdTime;
+ std::chrono::milliseconds waitTime;
bool print;
- QTime t;
+ QElapsedTimer t;
inline ReadLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false)
:testRwlock(l)
,runTime(runTime)
@@ -508,15 +481,15 @@ public:
,waitTime(waitTime)
,print(print)
{ }
- void run()
+ void run() override
{
t.start();
while (t.elapsed()<runTime) {
testRwlock.lockForRead();
if(print) printf("reading\n");
- if (holdTime) msleep(holdTime);
+ if (holdTime > 0ms) sleep(holdTime);
testRwlock.unlock();
- if (waitTime) msleep(waitTime);
+ if (waitTime > 0ms) sleep(waitTime);
}
}
};
@@ -533,10 +506,10 @@ class WriteLockLoopThread : public QThread
public:
QReadWriteLock &testRwlock;
int runTime;
- int holdTime;
- int waitTime;
+ std::chrono::milliseconds holdTime;
+ std::chrono::milliseconds waitTime;
bool print;
- QTime t;
+ QElapsedTimer t;
inline WriteLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false)
:testRwlock(l)
,runTime(runTime)
@@ -544,20 +517,20 @@ public:
,waitTime(waitTime)
,print(print)
{ }
- void run()
+ void run() override
{
t.start();
while (t.elapsed() < runTime) {
testRwlock.lockForWrite();
if (print) printf(".");
- if (holdTime) msleep(holdTime);
+ if (holdTime > 0ms) sleep(holdTime);
testRwlock.unlock();
- if (waitTime) msleep(waitTime);
+ if (waitTime > 0ms) sleep(waitTime);
}
}
};
-volatile int count=0;
+static volatile int count = 0;
/*
for(runTime msecs)
@@ -572,16 +545,16 @@ class WriteLockCountThread : public QThread
public:
QReadWriteLock &testRwlock;
int runTime;
- int waitTime;
+ std::chrono::milliseconds waitTime;
int maxval;
- QTime t;
+ QElapsedTimer t;
inline WriteLockCountThread(QReadWriteLock &l, int runTime, int waitTime, int maxval)
:testRwlock(l)
,runTime(runTime)
,waitTime(waitTime)
,maxval(maxval)
{ }
- void run()
+ void run() override
{
t.start();
while (t.elapsed() < runTime) {
@@ -589,15 +562,11 @@ public:
if(count)
qFatal("Non-zero count at start of write! (%d)",count );
// printf(".");
- int i;
- for(i=0; i<maxval; ++i) {
- volatile int lc=count;
- ++lc;
- count=lc;
- }
+ for (int i = 0; i < maxval; ++i)
+ QtPrivate::volatilePreIncrement(count);
count=0;
testRwlock.unlock();
- msleep(waitTime);
+ sleep(waitTime);
}
}
};
@@ -614,14 +583,14 @@ class ReadLockCountThread : public QThread
public:
QReadWriteLock &testRwlock;
int runTime;
- int waitTime;
- QTime t;
+ std::chrono::milliseconds waitTime;
+ QElapsedTimer t;
inline ReadLockCountThread(QReadWriteLock &l, int runTime, int waitTime)
:testRwlock(l)
,runTime(runTime)
,waitTime(waitTime)
{ }
- void run()
+ void run() override
{
t.start();
while (t.elapsed() < runTime) {
@@ -629,7 +598,7 @@ public:
if(count)
qFatal("Non-zero count at Read! (%d)",count );
testRwlock.unlock();
- msleep(waitTime);
+ sleep(waitTime);
}
}
};
@@ -646,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);
@@ -663,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);
@@ -676,17 +645,17 @@ void tst_QReadWriteLock::multipleReadersBlockRelease()
{
QReadWriteLock testLock;
- release.store(false);
+ release.storeRelaxed(false);
threadDone=false;
ReadLockReleasableThread rlt1(testLock);
ReadLockReleasableThread rlt2(testLock);
rlt1.start();
rlt2.start();
- sleep(1);
+ QThread::sleep(1s);
WriteLockThread wlt(testLock);
wlt.start();
- sleep(1);
- release.store(true);
+ QThread::sleep(1s);
+ release.storeRelaxed(true);
wlt.wait();
rlt1.wait();
rlt2.wait();
@@ -698,27 +667,29 @@ void tst_QReadWriteLock::multipleReadersBlockRelease()
*/
void tst_QReadWriteLock::multipleReadersLoop()
{
- int time=500;
- int hold=250;
- int wait=0;
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Flaky on QEMU, QTBUG-96103");
+
+ constexpr int time = 500;
+ constexpr int hold = 250;
+ constexpr int wait = 0;
#if defined (Q_OS_HPUX)
- const int numthreads=50;
+ constexpr int NumThreads = 50;
#elif defined(Q_OS_VXWORKS)
- const int numthreads=40;
+ constexpr int NumThreads = 40;
#else
- const int numthreads=75;
+ constexpr int NumThreads = 75;
#endif
QReadWriteLock testLock;
- ReadLockLoopThread *threads[numthreads];
- int i;
- for (i=0; i<numthreads; ++i)
- threads[i] = new ReadLockLoopThread(testLock, time, hold, wait);
- for (i=0; i<numthreads; ++i)
- threads[i]->start();
- for (i=0; i<numthreads; ++i)
- threads[i]->wait();
- for (i=0; i<numthreads; ++i)
- delete threads[i];
+ ReadLockLoopThread *threads[NumThreads];
+ for (auto &thread : threads)
+ thread = new ReadLockLoopThread(testLock, time, hold, wait);
+ for (auto thread : threads)
+ thread->start();
+ for (auto thread : threads)
+ thread->wait();
+ for (auto thread : threads)
+ delete thread;
}
/*
@@ -726,21 +697,20 @@ void tst_QReadWriteLock::multipleReadersLoop()
*/
void tst_QReadWriteLock::multipleWritersLoop()
{
- int time=500;
- int wait=0;
- int hold=0;
- const int numthreads=50;
- QReadWriteLock testLock;
- WriteLockLoopThread *threads[numthreads];
- int i;
- for (i=0; i<numthreads; ++i)
- threads[i] = new WriteLockLoopThread(testLock, time, hold, wait);
- for (i=0; i<numthreads; ++i)
- threads[i]->start();
- for (i=0; i<numthreads; ++i)
- threads[i]->wait();
- for (i=0; i<numthreads; ++i)
- delete threads[i];
+ constexpr int time = 500;
+ constexpr int wait = 0;
+ constexpr int hold = 0;
+ constexpr int numthreads = 50;
+ QReadWriteLock testLock;
+ WriteLockLoopThread *threads[numthreads];
+ for (auto &thread : threads)
+ thread = new WriteLockLoopThread(testLock, time, hold, wait);
+ for (auto thread : threads)
+ thread->start();
+ for (auto thread : threads)
+ thread->wait();
+ for (auto thread : threads)
+ delete thread;
}
/*
@@ -748,40 +718,36 @@ void tst_QReadWriteLock::multipleWritersLoop()
*/
void tst_QReadWriteLock::multipleReadersWritersLoop()
{
- //int time=INT_MAX;
- int time=10000;
- int readerThreads=20;
- int readerWait=0;
- int readerHold=1;
-
- int writerThreads=2;
- int writerWait=500;
- int writerHold=50;
-
- QReadWriteLock testLock;
- ReadLockLoopThread *readers[1024];
- WriteLockLoopThread *writers[1024];
- int i;
-
- for (i=0; i<readerThreads; ++i)
- readers[i] = new ReadLockLoopThread(testLock, time, readerHold, readerWait, false);
- for (i=0; i<writerThreads; ++i)
- writers[i] = new WriteLockLoopThread(testLock, time, writerHold, writerWait, false);
-
- for (i=0; i<readerThreads; ++i)
- readers[i]->start(QThread::NormalPriority);
- for (i=0; i<writerThreads; ++i)
- writers[i]->start(QThread::IdlePriority);
-
- for (i=0; i<readerThreads; ++i)
- readers[i]->wait();
- for (i=0; i<writerThreads; ++i)
- writers[i]->wait();
-
- for (i=0; i<readerThreads; ++i)
- delete readers[i];
- for (i=0; i<writerThreads; ++i)
- delete writers[i];
+ constexpr int time = 10000; // INT_MAX
+ constexpr int readerThreads = 20;
+ constexpr int readerWait = 0;
+ constexpr int readerHold = 1;
+
+ constexpr int writerThreads = 2;
+ constexpr int writerWait = 500;
+ constexpr int writerHold = 50;
+
+ QReadWriteLock testLock;
+ ReadLockLoopThread *readers[readerThreads];
+ WriteLockLoopThread *writers[writerThreads];
+
+ for (auto &thread : readers)
+ thread = new ReadLockLoopThread(testLock, time, readerHold, readerWait, false);
+ for (auto &thread : writers)
+ thread = new WriteLockLoopThread(testLock, time, writerHold, writerWait, false);
+ for (auto thread : readers)
+ thread->start(QThread::NormalPriority);
+ for (auto thread : writers)
+ thread->start(QThread::IdlePriority);
+
+ for (auto thread : readers)
+ thread->wait();
+ for (auto thread : writers)
+ thread->wait();
+ for (auto thread : readers)
+ delete thread;
+ for (auto thread : writers)
+ delete thread;
}
/*
@@ -790,39 +756,35 @@ void tst_QReadWriteLock::multipleReadersWritersLoop()
*/
void tst_QReadWriteLock::countingTest()
{
- //int time=INT_MAX;
- int time=10000;
- int readerThreads=20;
- int readerWait=1;
-
- int writerThreads=3;
- int writerWait=150;
- int maxval=10000;
-
- QReadWriteLock testLock;
- ReadLockCountThread *readers[1024];
- WriteLockCountThread *writers[1024];
- int i;
-
- for (i=0; i<readerThreads; ++i)
- readers[i] = new ReadLockCountThread(testLock, time, readerWait);
- for (i=0; i<writerThreads; ++i)
- writers[i] = new WriteLockCountThread(testLock, time, writerWait, maxval);
-
- for (i=0; i<readerThreads; ++i)
- readers[i]->start(QThread::NormalPriority);
- for (i=0; i<writerThreads; ++i)
- writers[i]->start(QThread::LowestPriority);
-
- for (i=0; i<readerThreads; ++i)
- readers[i]->wait();
- for (i=0; i<writerThreads; ++i)
- writers[i]->wait();
-
- for (i=0; i<readerThreads; ++i)
- delete readers[i];
- for (i=0; i<writerThreads; ++i)
- delete writers[i];
+ constexpr int time = 10000; // INT_MAX
+ constexpr int readerThreads = 20;
+ constexpr int readerWait = 1;
+
+ constexpr int writerThreads = 3;
+ constexpr int writerWait = 150;
+ constexpr int maxval = 10000;
+
+ QReadWriteLock testLock;
+ ReadLockCountThread *readers[readerThreads];
+ WriteLockCountThread *writers[writerThreads];
+
+ for (auto &thread : readers)
+ thread = new ReadLockCountThread(testLock, time, readerWait);
+ for (auto &thread : writers)
+ thread = new WriteLockCountThread(testLock, time, writerWait, maxval);
+ for (auto thread : readers)
+ thread->start(QThread::NormalPriority);
+ for (auto thread : writers)
+ thread->start(QThread::LowestPriority);
+
+ for (auto thread : readers)
+ thread->wait();
+ for (auto thread : writers)
+ thread->wait();
+ for (auto thread : readers)
+ delete thread;
+ for (auto thread : writers)
+ delete thread;
}
void tst_QReadWriteLock::limitedReaders()
@@ -848,7 +810,7 @@ class DeleteOnUnlockThread : public QThread
public:
DeleteOnUnlockThread(QReadWriteLock **lock, QWaitCondition *startup, QMutex *waitMutex)
:m_lock(lock), m_startup(startup), m_waitMutex(waitMutex) {}
- void run()
+ void run() override
{
m_waitMutex->lock();
m_startup->wakeAll();
@@ -867,13 +829,13 @@ private:
void tst_QReadWriteLock::deleteOnUnlock()
{
- QReadWriteLock *lock = 0;
+ QReadWriteLock *lock = nullptr;
QWaitCondition startup;
QMutex waitMutex;
DeleteOnUnlockThread thread2(&lock, &startup, &waitMutex);
- QTime t;
+ QElapsedTimer t;
t.start();
while(t.elapsed() < 4000) {
lock = new QReadWriteLock();
@@ -899,7 +861,7 @@ void tst_QReadWriteLock::uncontendedLocks()
uint count=0;
int millisecs=1000;
{
- QTime t;
+ QElapsedTimer t;
t.start();
while(t.elapsed() <millisecs)
{
@@ -908,7 +870,7 @@ void tst_QReadWriteLock::uncontendedLocks()
}
{
QReadWriteLock rwlock;
- QTime t;
+ QElapsedTimer t;
t.start();
while(t.elapsed() <millisecs)
{
@@ -919,7 +881,7 @@ void tst_QReadWriteLock::uncontendedLocks()
}
{
QReadWriteLock rwlock;
- QTime t;
+ QElapsedTimer t;
t.start();
while(t.elapsed() <millisecs)
{
@@ -946,7 +908,7 @@ void tst_QReadWriteLock::recursiveReadLock()
QReadWriteLock *lock;
bool tryLockForWriteResult;
- void run()
+ void run() override
{
testsTurn.release();
@@ -1041,7 +1003,7 @@ void tst_QReadWriteLock::recursiveWriteLock()
QReadWriteLock *lock;
bool tryLockForReadResult;
- void run()
+ void run() override
{
testsTurn.release();
diff --git a/tests/auto/corelib/thread/qresultstore/CMakeLists.txt b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt
new file mode 100644
index 0000000000..5abfc14ac6
--- /dev/null
+++ b/tests/auto/corelib/thread/qresultstore/CMakeLists.txt
@@ -0,0 +1,20 @@
+# 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
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::TestPrivate
+)
diff --git a/tests/auto/corelib/thread/qresultstore/qresultstore.pro b/tests/auto/corelib/thread/qresultstore/qresultstore.pro
deleted file mode 100644
index bbebe0976b..0000000000
--- a/tests/auto/corelib/thread/qresultstore/qresultstore.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qresultstore
-QT = core-private testlib
-SOURCES = tst_qresultstore.cpp
-DEFINES += QT_STRICT_ITERATORS
diff --git a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
index fba617e34d..722184a72a 100644
--- a/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
+++ b/tests/auto/corelib/thread/qresultstore/tst_qresultstore.cpp
@@ -1,40 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QTest>
+#include <QtTest/private/qcomparisontesthelper_p.h>
#include <qresultstore.h>
using namespace QtPrivate;
-struct ResultStoreInt : ResultStoreBase
+class IntResultsCleaner
{
- ~ResultStoreInt() { clear<int>(); }
+public:
+ IntResultsCleaner(QtPrivate::ResultStoreBase &s) : store(s) { }
+ ~IntResultsCleaner() { store.clear<int>(); }
+
+private:
+ QtPrivate::ResultStoreBase &store;
};
class tst_QtConcurrentResultStore : public QObject
@@ -43,6 +23,7 @@ class tst_QtConcurrentResultStore : public QObject
public slots:
void init();
private slots:
+ void compareCompiles();
void construction();
void iterators();
void addResult();
@@ -53,12 +34,14 @@ private slots:
void filterMode();
void addCanceledResult();
void count();
+ void pendingResultsDoNotLeak_data();
+ void pendingResultsDoNotLeak();
private:
int int0;
int int1;
int int2;
- QVector<int> vec0;
- QVector<int> vec1;
+ QList<int> vec0;
+ QList<int> vec1;
};
void tst_QtConcurrentResultStore::init()
@@ -66,8 +49,13 @@ void tst_QtConcurrentResultStore::init()
int0 = 0;
int1 = 1;
int2 = 2;
- vec0 = QVector<int>() << 2 << 3;
- vec1 = QVector<int>() << 4 << 5;
+ vec0 = QList<int> { 2, 3 };
+ vec1 = QList<int> { 4, 5 };
+}
+
+void tst_QtConcurrentResultStore::compareCompiles()
+{
+ QTestPrivate::testEqualityOperatorsCompile<ResultIteratorBase>();
}
void tst_QtConcurrentResultStore::construction()
@@ -85,22 +73,27 @@ void tst_QtConcurrentResultStore::iterators()
QCOMPARE(store.resultAt(1), store.end());
}
{
- ResultStoreInt storebase;
+ QtPrivate::ResultStoreBase storebase;
+ IntResultsCleaner cleanGuard(storebase);
+
storebase.addResult(-1, &int0); // note to self: adding a pointer to the stack here is ok since
storebase.addResult(1, &int1); // ResultStoreBase does not take ownership, only ResultStore<> does.
ResultIteratorBase it = storebase.begin();
QCOMPARE(it.resultIndex(), 0);
- QCOMPARE(it, storebase.begin());
+ QT_TEST_EQUALITY_OPS(it, storebase.begin(), true);
QVERIFY(it != storebase.end());
++it;
QCOMPARE(it.resultIndex(), 1);
QVERIFY(it != storebase.begin());
QVERIFY(it != storebase.end());
+ QT_TEST_EQUALITY_OPS(it, storebase.begin(), false);
+ QT_TEST_EQUALITY_OPS(it, storebase.end(), false);
++it;
QVERIFY(it != storebase.begin());
QCOMPARE(it, storebase.end());
+ QT_TEST_EQUALITY_OPS(it, storebase.end(), true);
}
}
@@ -108,7 +101,9 @@ void tst_QtConcurrentResultStore::addResult()
{
{
// test addResult return value
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
QCOMPARE(store.addResult(0, &int0), 0);
@@ -154,13 +149,15 @@ void tst_QtConcurrentResultStore::addResult()
void tst_QtConcurrentResultStore::addResults()
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(-1, &vec0);
store.addResults(-1, &vec1);
ResultIteratorBase it = store.begin();
QCOMPARE(it.resultIndex(), 0);
- QCOMPARE(it, store.begin());
- QVERIFY(it != store.end());
+ QT_TEST_EQUALITY_OPS(it, store.begin(), true);
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
QCOMPARE(it.resultIndex(), 1);
@@ -174,34 +171,44 @@ void tst_QtConcurrentResultStore::addResults()
QCOMPARE(it.resultIndex(), 3);
++it;
- QCOMPARE(it, store.end());
+ QT_TEST_EQUALITY_OPS(it, store.end(), true);
+
+ QList<int> empty;
+ const auto countBefore = store.count();
+ QCOMPARE(store.addResults(countBefore, &empty), -1);
+ QCOMPARE(store.count(), countBefore);
+
+ QCOMPARE(store.addResults(countBefore, &vec1), countBefore);
+ QCOMPARE(store.count(), countBefore + vec1.size());
}
void tst_QtConcurrentResultStore::resultIndex()
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(-1, &int1);
ResultIteratorBase it = store.begin();
QCOMPARE(it.resultIndex(), 0);
- QVERIFY(it == store.begin());
- QVERIFY(it != store.end());
+ QT_TEST_EQUALITY_OPS(it, store.begin(), true);
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
QCOMPARE(it.resultIndex(), 1);
- QVERIFY(it != store.begin());
- QVERIFY(it != store.end());
+ QT_TEST_EQUALITY_OPS(it, store.begin(), false);
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
QCOMPARE(it.resultIndex(), 2);
- QVERIFY(it != store.end());
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
QCOMPARE(it.resultIndex(), 3);
- QVERIFY(it != store.end());
+ QT_TEST_EQUALITY_OPS(it, store.end(), false);
++it;
- QVERIFY(it == store.end());
+ QT_TEST_EQUALITY_OPS(it, store.end(), true);
QCOMPARE(store.resultAt(0).value<int>(), int0);
QCOMPARE(store.resultAt(1).value<int>(), vec0[0]);
@@ -212,7 +219,9 @@ void tst_QtConcurrentResultStore::resultIndex()
void tst_QtConcurrentResultStore::resultAt()
{
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(-1, &int0);
store.addResults(-1, &vec0);
store.addResult(200, &int1);
@@ -223,7 +232,9 @@ void tst_QtConcurrentResultStore::resultAt()
QCOMPARE(store.resultAt(200).value<int>(), int1);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(1, &int1);
store.addResult(0, &int0);
store.addResult(-1, &int2);
@@ -237,7 +248,9 @@ void tst_QtConcurrentResultStore::resultAt()
void tst_QtConcurrentResultStore::contains()
{
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
QCOMPARE(store.contains(0), false);
QCOMPARE(store.contains(1), false);
QCOMPARE(store.contains(INT_MAX), false);
@@ -249,7 +262,9 @@ void tst_QtConcurrentResultStore::contains()
QVERIFY(store.contains(int2));
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(1, &int0);
store.addResult(3, &int0);
store.addResults(6, &vec0);
@@ -264,7 +279,9 @@ void tst_QtConcurrentResultStore::contains()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(1, &int0);
store.addResult(3, &int0);
@@ -292,7 +309,9 @@ void tst_QtConcurrentResultStore::contains()
QCOMPARE(store.contains(7), false);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addCanceledResult(0);
QCOMPARE(store.contains(0), false);
@@ -306,7 +325,9 @@ void tst_QtConcurrentResultStore::contains()
void tst_QtConcurrentResultStore::filterMode()
{
// Test filter mode, where "gaps" in the result array aren't allowed.
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
QCOMPARE(store.filterMode(), false);
store.setFilterMode(true);
QVERIFY(store.filterMode());
@@ -338,12 +359,22 @@ void tst_QtConcurrentResultStore::filterMode()
QCOMPARE(store.contains(6), true);
QCOMPARE(store.contains(7), true);
QCOMPARE(store.contains(8), false);
+
+ QList<int> empty;
+ const auto countBefore = store.count();
+ QCOMPARE(store.addResults(countBefore, &empty), -1);
+ QCOMPARE(store.count(), countBefore);
+
+ QCOMPARE(store.addResult(countBefore, &int2), countBefore);
+ QCOMPARE(store.count(), countBefore + 1);
}
void tst_QtConcurrentResultStore::addCanceledResult()
{
// test canceled results
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResult(0, &int0);
@@ -383,7 +414,9 @@ void tst_QtConcurrentResultStore::count()
{
// test resultCount in non-filtered mode. It should always be possible
// to iterate through the results 0 to resultCount.
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(0, &int0);
QCOMPARE(store.count(), 1);
@@ -397,7 +430,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResult(2, &int0);
QCOMPARE(store.count(), 0);
@@ -409,7 +444,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -421,7 +458,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(2, &vec1);
QCOMPARE(store.count(), 0);
@@ -429,7 +468,9 @@ void tst_QtConcurrentResultStore::count()
QCOMPARE(store.count(), 4);
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -441,7 +482,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -454,7 +497,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -464,7 +509,9 @@ void tst_QtConcurrentResultStore::count()
}
{
- ResultStoreInt store;
+ QtPrivate::ResultStoreBase store;
+ IntResultsCleaner cleanGuard(store);
+
store.setFilterMode(true);
store.addResults(3, &vec1);
QCOMPARE(store.count(), 0);
@@ -477,5 +524,75 @@ void tst_QtConcurrentResultStore::count()
}
}
+// simplified version of CountedObject from tst_qarraydata.cpp
+struct CountedObject
+{
+ CountedObject() : id(liveCount++)
+ { }
+
+ CountedObject(const CountedObject &other) : id(other.id)
+ {
+ ++liveCount;
+ }
+
+ ~CountedObject()
+ {
+ --liveCount;
+ }
+
+ CountedObject &operator=(const CountedObject &) = default;
+
+ struct LeakChecker
+ {
+ LeakChecker()
+ : previousLiveCount(liveCount)
+ {
+ }
+
+ ~LeakChecker()
+ {
+ QCOMPARE(liveCount, previousLiveCount);
+ }
+
+ private:
+ const size_t previousLiveCount;
+ };
+
+ size_t id = 0;
+ static size_t liveCount;
+};
+
+size_t CountedObject::liveCount = 0;
+
+void tst_QtConcurrentResultStore::pendingResultsDoNotLeak_data()
+{
+ QTest::addColumn<bool>("filterMode");
+
+ QTest::addRow("filter-mode-off") << false;
+ QTest::addRow("filter-mode-on") << true;
+}
+
+void tst_QtConcurrentResultStore::pendingResultsDoNotLeak()
+{
+ QFETCH(bool, filterMode);
+ CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker)
+
+ QtPrivate::ResultStoreBase store;
+ auto cleanGaurd = qScopeGuard([&] { store.clear<CountedObject>(); });
+
+ store.setFilterMode(filterMode);
+
+ // lvalue
+ auto lvalueObj = CountedObject();
+ store.addResult(42, &lvalueObj);
+
+ // rvalue
+ store.moveResult(43, CountedObject());
+
+ // array
+ auto lvalueListOfObj = QList<CountedObject>({CountedObject(), CountedObject()});
+ store.addResults(44, &lvalueListOfObj);
+}
+
QTEST_MAIN(tst_QtConcurrentResultStore)
#include "tst_qresultstore.moc"
diff --git a/tests/auto/corelib/thread/qsemaphore/BLACKLIST b/tests/auto/corelib/thread/qsemaphore/BLACKLIST
index 0786f50417..ecd42cff7c 100644
--- a/tests/auto/corelib/thread/qsemaphore/BLACKLIST
+++ b/tests/auto/corelib/thread/qsemaphore/BLACKLIST
@@ -1,8 +1,2 @@
-[tryAcquireWithTimeout:0.2s]
-windows
-osx-10.12
-osx-10.13
-[tryAcquireWithTimeout:2s]
-windows
-osx-10.12
-osx-10.13
+[tryAcquireWithTimeout]
+macos
diff --git a/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt b/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
new file mode 100644
index 0000000000..9f8a87558a
--- /dev/null
+++ b/tests/auto/corelib/thread/qsemaphore/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qsemaphore.pro b/tests/auto/corelib/thread/qsemaphore/qsemaphore.pro
deleted file mode 100644
index 5a0f0337e6..0000000000
--- a/tests/auto/corelib/thread/qsemaphore/qsemaphore.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qsemaphore
-QT = core testlib
-SOURCES = tst_qsemaphore.cpp
diff --git a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
index b7134d0454..3bb1e1960c 100644
--- a/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
+++ b/tests/auto/corelib/thread/qsemaphore/tst_qsemaphore.cpp
@@ -1,37 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <qcoreapplication.h>
#include <qthread.h>
#include <qsemaphore.h>
+#include <chrono>
+
+using namespace std::chrono_literals;
+
class tst_QSemaphore : public QObject
{
Q_OBJECT
@@ -47,9 +26,10 @@ private slots:
void tryAcquireWithTimeoutForever();
void producerConsumer();
void raii();
+ void stdCompat();
};
-static QSemaphore *semaphore = 0;
+static QSemaphore *semaphore = nullptr;
class ThreadOne : public QThread
{
@@ -57,7 +37,7 @@ public:
ThreadOne() {}
protected:
- void run()
+ void run() override
{
int i = 0;
while ( i < 100 ) {
@@ -76,7 +56,7 @@ public:
ThreadN(int n) :N(n) { }
protected:
- void run()
+ void run() override
{
int i = 0;
while ( i < 100 ) {
@@ -106,7 +86,7 @@ void tst_QSemaphore::acquire()
QVERIFY(t2.wait(4000));
delete semaphore;
- semaphore = 0;
+ semaphore = nullptr;
}
// old incrementN() test
@@ -126,7 +106,7 @@ void tst_QSemaphore::acquire()
QVERIFY(t2.wait(4000));
delete semaphore;
- semaphore = 0;
+ semaphore = nullptr;
}
QSemaphore semaphore;
@@ -166,7 +146,7 @@ void tst_QSemaphore::multiRelease()
};
QSemaphore sem;
- QVector<Thread *> threads;
+ QList<Thread *> threads;
threads.resize(4);
for (Thread *&t : threads)
@@ -177,7 +157,7 @@ void tst_QSemaphore::multiRelease()
// wait for all threads to reach the sem.acquire() and then
// release them all
QTest::qSleep(1);
- sem.release(threads.size());
+ sem.release(int(threads.size()));
for (Thread *&t : threads)
t->wait();
@@ -200,7 +180,7 @@ void tst_QSemaphore::multiAcquireRelease()
};
QSemaphore sem;
- QVector<Thread *> threads;
+ QList<Thread *> threads;
threads.resize(4);
for (Thread *&t : threads)
@@ -323,69 +303,69 @@ void tst_QSemaphore::tryAcquireWithTimeout()
QCOMPARE(semaphore.available(), 1);
time.start();
QVERIFY(!semaphore.tryAcquire(2, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 1);
semaphore.release();
QCOMPARE(semaphore.available(), 2);
time.start();
QVERIFY(!semaphore.tryAcquire(3, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 2);
semaphore.release(10);
QCOMPARE(semaphore.available(), 12);
time.start();
QVERIFY(!semaphore.tryAcquire(100, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 12);
semaphore.release(10);
QCOMPARE(semaphore.available(), 22);
time.start();
QVERIFY(!semaphore.tryAcquire(100, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 22);
time.start();
QVERIFY(semaphore.tryAcquire(1, timeout));
- FUZZYCOMPARE(time.elapsed(), 0);
+ FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 21);
time.start();
QVERIFY(semaphore.tryAcquire(1, timeout));
- FUZZYCOMPARE(time.elapsed(), 0);
+ FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 20);
time.start();
QVERIFY(semaphore.tryAcquire(10, timeout));
- FUZZYCOMPARE(time.elapsed(), 0);
+ FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 10);
time.start();
QVERIFY(semaphore.tryAcquire(10, timeout));
- FUZZYCOMPARE(time.elapsed(), 0);
+ FUZZYCOMPARE(int(time.elapsed()), 0);
QCOMPARE(semaphore.available(), 0);
// should not be able to acquire more
time.start();
QVERIFY(!semaphore.tryAcquire(1, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(1, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(10, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
time.start();
QVERIFY(!semaphore.tryAcquire(10, timeout));
- FUZZYCOMPARE(time.elapsed(), timeout);
+ FUZZYCOMPARE(int(time.elapsed()), timeout);
QCOMPARE(semaphore.available(), 0);
#undef FUZZYCOMPARE
@@ -400,7 +380,7 @@ void tst_QSemaphore::tryAcquireWithTimeoutStarvation()
QSemaphore *semaphore;
int amountToConsume, timeout;
- void run()
+ void run() override
{
startup.release();
forever {
@@ -481,7 +461,7 @@ const char alphabet[] = "ACGTH";
const int AlphabetSize = sizeof(alphabet) - 1;
const int BufferSize = 4096; // GCD of BufferSize and alphabet size must be 1
-char buffer[BufferSize];
+static char buffer[BufferSize];
const int ProducerChunkSize = 3;
const int ConsumerChunkSize = 7;
@@ -491,16 +471,16 @@ const int Multiplier = 10;
// ProducerChunkSize, ConsumerChunkSize, and BufferSize
const int DataSize = ProducerChunkSize * ConsumerChunkSize * BufferSize * Multiplier;
-QSemaphore freeSpace(BufferSize);
-QSemaphore usedSpace;
+static QSemaphore freeSpace(BufferSize);
+static QSemaphore usedSpace;
class Producer : public QThread
{
public:
- void run();
+ void run() override;
};
-static const int Timeout = 60 * 1000; // 1min
+static const auto Timeout = 1min;
void Producer::run()
{
@@ -521,7 +501,7 @@ void Producer::run()
class Consumer : public QThread
{
public:
- void run();
+ void run() override;
};
void Consumer::run()
@@ -599,5 +579,34 @@ void tst_QSemaphore::raii()
QCOMPARE(sem.available(), 49);
}
+void tst_QSemaphore::stdCompat()
+{
+ QSemaphore sem(1);
+
+ auto now = [] { return std::chrono::steady_clock::now(); };
+
+ QVERIFY(sem.try_acquire());
+ QCOMPARE(sem.available(), 0);
+ QVERIFY(!sem.try_acquire_for(10ms));
+ QCOMPARE(sem.available(), 0);
+ QVERIFY(!sem.try_acquire_until(now() + 10ms));
+ QCOMPARE(sem.available(), 0);
+
+ sem.release(2);
+
+ QVERIFY(sem.try_acquire());
+ QVERIFY(sem.try_acquire_for(5ms));
+ QCOMPARE(sem.available(), 0);
+ QVERIFY(!sem.try_acquire_until(now() + 5ms));
+ QCOMPARE(sem.available(), 0);
+
+ sem.release(3);
+
+ QVERIFY(sem.try_acquire());
+ QVERIFY(sem.try_acquire_for(5s));
+ QVERIFY(sem.try_acquire_until(now() + 5s));
+ QCOMPARE(sem.available(), 0);
+}
+
QTEST_MAIN(tst_QSemaphore)
#include "tst_qsemaphore.moc"
diff --git a/tests/auto/corelib/thread/qthread/BLACKLIST b/tests/auto/corelib/thread/qthread/BLACKLIST
index d75249454f..08e9912455 100644
--- a/tests/auto/corelib/thread/qthread/BLACKLIST
+++ b/tests/auto/corelib/thread/qthread/BLACKLIST
@@ -1,2 +1,3 @@
[wait3_slowDestructor]
-windows
+windows-10
+
diff --git a/tests/auto/corelib/thread/qthread/CMakeLists.txt b/tests/auto/corelib/thread/qthread/CMakeLists.txt
new file mode 100644
index 0000000000..abcea1ef9c
--- /dev/null
+++ b/tests/auto/corelib/thread/qthread/CMakeLists.txt
@@ -0,0 +1,22 @@
+# 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
+ LIBRARIES
+ Qt::TestPrivate
+)
+
+## Scopes:
+#####################################################################
diff --git a/tests/auto/corelib/thread/qthread/qthread.pro b/tests/auto/corelib/thread/qthread/qthread.pro
deleted file mode 100644
index 37552f1fca..0000000000
--- a/tests/auto/corelib/thread/qthread/qthread.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qthread
-QT = core testlib
-SOURCES = tst_qthread.cpp
-qtConfig(c++14):CONFIG += c++14
-qtConfig(c++1z):CONFIG += c++1z
-
-INCLUDEPATH += ../../../../shared/
-HEADERS += ../../../../shared/emulationdetector.h
diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
index d73dcc1b6d..18c8d5fbd5 100644
--- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp
+++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
@@ -1,47 +1,32 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QTestEventLoop>
+#include <QSignalSpy>
+#include <QSemaphore>
+#include <QAbstractEventDispatcher>
+#if defined(Q_OS_WIN32)
+#include <QWinEventNotifier>
+#endif
#include <qcoreapplication.h>
-#include <qdatetime.h>
+#include <qelapsedtimer.h>
#include <qmutex.h>
#include <qthread.h>
#include <qtimer.h>
#include <qwaitcondition.h>
#include <qdebug.h>
#include <qmetaobject.h>
+#include <qscopeguard.h>
+#include <private/qobject_p.h>
+#include <private/qthread_p.h>
#ifdef Q_OS_UNIX
#include <pthread.h>
#endif
#if defined(Q_OS_WIN)
-#include <windows.h>
+#include <qt_windows.h>
#if defined(Q_OS_WIN32)
#include <process.h>
#endif
@@ -51,7 +36,9 @@
#include <exception>
#endif
-#include "emulationdetector.h"
+#include <QtTest/private/qemulationdetector_p.h>
+
+using namespace std::chrono_literals;
class tst_QThread : public QObject
{
@@ -66,6 +53,7 @@ private slots:
void setStackSize();
void exit();
void start();
+ void startSlotUsedInStringBasedLookups();
void terminate();
void quit();
void started();
@@ -85,6 +73,7 @@ private slots:
void adoptedThreadExecFinished();
void adoptMultipleThreads();
void adoptMultipleThreadsOverlap();
+ void adoptedThreadBindingStatus();
void exitAndStart();
void exitAndExec();
@@ -106,10 +95,23 @@ private slots:
void quitLock();
void create();
+ void createDestruction();
+ void threadIdReuse();
+
+ void terminateAndPrematureDestruction();
+ void terminateAndDoubleDestruction();
+
+ void bindingListCleanupAfterDelete();
};
enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute };
+template <class Int>
+static QString msgElapsed(Int elapsed)
+{
+ return QString::fromLatin1("elapsed: %1").arg(elapsed);
+}
+
class SignalRecorder : public QObject
{
Q_OBJECT
@@ -121,7 +123,7 @@ public:
{ }
bool wasActivated()
- { return activationCount.load() > 0; }
+ { return activationCount.loadRelaxed() > 0; }
public slots:
void slot();
@@ -135,11 +137,13 @@ class Current_Thread : public QThread
public:
Qt::HANDLE id;
QThread *thread;
+ bool runCalledInCurrentThread = false;
- void run()
+ void run() override
{
id = QThread::currentThreadId();
thread = QThread::currentThread();
+ runCalledInCurrentThread = thread->isCurrentThread();
}
};
@@ -149,7 +153,7 @@ public:
QMutex mutex;
QWaitCondition cond;
- void run()
+ void run() override
{
QMutexLocker locker(&mutex);
cond.wakeOne();
@@ -174,7 +178,7 @@ public:
int code;
int result;
- void run()
+ void run() override
{
Simple_Thread::run();
if (object) {
@@ -189,7 +193,7 @@ public:
class Terminate_Thread : public Simple_Thread
{
public:
- void run()
+ void run() override
{
setTerminationEnabled(false);
{
@@ -218,7 +222,7 @@ public:
Quit_Object *object;
int result;
- void run()
+ void run() override
{
Simple_Thread::run();
if (object) {
@@ -235,29 +239,31 @@ public:
enum SleepType { Second, Millisecond, Microsecond };
SleepType sleepType;
- int interval;
+ ulong interval;
- int elapsed; // result, in *MILLISECONDS*
+ qint64 elapsed; // result, in *MILLISECONDS*
- void run()
+ void run() override
{
QMutexLocker locker(&mutex);
elapsed = 0;
- QTime time;
- time.start();
+ 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;
}
- elapsed = time.elapsed();
+ sleep(dur);
+ elapsed = timer.elapsed();
cond.wakeOne();
}
@@ -266,25 +272,30 @@ public:
void tst_QThread::currentThreadId()
{
Current_Thread thread;
- thread.id = 0;
- thread.thread = 0;
+ thread.id = nullptr;
+ thread.thread = nullptr;
thread.start();
QVERIFY(thread.wait(five_minutes));
- QVERIFY(thread.id != 0);
+ QVERIFY(thread.id != nullptr);
QVERIFY(thread.id != QThread::currentThreadId());
+ QVERIFY(!thread.isCurrentThread());
+ QVERIFY(!thread.thread->isCurrentThread());
+ QVERIFY(thread.QThread::thread()->isCurrentThread());
+ QVERIFY(thread.runCalledInCurrentThread);
+ QVERIFY(qApp->thread()->isCurrentThread());
}
void tst_QThread::currentThread()
{
- QVERIFY(QThread::currentThread() != 0);
+ QVERIFY(QThread::currentThread() != nullptr);
QCOMPARE(QThread::currentThread(), thread());
Current_Thread thread;
- thread.id = 0;
- thread.thread = 0;
+ thread.id = nullptr;
+ thread.thread = nullptr;
thread.start();
QVERIFY(thread.wait(five_minutes));
- QCOMPARE(thread.thread, (QThread *)&thread);
+ QCOMPARE(thread.thread, static_cast<QThread *>(&thread));
}
void tst_QThread::idealThreadCount()
@@ -423,7 +434,7 @@ void tst_QThread::exit()
delete thread.object;
Exit_Thread thread2;
- thread2.object = 0;
+ thread2.object = nullptr;
thread2.code = 53;
thread2.result = 0;
QMutexLocker locker2(&thread2.mutex);
@@ -463,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_WINRT) || defined(Q_OS_ANDROID)
- QSKIP("Thread termination is not supported on WinRT or Android.");
+#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);
@@ -499,7 +564,7 @@ void tst_QThread::quit()
delete thread.object;
Quit_Thread thread2;
- thread2.object = 0;
+ thread2.object = nullptr;
thread2.result = -1;
QMutexLocker locker2(&thread2.mutex);
thread2.start();
@@ -531,9 +596,13 @@ void tst_QThread::finished()
void tst_QThread::terminated()
{
-#if defined(Q_OS_WINRT) || defined(Q_OS_ANDROID)
- QSKIP("Thread termination is not supported on WinRT or Android.");
+#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);
@@ -557,7 +626,7 @@ void tst_QThread::exec()
MultipleExecThread() : res1(-2), res2(-2) { }
- void run()
+ void run() override
{
{
Exit_Object o;
@@ -591,7 +660,7 @@ void tst_QThread::sleep()
thread.interval = 2;
thread.start();
QVERIFY(thread.wait(five_minutes));
- QVERIFY(thread.elapsed >= 2000);
+ QVERIFY2(thread.elapsed >= 2000, qPrintable(msgElapsed(thread.elapsed)));
}
void tst_QThread::msleep()
@@ -601,11 +670,10 @@ void tst_QThread::msleep()
thread.interval = 120;
thread.start();
QVERIFY(thread.wait(five_minutes));
-#if defined (Q_OS_WIN)
- // Since the resolution of QTime is so coarse...
- QVERIFY(thread.elapsed >= 100);
+#if defined (Q_OS_WIN) // May no longer be needed
+ QVERIFY2(thread.elapsed >= 100, qPrintable(msgElapsed(thread.elapsed)));
#else
- QVERIFY(thread.elapsed >= 120);
+ QVERIFY2(thread.elapsed >= 120, qPrintable(msgElapsed(thread.elapsed)));
#endif
}
@@ -616,11 +684,10 @@ void tst_QThread::usleep()
thread.interval = 120000;
thread.start();
QVERIFY(thread.wait(five_minutes));
-#if defined (Q_OS_WIN)
- // Since the resolution of QTime is so coarse...
- QVERIFY(thread.elapsed >= 100);
+#if defined (Q_OS_WIN) // May no longer be needed
+ QVERIFY2(thread.elapsed >= 100, qPrintable(msgElapsed(thread.elapsed)));
#else
- QVERIFY(thread.elapsed >= 120);
+ QVERIFY2(thread.elapsed >= 120, qPrintable(msgElapsed(thread.elapsed)));
#endif
}
@@ -642,9 +709,9 @@ void noop(void*) { }
class NativeThreadWrapper
{
public:
- NativeThreadWrapper() : qthread(0), waitForStop(false) {}
- void start(FunctionPointer functionPointer = noop, void *data = 0);
- void startAndWait(FunctionPointer functionPointer = noop, void *data = 0);
+ NativeThreadWrapper() : qthread(nullptr), waitForStop(false) {}
+ void start(FunctionPointer functionPointer = noop, void *data = nullptr);
+ void startAndWait(FunctionPointer functionPointer = noop, void *data = nullptr);
void join();
void setWaitForStop() { waitForStop = true; }
void stop();
@@ -668,10 +735,8 @@ void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data)
this->functionPointer = functionPointer;
this->data = data;
#if defined Q_OS_UNIX
- const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this);
- Q_UNUSED(state);
-#elif defined(Q_OS_WINRT)
- nativeThreadHandle = CreateThread(NULL, 0 , (LPTHREAD_START_ROUTINE)NativeThreadWrapper::runWin , this, 0, NULL);
+ const int state = pthread_create(&nativeThreadHandle, nullptr, NativeThreadWrapper::runUnix, this);
+ Q_UNUSED(state)
#elif defined Q_OS_WIN
unsigned thrdid = 0;
nativeThreadHandle = (Qt::HANDLE) _beginthreadex(NULL, 0, NativeThreadWrapper::runWin, this, 0, &thrdid);
@@ -688,7 +753,7 @@ void NativeThreadWrapper::startAndWait(FunctionPointer functionPointer, void *da
void NativeThreadWrapper::join()
{
#if defined Q_OS_UNIX
- pthread_join(nativeThreadHandle, 0);
+ pthread_join(nativeThreadHandle, nullptr);
#elif defined Q_OS_WIN
WaitForSingleObjectEx(nativeThreadHandle, INFINITE, FALSE);
CloseHandle(nativeThreadHandle);
@@ -718,7 +783,7 @@ void *NativeThreadWrapper::runUnix(void *that)
nativeThreadWrapper->stopCondition.wait(lock.mutex());
}
- return 0;
+ return nullptr;
}
unsigned WIN_FIX_STDCALL NativeThreadWrapper::runWin(void *data)
@@ -734,12 +799,12 @@ void NativeThreadWrapper::stop()
stopCondition.wakeOne();
}
-bool threadAdoptedOk = false;
-QThread *mainThread;
+static bool threadAdoptedOk = false;
+static QThread *mainThread;
void testNativeThreadAdoption(void *)
{
- threadAdoptedOk = (QThread::currentThreadId() != 0
- && QThread::currentThread() != 0
+ threadAdoptedOk = (QThread::currentThreadId() != nullptr
+ && QThread::currentThread() != nullptr
&& QThread::currentThread() != mainThread);
}
void tst_QThread::nativeThreadAdoption()
@@ -767,14 +832,15 @@ void adoptedThreadAffinityFunction(void *arg)
void tst_QThread::adoptedThreadAffinity()
{
- QThread *affinity[2] = { 0, 0 };
+ QThread *affinity[2] = { nullptr, nullptr };
NativeThreadWrapper thread;
thread.startAndWait(adoptedThreadAffinityFunction, affinity);
thread.join();
- // adopted thread should have affinity to itself
- QCOMPARE(affinity[0], affinity[1]);
+ // adopted thread (deleted) should have affinity to itself
+ QCOMPARE(static_cast<const void *>(affinity[0]),
+ static_cast<const void *>(affinity[1]));
}
void tst_QThread::adoptedThreadSetPriority()
@@ -881,7 +947,7 @@ void tst_QThread::adoptMultipleThreads()
#else
const int numThreads = 5;
#endif
- QVector<NativeThreadWrapper*> nativeThreads;
+ QList<NativeThreadWrapper*> nativeThreads;
SignalRecorder recorder;
@@ -902,7 +968,7 @@ void tst_QThread::adoptMultipleThreads()
QTestEventLoop::instance().enterLoop(5);
QVERIFY(!QTestEventLoop::instance().timeout());
- QCOMPARE(recorder.activationCount.load(), numThreads);
+ QCOMPARE(recorder.activationCount.loadRelaxed(), numThreads);
}
void tst_QThread::adoptMultipleThreadsOverlap()
@@ -913,7 +979,7 @@ void tst_QThread::adoptMultipleThreadsOverlap()
#else
const int numThreads = 5;
#endif
- QVector<NativeThreadWrapper*> nativeThreads;
+ QList<NativeThreadWrapper*> nativeThreads;
SignalRecorder recorder;
@@ -939,18 +1005,32 @@ void tst_QThread::adoptMultipleThreadsOverlap()
QTestEventLoop::instance().enterLoop(5);
QVERIFY(!QTestEventLoop::instance().timeout());
- QCOMPARE(recorder.activationCount.load(), numThreads);
+ QCOMPARE(recorder.activationCount.loadRelaxed(), numThreads);
+}
+
+void tst_QThread::adoptedThreadBindingStatus()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+
+ nativeThread.startAndWait();
+ QVERIFY(nativeThread.qthread);
+ auto privThread = static_cast<QThreadPrivate *>(QObjectPrivate::get(nativeThread.qthread));
+ QVERIFY(privThread->m_statusOrPendingObjects.bindingStatus());
+
+ nativeThread.stop();
+ nativeThread.join();
}
// Disconnects on WinCE
void tst_QThread::stressTest()
{
- if (EmulationDetector::isRunningArmOnX86())
+ if (QTestPrivate::isRunningArmOnX86())
QSKIP("Qemu uses too much memory for each thread. Test would run out of memory.");
- QTime t;
- t.start();
- while (t.elapsed() < one_minute) {
+ QElapsedTimer timer;
+ timer.start();
+ while (timer.elapsed() < one_minute) {
Current_Thread t;
t.start();
t.wait(one_minute);
@@ -1000,7 +1080,8 @@ void tst_QThread::exitAndExec()
QSemaphore sem1;
QSemaphore sem2;
volatile int value;
- void run() {
+ void run() override
+ {
sem1.acquire();
value = exec(); //First entrence
sem2.release();
@@ -1051,7 +1132,7 @@ public:
QWaitCondition cond1;
QWaitCondition cond2;
- void run()
+ void run() override
{
QMutexLocker locker(&mutex);
cond1.wait(&mutex);
@@ -1067,17 +1148,19 @@ void tst_QThread::wait2()
timer.start();
QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
qint64 elapsed = timer.elapsed(); // On Windows, we sometimes get (WaitTime - 9).
- QVERIFY2(elapsed >= Waiting_Thread::WaitTime - 10, qPrintable(QString::fromLatin1("elapsed: %1").arg(elapsed)));
+ QVERIFY2(elapsed >= Waiting_Thread::WaitTime - 10,
+ qPrintable(msgElapsed(elapsed)));
timer.start();
thread.cond1.wakeOne();
QVERIFY(thread.wait(/*Waiting_Thread::WaitTime * 1.4*/));
elapsed = timer.elapsed();
- QVERIFY2(elapsed - Waiting_Thread::WaitTime >= -1, qPrintable(QString::fromLatin1("elapsed: %1").arg(elapsed)));
+ QVERIFY2(elapsed - Waiting_Thread::WaitTime >= -1,
+ qPrintable(msgElapsed(elapsed)));
}
-
-class SlowSlotObject : public QObject {
+class SlowSlotObject : public QObject
+{
Q_OBJECT
public:
QMutex mutex;
@@ -1093,28 +1176,29 @@ void tst_QThread::wait3_slowDestructor()
{
SlowSlotObject slow;
QThread thread;
- QObject::connect(&thread, SIGNAL(finished()), &slow, SLOT(slowSlot()), Qt::DirectConnection);
-
- enum { WaitTime = 1800 };
+ QObject::connect(&thread, &QThread::finished,
+ &slow, &SlowSlotObject::slowSlot, Qt::DirectConnection);
QElapsedTimer timer;
thread.start();
thread.quit();
- //the quit function will cause the thread to finish and enter the slowSlot that is blocking
+ // Calling quit() will cause the thread to finish and enter the blocking slowSlot().
timer.start();
- QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
- qint64 elapsed = timer.elapsed();
- QVERIFY2(elapsed >= Waiting_Thread::WaitTime - 1, qPrintable(QString::fromLatin1("elapsed: %1").arg(elapsed)));
-
- slow.cond.wakeOne();
- //now the thread should finish quickly
+ {
+ // Ensure thread finishes quickly after the checks - regardless of success:
+ QScopeGuard wakeSlow([&slow]() -> void { slow.cond.wakeOne(); });
+ QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
+ const qint64 elapsed = timer.elapsed();
+ QVERIFY2(elapsed >= Waiting_Thread::WaitTime - 1,
+ qPrintable(QString::fromLatin1("elapsed: %1").arg(elapsed)));
+ }
QVERIFY(thread.wait(one_minute));
}
void tst_QThread::destroyFinishRace()
{
- class Thread : public QThread { void run() {} };
+ class Thread : public QThread { void run() override {} };
for (int i = 0; i < 15; i++) {
Thread *thr = new Thread;
connect(thr, SIGNAL(finished()), thr, SLOT(deleteLater()));
@@ -1134,9 +1218,10 @@ void tst_QThread::startFinishRace()
class Thread : public QThread {
public:
Thread() : i (50) {}
- void run() {
+ void run() override
+ {
i--;
- if (!i) disconnect(this, SIGNAL(finished()), 0, 0);
+ if (!i) disconnect(this, SIGNAL(finished()), nullptr, nullptr);
}
int i;
};
@@ -1157,7 +1242,7 @@ void tst_QThread::startFinishRace()
void tst_QThread::startAndQuitCustomEventLoop()
{
struct Thread : QThread {
- void run() { QEventLoop().exec(); }
+ void run() override { QEventLoop().exec(); }
};
for (int i = 0; i < 5; i++) {
@@ -1202,32 +1287,33 @@ void tst_QThread::isRunningInFinished()
}
}
-QT_BEGIN_NAMESPACE
-Q_CORE_EXPORT uint qGlobalPostedEventsCount();
-QT_END_NAMESPACE
-
-class DummyEventDispatcher : public QAbstractEventDispatcher {
+class DummyEventDispatcher : public QAbstractEventDispatcherV2
+{
+ Q_OBJECT
public:
- DummyEventDispatcher() : QAbstractEventDispatcher() {}
- bool processEvents(QEventLoop::ProcessEventsFlags) {
- visited.store(true);
+ bool processEvents(QEventLoop::ProcessEventsFlags) override {
+ visited.storeRelaxed(true);
emit awake();
QCoreApplication::sendPostedEvents();
return false;
}
- bool hasPendingEvents() {
- return qGlobalPostedEventsCount();
+ void registerSocketNotifier(QSocketNotifier *) override {}
+ void unregisterSocketNotifier(QSocketNotifier *) override {}
+ void registerTimer(Qt::TimerId id, Duration, Qt::TimerType, QObject *) override
+ {
+ if (registeredTimerId <= Qt::TimerId::Invalid)
+ registeredTimerId = id;
+ }
+ bool unregisterTimer(Qt::TimerId id) override
+ {
+ Qt::TimerId oldId = std::exchange(registeredTimerId, Qt::TimerId::Invalid);
+ return id == oldId;
}
- void registerSocketNotifier(QSocketNotifier *) {}
- void unregisterSocketNotifier(QSocketNotifier *) {}
- void registerTimer(int, int, Qt::TimerType, QObject *) {}
- bool unregisterTimer(int ) { return false; }
- bool unregisterTimers(QObject *) { return false; }
- QList<TimerInfo> registeredTimers(QObject *) const { return QList<TimerInfo>(); }
- int remainingTime(int) { return 0; }
- void wakeUp() {}
- void interrupt() {}
- void flush() {}
+ bool unregisterTimers(QObject *) override { return false; }
+ QList<TimerInfoV2> timersForObject(QObject *) const override { return {}; }
+ Duration remainingTime(Qt::TimerId) const override { return 0s; }
+ void wakeUp() override {}
+ void interrupt() override {}
#ifdef Q_OS_WIN
bool registerEventNotifier(QWinEventNotifier *) { return false; }
@@ -1235,25 +1321,47 @@ public:
#endif
QBasicAtomicInt visited; // bool
+ Qt::TimerId registeredTimerId = Qt::TimerId::Invalid;
};
-class ThreadObj : public QObject
+struct ThreadLocalContent
{
- Q_OBJECT
-public slots:
- void visit() {
- emit visited();
+ static inline const QMetaObject *atStart;
+ static inline const QMetaObject *atEnd;
+ QSemaphore *sem;
+ QBasicTimer timer;
+
+ ThreadLocalContent(QObject *obj, QSemaphore *sem)
+ : sem(sem)
+ {
+ ensureEventDispatcher();
+ atStart = QAbstractEventDispatcher::instance()->metaObject();
+ timer.start(10s, obj);
+ }
+ ~ThreadLocalContent()
+ {
+ ensureEventDispatcher();
+ atEnd = QAbstractEventDispatcher::instance()->metaObject();
+ timer.stop();
+ sem->release();
+ }
+
+ void ensureEventDispatcher()
+ {
+ // QEventLoop's constructor has a call to QThreadData::ensureEventDispatcher()
+ QEventLoop dummy;
}
-signals:
- void visited();
};
void tst_QThread::customEventDispatcher()
{
+ ThreadLocalContent::atStart = ThreadLocalContent::atEnd = nullptr;
+
QThread thr;
// there should be no ED yet
QVERIFY(!thr.eventDispatcher());
DummyEventDispatcher *ed = new DummyEventDispatcher;
+ QPointer<DummyEventDispatcher> weak_ed(ed);
thr.setEventDispatcher(ed);
// the new ED should be set
QCOMPARE(thr.eventDispatcher(), ed);
@@ -1262,32 +1370,46 @@ void tst_QThread::customEventDispatcher()
thr.start();
// start() should not overwrite the ED
QCOMPARE(thr.eventDispatcher(), ed);
+ QVERIFY(!weak_ed.isNull());
- ThreadObj obj;
+ QObject obj;
obj.moveToThread(&thr);
// move was successful?
QCOMPARE(obj.thread(), &thr);
- QEventLoop loop;
- connect(&obj, SIGNAL(visited()), &loop, SLOT(quit()), Qt::QueuedConnection);
- QMetaObject::invokeMethod(&obj, "visit", Qt::QueuedConnection);
- loop.exec();
+
+ QSemaphore threadLocalSemaphore;
+ QMetaObject::invokeMethod(&obj, [&]() {
+#ifndef Q_OS_WIN
+ // On Windows, the thread_locals are unsequenced between DLLs, so this
+ // could run after QThreadPrivate::finish()
+ static thread_local
+#endif
+ ThreadLocalContent d(&obj, &threadLocalSemaphore);
+ }, Qt::BlockingQueuedConnection);
+
// test that the ED has really been used
- QVERIFY(ed->visited.load());
+ QVERIFY(ed->visited.loadRelaxed());
+ // and it's ours
+ QCOMPARE(ThreadLocalContent::atStart->className(), "DummyEventDispatcher");
- QPointer<DummyEventDispatcher> weak_ed(ed);
QVERIFY(!weak_ed.isNull());
thr.quit();
+
// wait for thread to be stopped
QVERIFY(thr.wait(30000));
+ QVERIFY(threadLocalSemaphore.tryAcquire(1, 30s));
+
// test that ED has been deleted
QVERIFY(weak_ed.isNull());
+ // test that ED was ours
+ QCOMPARE(ThreadLocalContent::atEnd->className(), "DummyEventDispatcher");
}
class Job : public QObject
{
Q_OBJECT
public:
- Job(QThread *thread, int deleteDelay, bool *flag, QObject *parent = 0)
+ Job(QThread *thread, int deleteDelay, bool *flag, QObject *parent = nullptr)
: QObject(parent), quitLocker(thread), exitThreadCalled(*flag)
{
exitThreadCalled = false;
@@ -1330,13 +1452,12 @@ void tst_QThread::quitLock()
QCOMPARE(job->thread(), &thread);
loop.exec();
QVERIFY(exitThreadCalled);
+
+ delete job;
}
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));
@@ -1443,7 +1564,6 @@ void tst_QThread::create()
QCOMPARE(i, 42);
}
-#if defined(__cpp_init_captures) && __cpp_init_captures >= 201304
{
int i = 0;
MoveOnlyValue mo(123);
@@ -1455,9 +1575,7 @@ void tst_QThread::create()
QVERIFY(thread->wait());
QCOMPARE(i, 123);
}
-#endif // __cpp_init_captures
-#ifdef QTHREAD_HAS_VARIADIC_CREATE
{
int i = 0;
const auto &function = [&i](MoveOnlyValue &&mo) { i = mo.v; };
@@ -1480,10 +1598,8 @@ void tst_QThread::create()
QVERIFY(thread->wait());
QCOMPARE(i, -1);
}
-#endif // QTHREAD_HAS_VARIADIC_CREATE
}
-#ifdef QTHREAD_HAS_VARIADIC_CREATE
{
// simple parameter passing
int i = 0;
@@ -1577,12 +1693,70 @@ void tst_QThread::create()
const auto &function = [](const ThrowWhenCopying &){};
QScopedPointer<QThread> thread;
ThrowWhenCopying t;
- QVERIFY_EXCEPTION_THROWN(thread.reset(QThread::create(function, t)), ThreadException);
+ QVERIFY_THROWS_EXCEPTION(ThreadException, thread.reset(QThread::create(function, t)));
QVERIFY(!thread);
}
#endif // QT_NO_EXCEPTIONS
-#endif // QTHREAD_HAS_VARIADIC_CREATE
-#endif // QT_CONFIG(cxx11_future)
+}
+
+void tst_QThread::createDestruction()
+{
+ for (int delay : {0, 10, 20}) {
+ auto checkForInterruptions = []() {
+ for (;;) {
+ if (QThread::currentThread()->isInterruptionRequested())
+ return;
+ QThread::sleep(1ms);
+ }
+ };
+
+ QScopedPointer<QThread> thread(QThread::create(checkForInterruptions));
+ QSignalSpy finishedSpy(thread.get(), &QThread::finished);
+ QVERIFY(finishedSpy.isValid());
+
+ thread->start();
+ if (delay)
+ QThread::msleep(delay);
+ thread.reset();
+
+ QCOMPARE(finishedSpy.size(), 1);
+ }
+
+ for (int delay : {0, 10, 20}) {
+ auto runEventLoop = []() {
+ QEventLoop loop;
+ loop.exec();
+ };
+
+ QScopedPointer<QThread> thread(QThread::create(runEventLoop));
+ QSignalSpy finishedSpy(thread.get(), &QThread::finished);
+ QVERIFY(finishedSpy.isValid());
+
+ thread->start();
+ if (delay)
+ QThread::msleep(delay);
+ thread.reset();
+
+ QCOMPARE(finishedSpy.size(), 1);
+ }
+
+ for (int delay : {0, 10, 20}) {
+ auto runEventLoop = [delay]() {
+ if (delay)
+ QThread::msleep(delay);
+ QEventLoop loop;
+ loop.exec();
+ };
+
+ QScopedPointer<QThread> thread(QThread::create(runEventLoop));
+ QSignalSpy finishedSpy(thread.get(), &QThread::finished);
+ QVERIFY(finishedSpy.isValid());
+
+ thread->start();
+ thread.reset();
+
+ QCOMPARE(finishedSpy.size(), 1);
+ }
}
class StopableJob : public QObject
@@ -1623,5 +1797,155 @@ void tst_QThread::requestTermination()
QVERIFY(!thread.isInterruptionRequested());
}
+/*
+ This is a regression test for QTBUG-96846.
+
+ Incorrect system thread ID cleanup can cause QThread::wait() to report that
+ a thread is trying to wait for itself.
+*/
+void tst_QThread::threadIdReuse()
+{
+ // It's important that those thread ID's are not accessed concurrently
+ Qt::HANDLE threadId1;
+
+ auto thread1Fn = [&threadId1]() -> void { threadId1 = QThread::currentThreadId(); };
+ QScopedPointer<QThread> thread1(QThread::create(thread1Fn));
+ thread1->start();
+ QVERIFY(thread1->wait());
+
+ // If the system thread allocated for thread1 is destroyed before thread2 is started,
+ // at least on some versions of Linux the system thread ID for thread2 would be the
+ // same as one that was used for thread1.
+
+ // The system thread may be alive for some time after returning from QThread::wait()
+ // because the implementation is using detachable threads, so some additional time is
+ // required for the system thread to terminate. Not waiting long enough here would result
+ // in a new system thread ID being allocated for thread2 and this test passing even without
+ // a fix for QTBUG-96846.
+ bool threadIdReused = false;
+
+ for (int i = 0; i < 42; i++) {
+ QThread::sleep(1ms);
+
+ Qt::HANDLE threadId2;
+ bool waitOk = false;
+
+ auto waitForThread1 = [&thread1, &threadId2, &waitOk]() -> void {
+ threadId2 = QThread::currentThreadId();
+ waitOk = thread1->wait();
+ };
+
+ QScopedPointer<QThread> thread2(QThread::create(waitForThread1));
+ thread2->start();
+ QVERIFY(thread2->wait());
+ QVERIFY(waitOk);
+
+ if (threadId1 == threadId2) {
+ qDebug("Thread ID reused at iteration %d", i);
+ threadIdReused = true;
+ break;
+ }
+ }
+
+ if (!threadIdReused) {
+ QSKIP("Thread ID was not reused");
+ }
+}
+
+class WaitToRun_Thread : public QThread
+{
+ Q_OBJECT
+public:
+ void run() override
+ {
+ emit running();
+ QThread::exec();
+ }
+
+Q_SIGNALS:
+ void running();
+};
+
+
+void tst_QThread::terminateAndPrematureDestruction()
+{
+#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+ QSKIP("Thread termination might result in stack underflow address sanitizer errors.");
+#endif
+
+ WaitToRun_Thread thread;
+ QSignalSpy spy(&thread, &WaitToRun_Thread::running);
+ thread.start();
+ QVERIFY(spy.wait(500));
+
+ QScopedPointer<QObject> obj(new QObject);
+ QPointer<QObject> pObj(obj.data());
+ obj->deleteLater();
+
+ thread.terminate();
+ QVERIFY2(pObj, "object was deleted prematurely!");
+ thread.wait(500);
+}
+
+void tst_QThread::terminateAndDoubleDestruction()
+{
+#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)
+ QSKIP("Thread termination might result in stack underflow address sanitizer errors.");
+#endif
+
+ class ChildObject : public QObject
+ {
+ public:
+ ChildObject(QObject *parent)
+ : QObject(parent)
+ {
+ QSignalSpy spy(&thread, &WaitToRun_Thread::running);
+ thread.start();
+ spy.wait(500);
+ }
+
+ ~ChildObject()
+ {
+ QVERIFY2(!inDestruction, "Double object destruction!");
+ inDestruction = true;
+ thread.terminate();
+ thread.wait(500);
+ }
+
+ bool inDestruction = false;
+ WaitToRun_Thread thread;
+ };
+
+ class TestObject : public QObject
+ {
+ public:
+ TestObject()
+ : child(new ChildObject(this))
+ {
+ }
+
+ ~TestObject()
+ {
+ child->deleteLater();
+ }
+
+ ChildObject *child = nullptr;
+ };
+
+ TestObject obj;
+}
+
+void tst_QThread::bindingListCleanupAfterDelete()
+{
+ QThread t;
+ auto optr = std::make_unique<QObject>();
+ optr->moveToThread(&t);
+ auto threadPriv = static_cast<QThreadPrivate *>(QObjectPrivate::get(&t));
+ auto list = threadPriv->m_statusOrPendingObjects.list();
+ QVERIFY(list);
+ optr.reset();
+ QVERIFY(list->empty());
+}
+
QTEST_MAIN(tst_QThread)
#include "tst_qthread.moc"
diff --git a/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt b/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
new file mode 100644
index 0000000000..2c92ca002e
--- /dev/null
+++ b/tests/auto/corelib/thread/qthreadonce/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qthreadonce Test:
+#####################################################################
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qthreadonce LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qthreadonce
+ SOURCES
+ qthreadonce.cpp
+ tst_qthreadonce.cpp
+ LIBRARIES
+ Qt::TestPrivate
+)
diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
index d27884197a..b32f455241 100644
--- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
+++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qplatformdefs.h"
@@ -32,7 +7,7 @@
#include "qmutex.h"
-Q_GLOBAL_STATIC_WITH_ARGS(QMutex, onceInitializationMutex, (QMutex::Recursive))
+Q_GLOBAL_STATIC(QRecursiveMutex, onceInitializationMutex)
enum QOnceExtra {
MustRunCode = 0x01,
diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.h b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h
index e5918b8fa5..1f804433e4 100644
--- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.h
+++ b/tests/auto/corelib/thread/qthreadonce/qthreadonce.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef QTHREADONCE_H
diff --git a/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro b/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro
deleted file mode 100644
index e8b95a06a7..0000000000
--- a/tests/auto/corelib/thread/qthreadonce/qthreadonce.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qthreadonce
-QT = core testlib
-SOURCES = tst_qthreadonce.cpp
-
-# Don't use gcc's threadsafe statics
-# Note: some versions of gcc generate invalid code with this option...
-# Some versions of gcc don't even have it, so disable it
-#*-g++*:QMAKE_CXXFLAGS += -fno-threadsafe-statics
-
-# Temporary:
-SOURCES += qthreadonce.cpp
diff --git a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
index a9af182ed8..37e1f744f3 100644
--- a/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
+++ b/tests/auto/corelib/thread/qthreadonce/tst_qthreadonce.cpp
@@ -1,33 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+
+#include <QTest>
+#include <QSemaphore>
#include <qcoreapplication.h>
#include <qmutex.h>
@@ -35,11 +11,14 @@
#include <qwaitcondition.h>
#include "qthreadonce.h"
+#include <QtTest/private/qemulationdetector_p.h>
+
class tst_QThreadOnce : public QObject
{
Q_OBJECT
private slots:
+ void initTestCase();
void sameThread();
void sameThread_data();
void multipleThreads();
@@ -51,12 +30,18 @@ private slots:
#endif
};
+void tst_QThreadOnce::initTestCase()
+{
+ if (QTestPrivate::isRunningArmOnX86())
+ QSKIP("Flaky on QEMU, QTBUG-94737");
+}
+
class SingletonObject: public QObject
{
Q_OBJECT
public:
static int runCount;
- SingletonObject() { val.store(42); ++runCount; }
+ SingletonObject() { val.storeRelaxed(42); ++runCount; }
~SingletonObject() { }
QBasicAtomicInt val;
@@ -77,7 +62,7 @@ public:
~IncrementThread() { wait(); }
protected:
- void run()
+ void run() override
{
sem2.release();
sem1.acquire(); // synchronize
@@ -112,7 +97,7 @@ void tst_QThreadOnce::sameThread()
QCOMPARE(controlVariable, 1);
static QSingleton<SingletonObject> s;
- QTEST((int)s->val.load(), "expectedValue");
+ QTEST(int(s->val.loadRelaxed()), "expectedValue");
s->val.ref();
QCOMPARE(SingletonObject::runCount, 1);
@@ -134,7 +119,7 @@ void tst_QThreadOnce::multipleThreads()
QCOMPARE(controlVariable, 0); // nothing must have set them yet
SingletonObject::runCount = 0;
- IncrementThread::runCount.store(0);
+ IncrementThread::runCount.storeRelaxed(0);
// wait for all of them to be ready
sem2.acquire(NumberOfThreads);
@@ -145,7 +130,7 @@ void tst_QThreadOnce::multipleThreads()
delete parent;
QCOMPARE(controlVariable, 1);
- QCOMPARE((int)IncrementThread::runCount.load(), NumberOfThreads);
+ QCOMPARE(int(IncrementThread::runCount.loadRelaxed()), NumberOfThreads);
QCOMPARE(SingletonObject::runCount, 1);
}
diff --git a/tests/auto/corelib/thread/qthreadpool/BLACKLIST b/tests/auto/corelib/thread/qthreadpool/BLACKLIST
deleted file mode 100644
index fc49731687..0000000000
--- a/tests/auto/corelib/thread/qthreadpool/BLACKLIST
+++ /dev/null
@@ -1,3 +0,0 @@
-[expiryTimeoutRace]
-osx
-linux
diff --git a/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt b/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
new file mode 100644
index 0000000000..fee9c541db
--- /dev/null
+++ b/tests/auto/corelib/thread/qthreadpool/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qthreadpool.pro b/tests/auto/corelib/thread/qthreadpool/qthreadpool.pro
deleted file mode 100644
index eac4ef9365..0000000000
--- a/tests/auto/corelib/thread/qthreadpool/qthreadpool.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qthreadpool
-QT = core testlib
-SOURCES = tst_qthreadpool.cpp
diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
index 838431cd5a..2006016d47 100644
--- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
+++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
@@ -1,33 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include <QtTest/QtTest>
-#include <qdatetime.h>
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QSemaphore>
+
+#include <qelapsedtimer.h>
+#include <qrunnable.h>
#include <qthreadpool.h>
#include <qstring.h>
#include <qmutex.h>
@@ -36,6 +15,8 @@
#include <unistd.h>
#endif
+using namespace std::chrono_literals;
+
typedef void (*FunctionPointer)();
class FunctionPointerTask : public QRunnable
@@ -43,7 +24,7 @@ class FunctionPointerTask : public QRunnable
public:
FunctionPointerTask(FunctionPointer function)
:function(function) {}
- void run() { function(); }
+ void run() override { function(); }
private:
FunctionPointer function;
};
@@ -64,6 +45,8 @@ public:
private slots:
void runFunction();
+ void runFunction2();
+ void runFunction3();
void createThreadRunFunction();
void runMultiple();
void waitcomplete();
@@ -71,6 +54,7 @@ private slots:
void singleton();
void destruction();
void threadRecycling();
+ void threadPriority();
void expiryTimeout();
void expiryTimeoutRace();
#ifndef QT_NO_EXCEPTIONS
@@ -84,6 +68,8 @@ private slots:
void releaseThread_data();
void releaseThread();
void reserveAndStart();
+ void reserveAndStart2();
+ void releaseAndBlock();
void start();
void tryStart();
void tryStartPeakThreadCount();
@@ -92,7 +78,7 @@ private slots:
void priorityStart();
void waitForDone();
void clear();
- void cancel();
+ void clearWithAutoDelete();
void tryTake();
void waitForDoneTimeout();
void destroyingWaitsForTasksToFinish();
@@ -100,13 +86,15 @@ private slots:
void stressTest();
void takeAllAndIncreaseMaxThreadCount();
void waitForDoneAfterTake();
+ void threadReuse();
+ void nullFunctions();
private:
QMutex m_functionTestMutex;
};
-QMutex *tst_QThreadPool::functionTestMutex = 0;
+QMutex *tst_QThreadPool::functionTestMutex = nullptr;
tst_QThreadPool::tst_QThreadPool()
{
@@ -115,10 +103,10 @@ tst_QThreadPool::tst_QThreadPool()
tst_QThreadPool::~tst_QThreadPool()
{
- tst_QThreadPool::functionTestMutex = 0;
+ tst_QThreadPool::functionTestMutex = nullptr;
}
-int testFunctionCount;
+static int testFunctionCount;
void sleepTestFunction()
{
@@ -153,22 +141,64 @@ 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(createTask(noSleepTestFunction));
+ manager.start(noSleepTestFunction);
}
QCOMPARE(testFunctionCount, 1);
}
+void tst_QThreadPool::runFunction2()
+{
+ int localCount = 0;
+ {
+ 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(createTask(noSleepTestFunction));
+ manager.start(noSleepTestFunction);
}
QCOMPARE(testFunctionCount, 1);
@@ -179,27 +209,27 @@ void tst_QThreadPool::runMultiple()
const int runs = 10;
{
- QThreadPool manager;
+ TestThreadPool manager;
testFunctionCount = 0;
for (int i = 0; i < runs; ++i) {
- manager.start(createTask(sleepTestFunctionMutex));
+ manager.start(sleepTestFunctionMutex);
}
}
QCOMPARE(testFunctionCount, runs);
{
- QThreadPool manager;
+ TestThreadPool manager;
testFunctionCount = 0;
for (int i = 0; i < runs; ++i) {
- manager.start(createTask(noSleepTestFunctionMutex));
+ manager.start(noSleepTestFunctionMutex);
}
}
QCOMPARE(testFunctionCount, runs);
{
- QThreadPool manager;
+ TestThreadPool manager;
for (int i = 0; i < 500; ++i)
- manager.start(createTask(emptyFunct));
+ manager.start(emptyFunct);
}
}
@@ -208,28 +238,29 @@ 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(createTask(noSleepTestFunction));
+ pool.start(noSleepTestFunction);
}
QCOMPARE(testFunctionCount, runs);
}
-QAtomicInt ran; // bool
+static QAtomicInt ran; // bool
class TestTask : public QRunnable
{
public:
- void run()
+ void run() override
{
- ran.store(true);
+ ran.storeRelaxed(true);
}
};
void tst_QThreadPool::runTask()
{
- QThreadPool manager;
- ran.store(false);
+ TestThreadPool manager;
+ ran.storeRelaxed(false);
manager.start(new TestTask());
- QTRY_VERIFY(ran.load());
+ QTRY_VERIFY(ran.loadRelaxed());
}
/*
@@ -237,16 +268,16 @@ void tst_QThreadPool::runTask()
*/
void tst_QThreadPool::singleton()
{
- ran.store(false);
+ ran.storeRelaxed(false);
QThreadPool::globalInstance()->start(new TestTask());
- QTRY_VERIFY(ran.load());
+ QTRY_VERIFY(ran.loadRelaxed());
}
-QAtomicInt *value = 0;
+static QAtomicInt *value = nullptr;
class IntAccessor : public QRunnable
{
public:
- void run()
+ void run() override
{
for (int i = 0; i < 100; ++i) {
value->ref();
@@ -267,16 +298,16 @@ void tst_QThreadPool::destruction()
threadManager->start(new IntAccessor());
delete threadManager;
delete value;
- value = 0;
+ value = nullptr;
}
-QSemaphore threadRecyclingSemaphore;
-QThread *recycledThread = 0;
+static QSemaphore threadRecyclingSemaphore;
+static QThread *recycledThread = nullptr;
class ThreadRecorderTask : public QRunnable
{
public:
- void run()
+ void run() override
{
recycledThread = QThread::currentThread();
threadRecyclingSemaphore.release();
@@ -288,7 +319,7 @@ public:
*/
void tst_QThreadPool::threadRecycling()
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.start(new ThreadRecorderTask());
threadRecyclingSemaphore.acquire();
@@ -309,6 +340,25 @@ void tst_QThreadPool::threadRecycling()
QCOMPARE(thread2, thread3);
}
+/*
+ Test that the thread priority from the thread created by the pool matches
+ the one configured on the pool.
+*/
+void tst_QThreadPool::threadPriority()
+{
+ QThread::Priority priority = QThread::HighPriority;
+ TestThreadPool threadPool;
+ threadPool.setThreadPriority(priority);
+
+ threadPool.start(new ThreadRecorderTask());
+ threadRecyclingSemaphore.acquire();
+ QThread *thread = recycledThread;
+
+ QTest::qSleep(100);
+
+ QCOMPARE(thread->priority(), priority);
+}
+
class ExpiryTimeoutTask : public QRunnable
{
public:
@@ -317,12 +367,12 @@ public:
QSemaphore semaphore;
ExpiryTimeoutTask()
- : thread(0), runCount(0)
+ : thread(nullptr), runCount(0)
{
setAutoDelete(false);
}
- void run()
+ void run() override
{
thread = QThread::currentThread();
runCount.ref();
@@ -334,7 +384,7 @@ void tst_QThreadPool::expiryTimeout()
{
ExpiryTimeoutTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
int expiryTimeout = threadPool.expiryTimeout();
@@ -344,7 +394,7 @@ void tst_QThreadPool::expiryTimeout()
// run the task
threadPool.start(&task);
QVERIFY(task.semaphore.tryAcquire(1, 10000));
- QCOMPARE(task.runCount.load(), 1);
+ QCOMPARE(task.runCount.loadRelaxed(), 1);
QVERIFY(!task.thread->wait(100));
// thread should expire
QThread *firstThread = task.thread;
@@ -353,7 +403,7 @@ void tst_QThreadPool::expiryTimeout()
// run task again, thread should be restarted
threadPool.start(&task);
QVERIFY(task.semaphore.tryAcquire(1, 10000));
- QCOMPARE(task.runCount.load(), 2);
+ QCOMPARE(task.runCount.loadRelaxed(), 2);
QVERIFY(!task.thread->wait(100));
// thread should expire again
QVERIFY(task.thread->wait(10000));
@@ -368,21 +418,18 @@ 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.load(), numTasks);
+ QCOMPARE(task.runCount.loadRelaxed(), numTasks);
QVERIFY(threadPool.waitForDone(2000));
}
@@ -390,7 +437,7 @@ void tst_QThreadPool::expiryTimeoutRace() // QTBUG-3786
class ExceptionTask : public QRunnable
{
public:
- void run()
+ void run() override
{
throw new int;
}
@@ -400,7 +447,7 @@ void tst_QThreadPool::exceptions()
{
ExceptionTask task;
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
// Uncomment this for a nice crash.
// threadPool.start(&task);
}
@@ -429,6 +476,9 @@ void tst_QThreadPool::setMaxThreadCount()
QFETCH(int, limit);
QThreadPool *threadPool = QThreadPool::globalInstance();
int savedLimit = threadPool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadPool->setMaxThreadCount(savedLimit);
+ });
// maxThreadCount() should always return the previous argument to
// setMaxThreadCount(), regardless of input
@@ -441,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
@@ -464,65 +514,64 @@ void tst_QThreadPool::setMaxThreadCountStartsAndStopsThreads()
WaitingTask() { setAutoDelete(false); }
- void run()
+ void run() override
{
waitForStarted.release();
waitToFinish.acquire();
}
};
- QThreadPool threadPool;
- threadPool.setMaxThreadCount(1);
+ TestThreadPool threadPool;
+ threadPool.setMaxThreadCount(-1); // docs say we'll always start at least one
- WaitingTask *task = new WaitingTask;
- threadPool.start(task);
- QVERIFY(task->waitForStarted.tryAcquire(1, 1000));
+ WaitingTask task;
+ threadPool.start(&task);
+ QVERIFY(task.waitForStarted.tryAcquire(1, 1000));
// thread limit is 1, cannot start more tasks
- threadPool.start(task);
- QVERIFY(!task->waitForStarted.tryAcquire(1, 1000));
+ threadPool.start(&task);
+ QVERIFY(!task.waitForStarted.tryAcquire(1, 1000));
// increasing the limit by 1 should start the task immediately
threadPool.setMaxThreadCount(2);
- QVERIFY(task->waitForStarted.tryAcquire(1, 1000));
+ QVERIFY(task.waitForStarted.tryAcquire(1, 1000));
// ... but we still cannot start more tasks
- threadPool.start(task);
- QVERIFY(!task->waitForStarted.tryAcquire(1, 1000));
+ threadPool.start(&task);
+ QVERIFY(!task.waitForStarted.tryAcquire(1, 1000));
// increasing the limit should be able to start more than one at a time
- threadPool.start(task);
+ threadPool.start(&task);
threadPool.setMaxThreadCount(4);
- QVERIFY(task->waitForStarted.tryAcquire(2, 1000));
+ QVERIFY(task.waitForStarted.tryAcquire(2, 1000));
// ... but we still cannot start more tasks
- threadPool.start(task);
- threadPool.start(task);
- QVERIFY(!task->waitForStarted.tryAcquire(2, 1000));
+ threadPool.start(&task);
+ threadPool.start(&task);
+ QVERIFY(!task.waitForStarted.tryAcquire(2, 1000));
// decreasing the thread limit should cause the active thread count to go down
threadPool.setMaxThreadCount(2);
QCOMPARE(threadPool.activeThreadCount(), 4);
- task->waitToFinish.release(2);
+ task.waitToFinish.release(2);
QTest::qWait(1000);
QCOMPARE(threadPool.activeThreadCount(), 2);
// ... and we still cannot start more tasks
- threadPool.start(task);
- threadPool.start(task);
- QVERIFY(!task->waitForStarted.tryAcquire(2, 1000));
+ threadPool.start(&task);
+ threadPool.start(&task);
+ QVERIFY(!task.waitForStarted.tryAcquire(2, 1000));
// start all remaining tasks
- threadPool.start(task);
- threadPool.start(task);
- threadPool.start(task);
- threadPool.start(task);
+ threadPool.start(&task);
+ threadPool.start(&task);
+ threadPool.start(&task);
+ threadPool.start(&task);
threadPool.setMaxThreadCount(8);
- QVERIFY(task->waitForStarted.tryAcquire(6, 1000));
+ QVERIFY(task.waitForStarted.tryAcquire(6, 1000));
- task->waitToFinish.release(10);
+ task.waitToFinish.release(10);
threadPool.waitForDone();
- delete task;
}
void tst_QThreadPool::reserveThread_data()
@@ -534,7 +583,11 @@ void tst_QThreadPool::reserveThread()
{
QFETCH(int, limit);
QThreadPool *threadpool = QThreadPool::globalInstance();
- int savedLimit = threadpool->maxThreadCount();
+ const int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
+
threadpool->setMaxThreadCount(limit);
// reserve up to the limit
@@ -556,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
@@ -583,9 +636,6 @@ void tst_QThreadPool::reserveThread()
while (threadpool2.activeThreadCount() > 0)
threadpool2.releaseThread();
}
-
- // reset limit on global QThreadPool
- threadpool->setMaxThreadCount(savedLimit);
}
void tst_QThreadPool::releaseThread_data()
@@ -597,7 +647,10 @@ void tst_QThreadPool::releaseThread()
{
QFETCH(int, limit);
QThreadPool *threadpool = QThreadPool::globalInstance();
- int savedLimit = threadpool->maxThreadCount();
+ const int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
threadpool->setMaxThreadCount(limit);
// reserve up to the limit
@@ -620,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
@@ -645,9 +698,6 @@ void tst_QThreadPool::releaseThread()
QCOMPARE(threadpool2.activeThreadCount(), 0);
QCOMPARE(threadpool->activeThreadCount(), 0);
}
-
- // reset limit on global QThreadPool
- threadpool->setMaxThreadCount(savedLimit);
}
void tst_QThreadPool::reserveAndStart() // QTBUG-21051
@@ -661,7 +711,7 @@ void tst_QThreadPool::reserveAndStart() // QTBUG-21051
WaitingTask() { setAutoDelete(false); }
- void run()
+ void run() override
{
count.ref();
waitForStarted.release();
@@ -672,6 +722,10 @@ void tst_QThreadPool::reserveAndStart() // QTBUG-21051
// Set up
QThreadPool *threadpool = QThreadPool::globalInstance();
int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
+
threadpool->setMaxThreadCount(1);
QCOMPARE(threadpool->activeThreadCount(), 0);
@@ -679,40 +733,137 @@ void tst_QThreadPool::reserveAndStart() // QTBUG-21051
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), 1);
- // start a task, to get a running thread
- WaitingTask *task = new WaitingTask;
- threadpool->start(task);
+ // start a task, to get a running thread, works since one thread is always allowed
+ WaitingTask task;
+ threadpool->start(&task);
QCOMPARE(threadpool->activeThreadCount(), 2);
- task->waitForStarted.acquire();
- task->waitBeforeDone.release();
- QTRY_COMPARE(task->count.load(), 1);
+ // tryStart() will fail since activeThreadCount() >= maxThreadCount() and one thread is already running
+ QVERIFY(!threadpool->tryStart(&task));
+ QTRY_COMPARE(threadpool->activeThreadCount(), 2);
+ task.waitForStarted.acquire();
+ task.waitBeforeDone.release();
+ QTRY_COMPARE(task.count.loadRelaxed(), 1);
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
- // now the thread is waiting, but tryStart() will fail since activeThreadCount() >= maxThreadCount()
- QVERIFY(!threadpool->tryStart(task));
+ // start() will wake up the waiting thread.
+ threadpool->start(&task);
+ QTRY_COMPARE(threadpool->activeThreadCount(), 2);
+ QTRY_COMPARE(task.count.loadRelaxed(), 2);
+ WaitingTask task2;
+ // startOnReservedThread() will try to take the reserved task, but end up waiting instead
+ threadpool->startOnReservedThread(&task2);
+ QTRY_COMPARE(threadpool->activeThreadCount(), 1);
+ task.waitForStarted.acquire();
+ task.waitBeforeDone.release();
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
+ task2.waitForStarted.acquire();
+ task2.waitBeforeDone.release();
- // start() will therefore do a failing tryStart(), followed by enqueueTask()
- // which will actually wake up the waiting thread.
- threadpool->start(task);
- QTRY_COMPARE(threadpool->activeThreadCount(), 2);
- task->waitForStarted.acquire();
- task->waitBeforeDone.release();
- QTRY_COMPARE(task->count.load(), 2);
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
+}
+
+void tst_QThreadPool::reserveAndStart2()
+{
+ class WaitingTask : public QRunnable
+ {
+ public:
+ QSemaphore waitBeforeDone;
+
+ WaitingTask() { setAutoDelete(false); }
+
+ void run() override
+ {
+ waitBeforeDone.acquire();
+ }
+ };
+
+ // Set up
+ QThreadPool *threadpool = QThreadPool::globalInstance();
+ int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
+ threadpool->setMaxThreadCount(2);
+
+ // reserve
+ threadpool->reserveThread();
+
+ // start two task, to get a running thread and one queued
+ WaitingTask task1, task2, task3;
+ threadpool->start(&task1);
+ // one running thread, one reserved:
+ QCOMPARE(threadpool->activeThreadCount(), 2);
+ // task2 starts queued
+ threadpool->start(&task2);
+ QCOMPARE(threadpool->activeThreadCount(), 2);
+ // startOnReservedThread() will take the reserved thread however, bypassing the queue
+ threadpool->startOnReservedThread(&task3);
+ // two running threads, none reserved:
+ QCOMPARE(threadpool->activeThreadCount(), 2);
+ task3.waitBeforeDone.release();
+ // task3 can finish even if all other tasks are blocking
+ // then task2 will use the previously reserved thread
+ task2.waitBeforeDone.release();
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
+ task1.waitBeforeDone.release();
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
+}
+
+void tst_QThreadPool::releaseAndBlock()
+{
+ class WaitingTask : public QRunnable
+ {
+ public:
+ QSemaphore waitBeforeDone;
+ WaitingTask() { setAutoDelete(false); }
+
+ void run() override
+ {
+ waitBeforeDone.acquire();
+ }
+ };
+
+ // Set up
+ QThreadPool *threadpool = QThreadPool::globalInstance();
+ const int savedLimit = threadpool->maxThreadCount();
+ auto restoreThreadCount = qScopeGuard([=]{
+ threadpool->setMaxThreadCount(savedLimit);
+ });
+
+ threadpool->setMaxThreadCount(1);
+ QCOMPARE(threadpool->activeThreadCount(), 0);
+
+ // start a task, to get a running thread, works since one thread is always allowed
+ WaitingTask task1, task2;
+ threadpool->start(&task1);
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+
+ // tryStart() will fail since activeThreadCount() >= maxThreadCount() and one thread is already running
+ QVERIFY(!threadpool->tryStart(&task2));
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+
+ // Use release without reserve to account for the blocking thread.
threadpool->releaseThread();
QTRY_COMPARE(threadpool->activeThreadCount(), 0);
- delete task;
+ // Now we can start task2
+ QVERIFY(threadpool->tryStart(&task2));
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+ task2.waitBeforeDone.release();
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
- threadpool->setMaxThreadCount(savedLimit);
+ threadpool->reserveThread();
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+ task1.waitBeforeDone.release();
+ QTRY_COMPARE(threadpool->activeThreadCount(), 0);
}
-QAtomicInt count;
+static QAtomicInt count;
class CountingRunnable : public QRunnable
{
- public: void run()
+public:
+ void run() override
{
count.ref();
}
@@ -721,14 +872,14 @@ class CountingRunnable : public QRunnable
void tst_QThreadPool::start()
{
const int runs = 1000;
- count.store(0);
+ count.storeRelaxed(0);
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
for (int i = 0; i< runs; ++i) {
threadPool.start(new CountingRunnable());
}
}
- QCOMPARE(count.load(), runs);
+ QCOMPARE(count.loadRelaxed(), runs);
}
void tst_QThreadPool::tryStart()
@@ -740,29 +891,29 @@ void tst_QThreadPool::tryStart()
WaitingTask() { setAutoDelete(false); }
- void run()
+ void run() override
{
semaphore.acquire();
count.ref();
}
};
- count.store(0);
+ 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();
- QCOMPARE(count.load(), threadPool.maxThreadCount());
+ WAIT_FOR_DONE(threadPool);
+ QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount());
}
-QMutex mutex;
-QAtomicInt activeThreads;
-QAtomicInt peakActiveThreads;
+static QMutex mutex;
+static QAtomicInt activeThreads;
+static QAtomicInt peakActiveThreads;
void tst_QThreadPool::tryStartPeakThreadCount()
{
class CounterTask : public QRunnable
@@ -770,12 +921,12 @@ void tst_QThreadPool::tryStartPeakThreadCount()
public:
CounterTask() { setAutoDelete(false); }
- void run()
+ void run() override
{
{
QMutexLocker lock(&mutex);
activeThreads.ref();
- peakActiveThreads.store(qMax(peakActiveThreads.load(), activeThreads.load()));
+ peakActiveThreads.storeRelaxed(qMax(peakActiveThreads.loadRelaxed(), activeThreads.loadRelaxed()));
}
QTest::qWait(100);
@@ -787,19 +938,19 @@ void tst_QThreadPool::tryStartPeakThreadCount()
};
CounterTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
- for (int i = 0; i < 20; ++i) {
+ for (int i = 0; i < 4*QThread::idealThreadCount(); ++i) {
if (threadPool.tryStart(&task) == false)
QTest::qWait(10);
}
- QCOMPARE(peakActiveThreads.load(), QThread::idealThreadCount());
+ QCOMPARE(peakActiveThreads.loadRelaxed(), QThread::idealThreadCount());
for (int i = 0; i < 20; ++i) {
if (threadPool.tryStart(&task) == false)
QTest::qWait(10);
}
- QCOMPARE(peakActiveThreads.load(), QThread::idealThreadCount());
+ QCOMPARE(peakActiveThreads.loadRelaxed(), QThread::idealThreadCount());
}
void tst_QThreadPool::tryStartCount()
@@ -809,14 +960,14 @@ void tst_QThreadPool::tryStartCount()
public:
SleeperTask() { setAutoDelete(false); }
- void run()
+ void run() override
{
QTest::qWait(50);
}
};
SleeperTask task;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
const int runs = 5;
for (int i = 0; i < runs; ++i) {
@@ -844,7 +995,7 @@ void tst_QThreadPool::priorityStart()
public:
QSemaphore &sem;
Holder(QSemaphore &sem) : sem(sem) {}
- void run()
+ void run() override
{
sem.acquire();
}
@@ -854,9 +1005,9 @@ void tst_QThreadPool::priorityStart()
public:
QAtomicPointer<QRunnable> &ptr;
Runner(QAtomicPointer<QRunnable> &ptr) : ptr(ptr) {}
- void run()
+ void run() override
{
- ptr.testAndSetRelaxed(0, this);
+ ptr.testAndSetRelaxed(nullptr, this);
}
};
@@ -864,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
@@ -876,36 +1027,37 @@ void tst_QThreadPool::priorityStart()
threadPool.start(expected = new Runner(firstStarted), 1); // priority 1
sem.release();
- QVERIFY(threadPool.waitForDone());
- QCOMPARE(firstStarted.load(), expected);
+ WAIT_FOR_DONE(threadPool);
+ QCOMPARE(firstStarted.loadRelaxed(), expected);
}
void tst_QThreadPool::waitForDone()
{
- QTime total, pass;
+ QElapsedTimer total, pass;
total.start();
+ pass.start();
- QThreadPool threadPool;
+ TestThreadPool threadPool;
while (total.elapsed() < 10000) {
int runs;
- count.store(runs = 0);
+ count.storeRelaxed(runs = 0);
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
++runs;
}
- threadPool.waitForDone();
- QCOMPARE(count.load(), runs);
+ WAIT_FOR_DONE(threadPool);
+ QCOMPARE(count.loadRelaxed(), runs);
- count.store(runs = 0);
+ count.storeRelaxed(runs = 0);
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
threadPool.start(new CountingRunnable());
runs += 2;
}
- threadPool.waitForDone();
- QCOMPARE(count.load(), runs);
+ WAIT_FOR_DONE(threadPool);
+ QCOMPARE(count.loadRelaxed(), runs);
}
}
@@ -918,7 +1070,7 @@ void tst_QThreadPool::waitForDoneTimeout()
QMutex &mutex;
explicit BlockedTask(QMutex &m) : mutex(m) {}
- void run()
+ void run() override
{
mutex.lock();
mutex.unlock();
@@ -926,7 +1078,7 @@ void tst_QThreadPool::waitForDoneTimeout()
}
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
mutex.lock();
threadPool.start(new BlockedTask(mutex));
@@ -943,96 +1095,48 @@ void tst_QThreadPool::clear()
public:
QSemaphore & sem;
BlockingRunnable(QSemaphore & sem) : sem(sem){}
- void run()
+ void run() override
{
sem.acquire();
count.ref();
}
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(10);
int runs = 2 * threadPool.maxThreadCount();
- count.store(0);
+ count.storeRelaxed(0);
for (int i = 0; i <= runs; i++) {
threadPool.start(new BlockingRunnable(sem));
}
threadPool.clear();
sem.release(threadPool.maxThreadCount());
- threadPool.waitForDone();
- QCOMPARE(count.load(), threadPool.maxThreadCount());
+ WAIT_FOR_DONE(threadPool);
+ QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount());
}
-void tst_QThreadPool::cancel()
+void tst_QThreadPool::clearWithAutoDelete()
{
- QSemaphore sem(0);
- QSemaphore startedThreads(0);
-
- class BlockingRunnable : public QRunnable
+ class MyRunnable : public QRunnable
{
public:
- QSemaphore & sem;
- QSemaphore &startedThreads;
- QAtomicInt &dtorCounter;
- QAtomicInt &runCounter;
- int dummy;
-
- explicit BlockingRunnable(QSemaphore &s, QSemaphore &started, QAtomicInt &c, QAtomicInt &r)
- : sem(s), startedThreads(started), dtorCounter(c), runCounter(r){}
-
- ~BlockingRunnable()
- {
- dtorCounter.fetchAndAddRelaxed(1);
- }
-
- void run()
- {
- startedThreads.release();
- runCounter.fetchAndAddRelaxed(1);
- sem.acquire();
- count.ref();
- }
- };
-
- enum {
- MaxThreadCount = 3,
- OverProvisioning = 2,
- runs = MaxThreadCount * OverProvisioning
+ MyRunnable() {}
+ void run() override { QThread::sleep(30us); }
};
- QThreadPool threadPool;
- threadPool.setMaxThreadCount(MaxThreadCount);
- BlockingRunnable *runnables[runs];
-
- // ensure that the QThreadPool doesn't deadlock if any of the checks fail
- // and cause an early return:
- const QSemaphoreReleaser semReleaser(sem, runs);
-
- count.store(0);
- QAtomicInt dtorCounter = 0;
- QAtomicInt runCounter = 0;
- for (int i = 0; i < runs; i++) {
- runnables[i] = new BlockingRunnable(sem, startedThreads, dtorCounter, runCounter);
- runnables[i]->setAutoDelete(i != 0 && i != (runs-1)); //one which will run and one which will not
- threadPool.cancel(runnables[i]); //verify NOOP for jobs not in the queue
- threadPool.start(runnables[i]);
- }
- // wait for all worker threads to have started up:
- QVERIFY(startedThreads.tryAcquire(MaxThreadCount, 60*1000 /* 1min */));
-
- for (int i = 0; i < runs; i++) {
- threadPool.cancel(runnables[i]);
+ TestThreadPool threadPool;
+ threadPool.setMaxThreadCount(4);
+ const int loopCount = 20;
+ const int batchSize = 500;
+ // Should not crash see QTBUG-87092
+ for (int i = 0; i < loopCount; i++) {
+ threadPool.clear();
+ for (int j = 0; j < batchSize; j++) {
+ auto *runnable = new MyRunnable();
+ runnable->setAutoDelete(true);
+ threadPool.start(runnable);
+ }
}
- runnables[0]->dummy = 0; //valgrind will catch this if cancel() is crazy enough to delete currently running jobs
- runnables[runs-1]->dummy = 0;
- QCOMPARE(dtorCounter.load(), runs - threadPool.maxThreadCount() - 1);
- sem.release(threadPool.maxThreadCount());
- threadPool.waitForDone();
- QCOMPARE(runCounter.load(), threadPool.maxThreadCount());
- QCOMPARE(count.load(), threadPool.maxThreadCount());
- QCOMPARE(dtorCounter.load(), runs - 2);
- delete runnables[0]; //if the pool deletes them then we'll get double-free crash
- delete runnables[runs-1];
}
void tst_QThreadPool::tryTake()
@@ -1052,7 +1156,7 @@ void tst_QThreadPool::tryTake()
explicit BlockingRunnable(QSemaphore &s, QSemaphore &started, QAtomicInt &c, QAtomicInt &r)
: sem(s), startedThreads(started), dtorCounter(c), runCounter(r) {}
- ~BlockingRunnable()
+ ~BlockingRunnable() override
{
dtorCounter.fetchAndAddRelaxed(1);
}
@@ -1072,7 +1176,7 @@ void tst_QThreadPool::tryTake()
Runs = MaxThreadCount * OverProvisioning
};
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(MaxThreadCount);
BlockingRunnable *runnables[Runs];
@@ -1080,7 +1184,7 @@ void tst_QThreadPool::tryTake()
// and cause an early return:
const QSemaphoreReleaser semReleaser(sem, Runs);
- count.store(0);
+ count.storeRelaxed(0);
QAtomicInt dtorCounter = 0;
QAtomicInt runCounter = 0;
for (int i = 0; i < Runs; i++) {
@@ -1102,36 +1206,37 @@ void tst_QThreadPool::tryTake()
}
runnables[0]->dummy = 0; // valgrind will catch this if tryTake() is crazy enough to delete currently running jobs
- QCOMPARE(dtorCounter.load(), int(Runs - MaxThreadCount));
+ QCOMPARE(dtorCounter.loadRelaxed(), int(Runs - MaxThreadCount));
sem.release(MaxThreadCount);
- threadPool.waitForDone();
- QCOMPARE(runCounter.load(), int(MaxThreadCount));
- QCOMPARE(count.load(), int(MaxThreadCount));
- QCOMPARE(dtorCounter.load(), int(Runs - 1));
+ WAIT_FOR_DONE(threadPool);
+ QCOMPARE(runCounter.loadRelaxed(), int(MaxThreadCount));
+ QCOMPARE(count.loadRelaxed(), int(MaxThreadCount));
+ QCOMPARE(dtorCounter.loadRelaxed(), int(Runs - 1));
delete runnables[0]; // if the pool deletes them then we'll get double-free crash
}
void tst_QThreadPool::destroyingWaitsForTasksToFinish()
{
- QTime total, pass;
+ QElapsedTimer total, pass;
total.start();
+ pass.start();
while (total.elapsed() < 10000) {
int runs;
- count.store(runs = 0);
+ count.storeRelaxed(runs = 0);
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
++runs;
}
}
- QCOMPARE(count.load(), runs);
+ QCOMPARE(count.loadRelaxed(), runs);
- count.store(runs = 0);
+ count.storeRelaxed(runs = 0);
{
- QThreadPool threadPool;
+ TestThreadPool threadPool;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
@@ -1139,7 +1244,7 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish()
runs += 2;
}
}
- QCOMPARE(count.load(), runs);
+ QCOMPARE(count.loadRelaxed(), runs);
}
}
@@ -1167,16 +1272,16 @@ void tst_QThreadPool::stackSize()
}
- void run()
+ void run() override
{
*stackSize = QThread::currentThread()->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);
}
@@ -1198,13 +1303,13 @@ void tst_QThreadPool::stressTest()
semaphore.acquire();
}
- void run()
+ void run() override
{
semaphore.release();
}
};
- QTime total;
+ QElapsedTimer total;
total.start();
while (total.elapsed() < 30000) {
Task t;
@@ -1224,7 +1329,8 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
setAutoDelete(false);
}
- void run() {
+ void run() override
+ {
m_mainBarrier->release();
m_threadBarrier->acquire();
}
@@ -1236,24 +1342,24 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
QSemaphore mainBarrier;
QSemaphore taskBarrier;
- QThreadPool threadPool;
+ TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
- Task *task1 = new Task(&mainBarrier, &taskBarrier);
- Task *task2 = new Task(&mainBarrier, &taskBarrier);
- Task *task3 = new Task(&mainBarrier, &taskBarrier);
+ Task task1(&mainBarrier, &taskBarrier);
+ Task task2(&mainBarrier, &taskBarrier);
+ Task task3(&mainBarrier, &taskBarrier);
- threadPool.start(task1);
- threadPool.start(task2);
- threadPool.start(task3);
+ threadPool.start(&task1);
+ threadPool.start(&task2);
+ threadPool.start(&task3);
mainBarrier.acquire(1);
QCOMPARE(threadPool.activeThreadCount(), 1);
- QVERIFY(!threadPool.tryTake(task1));
- QVERIFY(threadPool.tryTake(task2));
- QVERIFY(threadPool.tryTake(task3));
+ QVERIFY(!threadPool.tryTake(&task1));
+ QVERIFY(threadPool.tryTake(&task2));
+ QVERIFY(threadPool.tryTake(&task3));
// A bad queue implementation can segfault here because two consecutive items in the queue
// have been taken
@@ -1267,13 +1373,9 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
taskBarrier.release(1);
- threadPool.waitForDone();
+ WAIT_FOR_DONE(threadPool);
QCOMPARE(threadPool.activeThreadCount(), 0);
-
- delete task1;
- delete task2;
- delete task3;
}
void tst_QThreadPool::waitForDoneAfterTake()
@@ -1286,7 +1388,7 @@ void tst_QThreadPool::waitForDoneAfterTake()
, m_threadBarrier(threadBarrier)
{}
- void run()
+ void run() override
{
m_mainBarrier->release();
m_threadBarrier->acquire();
@@ -1304,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
@@ -1319,9 +1421,9 @@ void tst_QThreadPool::waitForDoneAfterTake()
// This sets the queue elements to nullptr in QThreadPool and we want to test that
// the threads keep going through the queue after encountering a nullptr.
for (int i = 0; i < threadCount; i++) {
- QRunnable *runnable = createTask(emptyFunct);
- manager.start(runnable);
- QVERIFY(manager.tryTake(runnable));
+ QScopedPointer<QRunnable> runnable(createTask(emptyFunct));
+ manager.start(runnable.get());
+ QVERIFY(manager.tryTake(runnable.get()));
}
// Add another runnable that will not be removed
@@ -1335,12 +1437,55 @@ void tst_QThreadPool::waitForDoneAfterTake()
// Release runnables that are waiting and expect all runnables to complete
threadBarrier.release(threadCount);
+}
- // Using qFatal instead of QVERIFY to force exit if threads are still running after timeout.
- // Otherwise, QCoreApplication will still wait for the stale threads and never exit the test.
- if (!manager.waitForDone(5 * 60 * 1000))
- qFatal("waitForDone returned false. Aborting to stop background threads.");
+/*
+ Try trigger reuse of expired threads and check that all tasks execute.
+ This is a regression test for QTBUG-72872.
+*/
+void tst_QThreadPool::threadReuse()
+{
+ TestThreadPool manager;
+ manager.setExpiryTimeout(-1);
+ manager.setMaxThreadCount(1);
+
+ constexpr int repeatCount = 10000;
+ constexpr int timeoutMs = 1000;
+ QSemaphore sem;
+
+ for (int i = 0; i < repeatCount; i++) {
+ manager.start([&sem]() { sem.release(); });
+ manager.start([&sem]() { sem.release(); });
+ manager.releaseThread();
+ QVERIFY(sem.tryAcquire(2, timeoutMs));
+ manager.reserveThread();
+ }
+}
+
+void tst_QThreadPool::nullFunctions()
+{
+ const auto expectWarning = [] {
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg,
+ "Trying to create null QRunnable. This may stop working.");
+ };
+ // Note this is not necessarily testing intended behavior, only undocumented behavior.
+ // If this is changed it should be noted in Behavioral Changes.
+ FunctionPointer nullFunction = nullptr;
+ std::function<void()> nullStdFunction(nullptr);
+ {
+ TestThreadPool manager;
+ // should not crash:
+ expectWarning();
+ manager.start(nullFunction);
+ expectWarning();
+ manager.start(nullStdFunction);
+ // should fail (and not leak):
+ expectWarning();
+ QVERIFY(!manager.tryStart(nullStdFunction));
+ expectWarning();
+ QVERIFY(!manager.tryStart(nullFunction));
+ }
}
QTEST_MAIN(tst_QThreadPool);
diff --git a/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
new file mode 100644
index 0000000000..14d8d7dd40
--- /dev/null
+++ b/tests/auto/corelib/thread/qthreadstorage/CMakeLists.txt
@@ -0,0 +1,27 @@
+# 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
+)
+
+## Scopes:
+#####################################################################
+
+if(NOT ANDROID)
+ add_subdirectory(crashonexit)
+ if(QT_FEATURE_process)
+ add_dependencies(tst_qthreadstorage crashOnExit_helper)
+ endif()
+endif()
diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt b/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt
new file mode 100644
index 0000000000..4d62e61a36
--- /dev/null
+++ b/tests/auto/corelib/thread/qthreadstorage/crashonexit/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## crashonexit Binary:
+#####################################################################
+
+qt_internal_add_executable(crashOnExit_helper
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.."
+ INSTALL_DIRECTORY "${INSTALL_TESTSDIR}/tst_qthreadstorage/crashOnExit_helper"
+ SOURCES
+ crashOnExit.cpp
+)
diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp
index 5a42156b85..3b3a4d4813 100644
--- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp
+++ b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashOnExit.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QCoreApplication>
#include <QtCore/QThreadStorage>
diff --git a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro b/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro
deleted file mode 100644
index 57bd78bcee..0000000000
--- a/tests/auto/corelib/thread/qthreadstorage/crashonexit/crashonexit.pro
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCES += crashOnExit.cpp
-debug_and_release {
- CONFIG(debug, debug|release) {
- TARGET = ../../debug/crashOnExit_helper
- } else {
- TARGET = ../../release/crashOnExit_helper
- }
-} else {
- TARGET = ../crashOnExit_helper
-}
-QT = core
-CONFIG += cmdline
-
-# This app is testdata for tst_qthreadstorage
-target.path = $$[QT_INSTALL_TESTS]/tst_qthreadstorage/$$TARGET
-INSTALLS += target
diff --git a/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro b/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro
deleted file mode 100644
index 432c564ba1..0000000000
--- a/tests/auto/corelib/thread/qthreadstorage/qthreadstorage.pro
+++ /dev/null
@@ -1,8 +0,0 @@
-TEMPLATE = subdirs
-
-!android:!winrt {
- test.depends = crashonexit
- SUBDIRS += crashonexit
-}
-
-SUBDIRS += test
diff --git a/tests/auto/corelib/thread/qthreadstorage/test/test.pro b/tests/auto/corelib/thread/qthreadstorage/test/test.pro
deleted file mode 100644
index d2f21f48f0..0000000000
--- a/tests/auto/corelib/thread/qthreadstorage/test/test.pro
+++ /dev/null
@@ -1,16 +0,0 @@
-CONFIG += testcase
-debug_and_release {
- CONFIG(debug, debug|release) {
- TARGET = ../../debug/tst_qthreadstorage
- !android:!winrt: TEST_HELPER_INSTALLS = ../../debug/crashonexit_helper
- } else {
- TARGET = ../../release/tst_qthreadstorage
- !android:!winrt: TEST_HELPER_INSTALLS = ../../release/crashonexit_helper
- }
-} else {
- TARGET = ../tst_qthreadstorage
- !android:!winrt: TEST_HELPER_INSTALLS = ../crashonexit_helper
-}
-CONFIG += console
-QT = core testlib
-SOURCES = ../tst_qthreadstorage.cpp
diff --git a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
index ef5d3452d5..ca382cf60c 100644
--- a/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
+++ b/tests/auto/corelib/thread/qthreadstorage/tst_qthreadstorage.cpp
@@ -1,32 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#if QT_CONFIG(process)
+#include <QProcess>
+#endif
+#include <QTestEventLoop>
#include <qcoreapplication.h>
#include <qmutex.h>
@@ -76,7 +55,7 @@ void tst_QThreadStorage::hasLocalData()
QVERIFY(!pointers.hasLocalData());
pointers.setLocalData(new Pointer);
QVERIFY(pointers.hasLocalData());
- pointers.setLocalData(0);
+ pointers.setLocalData(nullptr);
QVERIFY(!pointers.hasLocalData());
}
@@ -88,8 +67,8 @@ void tst_QThreadStorage::localData()
pointers.setLocalData(p);
QVERIFY(pointers.hasLocalData());
QCOMPARE(pointers.localData(), p);
- pointers.setLocalData(0);
- QCOMPARE(pointers.localData(), (Pointer *)0);
+ pointers.setLocalData(nullptr);
+ QCOMPARE(pointers.localData(), nullptr);
QVERIFY(!pointers.hasLocalData());
}
@@ -102,8 +81,8 @@ void tst_QThreadStorage::localData_const()
pointers.setLocalData(p);
QVERIFY(pointers.hasLocalData());
QCOMPARE(const_pointers.localData(), p);
- pointers.setLocalData(0);
- QCOMPARE(const_pointers.localData(), (Pointer *)0);
+ pointers.setLocalData(nullptr);
+ QCOMPARE(const_pointers.localData(), nullptr);
QVERIFY(!pointers.hasLocalData());
}
@@ -113,7 +92,7 @@ void tst_QThreadStorage::setLocalData()
QVERIFY(!pointers.hasLocalData());
pointers.setLocalData(new Pointer);
QVERIFY(pointers.hasLocalData());
- pointers.setLocalData(0);
+ pointers.setLocalData(nullptr);
QVERIFY(!pointers.hasLocalData());
}
@@ -129,7 +108,7 @@ public:
: pointers(p)
{ }
- void run()
+ void run() override
{
pointers.setLocalData(new Pointer);
@@ -157,7 +136,7 @@ void tst_QThreadStorage::autoDelete()
QCOMPARE(Pointer::count, c);
}
-bool threadStorageOk;
+static bool threadStorageOk;
void testAdoptedThreadStorageWin(void *p)
{
QThreadStorage<Pointer *> *pointers = reinterpret_cast<QThreadStorage<Pointer *> *>(p);
@@ -180,17 +159,10 @@ void testAdoptedThreadStorageWin(void *p)
}
QObject::connect(QThread::currentThread(), SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
}
-#ifdef Q_OS_WINRT
-unsigned __stdcall testAdoptedThreadStorageWinRT(void *p)
-{
- testAdoptedThreadStorageWin(p);
- return 0;
-}
-#endif
void *testAdoptedThreadStorageUnix(void *pointers)
{
testAdoptedThreadStorageWin(pointers);
- return 0;
+ return nullptr;
}
void tst_QThreadStorage::adoptedThreads()
{
@@ -201,14 +173,9 @@ void tst_QThreadStorage::adoptedThreads()
{
#ifdef Q_OS_UNIX
pthread_t thread;
- const int state = pthread_create(&thread, 0, testAdoptedThreadStorageUnix, &pointers);
+ const int state = pthread_create(&thread, nullptr, testAdoptedThreadStorageUnix, &pointers);
QCOMPARE(state, 0);
- pthread_join(thread, 0);
-#elif defined Q_OS_WINRT
- HANDLE thread;
- thread = (HANDLE) _beginthreadex(NULL, 0, testAdoptedThreadStorageWinRT, &pointers, 0, 0);
- QVERIFY(thread);
- WaitForSingleObjectEx(thread, INFINITE, FALSE);
+ pthread_join(thread, nullptr);
#elif defined Q_OS_WIN
HANDLE thread;
thread = (HANDLE)_beginthread(testAdoptedThreadStorageWin, 0, &pointers);
@@ -224,7 +191,7 @@ void tst_QThreadStorage::adoptedThreads()
QTRY_COMPARE(Pointer::count, c);
}
-QBasicAtomicInt cleanupOrder = Q_BASIC_ATOMIC_INITIALIZER(0);
+static QBasicAtomicInt cleanupOrder = Q_BASIC_ATOMIC_INITIALIZER(0);
class First
{
@@ -261,7 +228,7 @@ void tst_QThreadStorage::ensureCleanupOrder()
: first(first), second(second)
{ }
- void run()
+ void run() override
{
// set in reverse order, but shouldn't matter, the data
// will be deleted in the order the thread storage objects
@@ -305,11 +272,14 @@ static inline bool runCrashOnExit(const QString &binary, QString *errorMessage)
void tst_QThreadStorage::crashOnExit()
{
+#ifdef Q_OS_ANDROID
+ QSKIP("Can't start QProcess to run a custom user binary on Android");
+#endif
#if !QT_CONFIG(process)
QSKIP("No qprocess support", SkipAll);
#else
QString errorMessage;
- QVERIFY2(runCrashOnExit("crashOnExit_helper", &errorMessage),
+ QVERIFY2(runCrashOnExit("./crashOnExit_helper", &errorMessage),
qPrintable(errorMessage));
#endif
}
@@ -352,14 +322,14 @@ void tst_QThreadStorage::leakInDestructor()
Thread(QThreadStorage<ThreadStorageLocalDataTester *> &t) : tls(t) { }
- void run()
+ void run() override
{
QVERIFY(!tls.hasLocalData());
tls.setLocalData(new ThreadStorageLocalDataTester);
QVERIFY(tls.hasLocalData());
}
};
- int c = SPointer::count.load();
+ int c = SPointer::count.loadRelaxed();
QThreadStorage<ThreadStorageLocalDataTester *> tls;
@@ -383,7 +353,7 @@ void tst_QThreadStorage::leakInDestructor()
QVERIFY(t3.wait());
//check all the constructed things have been destructed
- QCOMPARE(int(SPointer::count.load()), c);
+ QCOMPARE(int(SPointer::count.loadRelaxed()), c);
}
class ThreadStorageResetLocalDataTester {
@@ -404,14 +374,14 @@ void tst_QThreadStorage::resetInDestructor()
class Thread : public QThread
{
public:
- void run()
+ void run() override
{
QVERIFY(!ThreadStorageResetLocalDataTesterTls()->hasLocalData());
ThreadStorageResetLocalDataTesterTls()->setLocalData(new ThreadStorageResetLocalDataTester);
QVERIFY(ThreadStorageResetLocalDataTesterTls()->hasLocalData());
}
};
- int c = SPointer::count.load();
+ int c = SPointer::count.loadRelaxed();
Thread t1;
Thread t2;
@@ -424,7 +394,7 @@ void tst_QThreadStorage::resetInDestructor()
QVERIFY(t3.wait());
//check all the constructed things have been destructed
- QCOMPARE(int(SPointer::count.load()), c);
+ QCOMPARE(int(SPointer::count.loadRelaxed()), c);
}
@@ -440,7 +410,8 @@ void tst_QThreadStorage::valueBased()
Thread(QThreadStorage<SPointer> &t1, QThreadStorage<QString> &t2, QThreadStorage<int> &t3)
: tlsSPointer(t1), tlsString(t2), tlsInt(t3) { }
- void run() {
+ void run() override
+ {
/*QVERIFY(!tlsSPointer.hasLocalData());
QVERIFY(!tlsString.hasLocalData());
QVERIFY(!tlsInt.hasLocalData());*/
@@ -475,7 +446,7 @@ void tst_QThreadStorage::valueBased()
QThreadStorage<QString> tlsString;
QThreadStorage<int> tlsInt;
- int c = SPointer::count.load();
+ int c = SPointer::count.loadRelaxed();
Thread t1(tlsSPointer, tlsString, tlsInt);
Thread t2(tlsSPointer, tlsString, tlsInt);
@@ -495,7 +466,7 @@ void tst_QThreadStorage::valueBased()
QVERIFY(t2.wait());
QVERIFY(t3.wait());
- QCOMPARE(c, int(SPointer::count.load()));
+ QCOMPARE(c, int(SPointer::count.loadRelaxed()));
}
diff --git a/tests/auto/corelib/thread/qwaitcondition/BLACKLIST b/tests/auto/corelib/thread/qwaitcondition/BLACKLIST
deleted file mode 100644
index 3ff336576b..0000000000
--- a/tests/auto/corelib/thread/qwaitcondition/BLACKLIST
+++ /dev/null
@@ -1,2 +0,0 @@
-[wakeOne]
-windows
diff --git a/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt b/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
new file mode 100644
index 0000000000..0a2830622e
--- /dev/null
+++ b/tests/auto/corelib/thread/qwaitcondition/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qwaitcondition.pro b/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro
deleted file mode 100644
index 2098d9dd2f..0000000000
--- a/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qwaitcondition
-QT = core testlib
-SOURCES = tst_qwaitcondition.cpp
diff --git a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
index 126cb6b180..4e3413afe8 100644
--- a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
+++ b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
@@ -1,32 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
+#include <QReadWriteLock>
#include <qatomic.h>
#include <qcoreapplication.h>
@@ -77,7 +53,7 @@ public:
inline wait_QMutex_Thread_1()
{ }
- void run()
+ void run() override
{
mutex.lock();
cond.wakeOne();
@@ -95,10 +71,10 @@ public:
QWaitCondition *cond;
inline wait_QMutex_Thread_2()
- : mutex(0), cond(0)
+ : mutex(nullptr), cond(nullptr)
{ }
- void run()
+ void run() override
{
mutex->lock();
started.wakeOne();
@@ -116,7 +92,7 @@ public:
inline wait_QReadWriteLock_Thread_1()
{ }
- void run()
+ void run() override
{
readWriteLock.lockForWrite();
cond.wakeOne();
@@ -134,10 +110,10 @@ public:
QWaitCondition *cond;
inline wait_QReadWriteLock_Thread_2()
- : readWriteLock(0), cond(0)
+ : readWriteLock(nullptr), cond(nullptr)
{ }
- void run()
+ void run() override
{
readWriteLock->lockForRead();
started.wakeOne();
@@ -392,13 +368,13 @@ public:
QWaitCondition *cond;
inline wake_Thread()
- : mutex(0), cond(0)
+ : mutex(nullptr), cond(nullptr)
{ }
static inline void sleep(ulong s)
- { QThread::sleep(s); }
+ { QThread::sleep(std::chrono::seconds{s}); }
- void run()
+ void run() override
{
Q_ASSERT(count);
Q_ASSERT(mutex);
@@ -424,13 +400,13 @@ public:
QWaitCondition *cond;
inline wake_Thread_2()
- : readWriteLock(0), cond(0)
+ : readWriteLock(nullptr), cond(nullptr)
{ }
static inline void sleep(ulong s)
- { QThread::sleep(s); }
+ { QThread::sleep(std::chrono::seconds{s}); }
- void run()
+ void run() override
{
Q_ASSERT(count);
Q_ASSERT(readWriteLock);
@@ -481,7 +457,7 @@ void tst_QWaitCondition::wakeOne()
}
mutex.unlock();
- QCOMPARE(count.load(), ThreadCount);
+ QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up threads one at a time
for (x = 0; x < ThreadCount; ++x) {
@@ -502,10 +478,10 @@ void tst_QWaitCondition::wakeOne()
}
QCOMPARE(exited, 1);
- QCOMPARE(count.load(), ThreadCount - (x + 1));
+ QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 1));
}
- QCOMPARE(count.load(), 0);
+ QCOMPARE(count.loadRelaxed(), 0);
// QReadWriteLock
QReadWriteLock readWriteLock;
@@ -530,7 +506,7 @@ void tst_QWaitCondition::wakeOne()
}
readWriteLock.unlock();
- QCOMPARE(count.load(), ThreadCount);
+ QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up threads one at a time
for (x = 0; x < ThreadCount; ++x) {
@@ -551,10 +527,10 @@ void tst_QWaitCondition::wakeOne()
}
QCOMPARE(exited, 1);
- QCOMPARE(count.load(), ThreadCount - (x + 1));
+ QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 1));
}
- QCOMPARE(count.load(), 0);
+ QCOMPARE(count.loadRelaxed(), 0);
}
// wake up threads, two at a time
@@ -585,7 +561,7 @@ void tst_QWaitCondition::wakeOne()
}
mutex.unlock();
- QCOMPARE(count.load(), ThreadCount);
+ QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up threads one at a time
for (x = 0; x < ThreadCount; x += 2) {
@@ -608,10 +584,10 @@ void tst_QWaitCondition::wakeOne()
}
QCOMPARE(exited, 2);
- QCOMPARE(count.load(), ThreadCount - (x + 2));
+ QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 2));
}
- QCOMPARE(count.load(), 0);
+ QCOMPARE(count.loadRelaxed(), 0);
// QReadWriteLock
QReadWriteLock readWriteLock;
@@ -636,7 +612,7 @@ void tst_QWaitCondition::wakeOne()
}
readWriteLock.unlock();
- QCOMPARE(count.load(), ThreadCount);
+ QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up threads one at a time
for (x = 0; x < ThreadCount; x += 2) {
@@ -659,10 +635,10 @@ void tst_QWaitCondition::wakeOne()
}
QCOMPARE(exited, 2);
- QCOMPARE(count.load(), ThreadCount - (x + 2));
+ QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 2));
}
- QCOMPARE(count.load(), 0);
+ QCOMPARE(count.loadRelaxed(), 0);
}
}
@@ -692,7 +668,7 @@ void tst_QWaitCondition::wakeAll()
}
mutex.unlock();
- QCOMPARE(count.load(), ThreadCount);
+ QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up all threads at once
mutex.lock();
@@ -707,7 +683,7 @@ void tst_QWaitCondition::wakeAll()
}
QCOMPARE(exited, ThreadCount);
- QCOMPARE(count.load(), 0);
+ QCOMPARE(count.loadRelaxed(), 0);
// QReadWriteLock
QReadWriteLock readWriteLock;
@@ -728,7 +704,7 @@ void tst_QWaitCondition::wakeAll()
}
readWriteLock.unlock();
- QCOMPARE(count.load(), ThreadCount);
+ QCOMPARE(count.loadRelaxed(), ThreadCount);
// wake up all threads at once
readWriteLock.lockForWrite();
@@ -743,7 +719,7 @@ void tst_QWaitCondition::wakeAll()
}
QCOMPARE(exited, ThreadCount);
- QCOMPARE(count.load(), 0);
+ QCOMPARE(count.loadRelaxed(), 0);
}
}
@@ -764,7 +740,8 @@ public:
QWaitCondition *startup;
QWaitCondition *waitCondition;
- void run() {
+ void run() override
+ {
mutex->lock();
ready = true;
@@ -796,7 +773,8 @@ public:
QWaitCondition *startup;
QWaitCondition *waitCondition;
- void run() {
+ void run() override
+ {
readWriteLock->lockForWrite();
ready = true;
diff --git a/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt b/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
new file mode 100644
index 0000000000..5345522ea5
--- /dev/null
+++ b/tests/auto/corelib/thread/qwritelocker/CMakeLists.txt
@@ -0,0 +1,17 @@
+# 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/qwritelocker.pro b/tests/auto/corelib/thread/qwritelocker/qwritelocker.pro
deleted file mode 100644
index a6c4f70b8d..0000000000
--- a/tests/auto/corelib/thread/qwritelocker/qwritelocker.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase
-TARGET = tst_qwritelocker
-QT = core testlib
-SOURCES = tst_qwritelocker.cpp
diff --git a/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
index 876c18c721..b4e6b45dbd 100644
--- a/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
+++ b/tests/auto/corelib/thread/qwritelocker/tst_qwritelocker.cpp
@@ -1,32 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QTest>
#include <QCoreApplication>
#include <QWriteLocker>
@@ -73,7 +48,7 @@ void tst_QWriteLocker::scopeTest()
class ScopeTestThread : public tst_QWriteLockerThread
{
public:
- void run()
+ void run() override
{
waitForTest();
@@ -109,7 +84,7 @@ void tst_QWriteLocker::scopeTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
@@ -118,7 +93,7 @@ void tst_QWriteLocker::unlockAndRelockTest()
class UnlockAndRelockThread : public tst_QWriteLockerThread
{
public:
- void run()
+ void run() override
{
QWriteLocker locker(&lock);
@@ -156,7 +131,7 @@ void tst_QWriteLocker::unlockAndRelockTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
void tst_QWriteLocker::lockerStateTest()
@@ -164,7 +139,7 @@ void tst_QWriteLocker::lockerStateTest()
class LockerStateThread : public tst_QWriteLockerThread
{
public:
- void run()
+ void run() override
{
{
QWriteLocker locker(&lock);
@@ -196,7 +171,7 @@ void tst_QWriteLocker::lockerStateTest()
QVERIFY(thread->wait());
delete thread;
- thread = 0;
+ thread = nullptr;
}
QTEST_MAIN(tst_QWriteLocker)
diff --git a/tests/auto/corelib/thread/thread.pro b/tests/auto/corelib/thread/thread.pro
deleted file mode 100644
index 90b8d6806e..0000000000
--- a/tests/auto/corelib/thread/thread.pro
+++ /dev/null
@@ -1,27 +0,0 @@
-TEMPLATE=subdirs
-
-qtConfig(thread) {
- SUBDIRS=\
- qatomicint \
- qatomicinteger \
- qatomicpointer \
- qresultstore \
- qfuture \
- qfuturesynchronizer \
- qmutex \
- qmutexlocker \
- qreadlocker \
- qreadwritelock \
- qsemaphore \
- qthread \
- qthreadonce \
- qthreadpool \
- qthreadstorage \
- qwaitcondition \
- qwritelocker
-}
-
-qtHaveModule(concurrent) {
- SUBDIRS += \
- qfuturewatcher
-}