diff options
Diffstat (limited to 'tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp')
-rw-r--r-- | tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp | 954 |
1 files changed, 914 insertions, 40 deletions
diff --git a/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp b/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp index 8f50329ec5..0bc2961903 100644 --- a/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp +++ b/tests/auto/concurrent/qtconcurrentrun/tst_qtconcurrentrun.cpp @@ -1,34 +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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtconcurrentrun.h> -#include <qfuture.h> +#include <QFuture> +#include <QMutex> +#include <QMutexLocker> #include <QString> -#include <QtTest/QtTest> +#include <QWaitCondition> +#include <QTest> +#include <QTimer> +#include <QFutureSynchronizer> + +#include <QtTest/private/qemulationdetector_p.h> using namespace QtConcurrent; @@ -36,9 +18,11 @@ class tst_QtConcurrentRun: public QObject { Q_OBJECT private slots: + void initTestCase(); void runLightFunction(); void runHeavyFunction(); void returnValue(); + void reportValueWithPromise(); void functionObject(); void memberFunctions(); void implicitConvertibleTypes(); @@ -47,10 +31,20 @@ private slots: void recursive(); #ifndef QT_NO_EXCEPTIONS void exceptions(); + void unhandledException(); #endif void functor(); void lambda(); void callableObjectWithState(); + void withPromise(); + void withPromiseInThreadPool(); + void withPromiseAndThen(); + void moveOnlyType(); + void crefFunction(); + void customPromise(); + void nonDefaultConstructibleValue(); + void nullThreadPool(); + void nullThreadPoolNoLeak(); }; void light() @@ -59,6 +53,30 @@ void light() qDebug("done function"); } +void lightOverloaded() +{ + qDebug("in function"); + qDebug("done function"); +} + +void lightOverloaded(int) +{ + qDebug("in function with arg"); + qDebug("done function"); +} + +void lightOverloaded(QPromise<int> &) +{ + qDebug("in function with promise"); + qDebug("done function"); +} + +void lightOverloaded(QPromise<double> &, int) +{ + qDebug("in function with promise and with arg"); + qDebug("done function"); +} + void heavy() { qDebug("in function"); @@ -68,6 +86,12 @@ void heavy() qDebug("done function"); } +void tst_QtConcurrentRun::initTestCase() +{ + // proxy check for QEMU; catches slightly more though + if (QTestPrivate::isRunningArmOnX86()) + QSKIP("Runs into spurious crashes on QEMU -- QTBUG-106906"); +} void tst_QtConcurrentRun::runLightFunction() { @@ -76,6 +100,34 @@ void tst_QtConcurrentRun::runLightFunction() qDebug("waiting"); future.waitForFinished(); qDebug("done"); + + void (*f1)() = lightOverloaded; + qDebug("starting function"); + QFuture<void> future1 = run(f1); + qDebug("waiting"); + future1.waitForFinished(); + qDebug("done"); + + void (*f2)(int) = lightOverloaded; + qDebug("starting function with arg"); + QFuture<void> future2 = run(f2, 2); + qDebug("waiting"); + future2.waitForFinished(); + qDebug("done"); + + void (*f3)(QPromise<int> &) = lightOverloaded; + qDebug("starting function with promise"); + QFuture<int> future3 = run(f3); + qDebug("waiting"); + future3.waitForFinished(); + qDebug("done"); + + void (*f4)(QPromise<double> &, int v) = lightOverloaded; + qDebug("starting function with promise and with arg"); + QFuture<double> future4 = run(f4, 2); + qDebug("waiting"); + future4.waitForFinished(); + qDebug("done"); } void tst_QtConcurrentRun::runHeavyFunction() @@ -147,6 +199,11 @@ void tst_QtConcurrentRun::returnValue() QCOMPARE(f.result(), 10); f = run(&pool, returnInt0); QCOMPARE(f.result(), 10); + f = run(returnInt1, 4); + QCOMPARE(f.result(), 4); + f = run(&pool, returnInt1, 4); + QCOMPARE(f.result(), 4); + A a; f = run(&A::member0, &a); @@ -189,6 +246,7 @@ void tst_QtConcurrentRun::returnValue() f = run(&pool, std::ref(a), 20); QCOMPARE(f.result(), 20); + const AConst aConst = AConst(); f = run(&AConst::member0, &aConst); QCOMPARE(f.result(), 10); @@ -230,6 +288,7 @@ void tst_QtConcurrentRun::returnValue() f = run(&pool, std::ref(aConst), 20); QCOMPARE(f.result(), 20); + ANoExcept aNoExcept; f = run(&ANoExcept::member0, &aNoExcept); QCOMPARE(f.result(), 10); @@ -271,6 +330,7 @@ void tst_QtConcurrentRun::returnValue() f = run(&pool, std::ref(aNoExcept), 20); QCOMPARE(f.result(), 20); + const AConstNoExcept aConstNoExcept = AConstNoExcept(); f = run(&AConstNoExcept::member0, &aConstNoExcept); QCOMPARE(f.result(), 10); @@ -313,6 +373,195 @@ void tst_QtConcurrentRun::returnValue() QCOMPARE(f.result(), 20); } +void reportInt0(QPromise<int> &promise) +{ + promise.addResult(0); +} + +void reportIntPlusOne(QPromise<int> &promise, int i) +{ + promise.addResult(i + 1); +} + +class AWithPromise +{ +public: + void member0(QPromise<int> &promise) { promise.addResult(10); } + void member1(QPromise<int> &promise, int in) { promise.addResult(in); } + + void operator()(QPromise<int> &promise) { promise.addResult(10); } +}; + +class AConstWithPromise +{ +public: + void member0(QPromise<int> &promise) const { promise.addResult(10); } + void member1(QPromise<int> &promise, int in) const { promise.addResult(in); } + + void operator()(QPromise<int> &promise) const { promise.addResult(10); } +}; + +class ANoExceptWithPromise +{ +public: + void member0(QPromise<int> &promise) noexcept { promise.addResult(10); } + void member1(QPromise<int> &promise, int in) noexcept { promise.addResult(in); } + + void operator()(QPromise<int> &promise) noexcept { promise.addResult(10); } +}; + +class AConstNoExceptWithPromise +{ +public: + void member0(QPromise<int> &promise) const noexcept { promise.addResult(10); } + void member1(QPromise<int> &promise, int in) const noexcept { promise.addResult(in); } + + void operator()(QPromise<int> &promise) const noexcept { promise.addResult(10); } +}; + +void tst_QtConcurrentRun::reportValueWithPromise() +{ + QThreadPool pool; + QFuture<int> f; + + f = run(reportInt0); + QCOMPARE(f.result(), 0); + f = run(&pool, reportInt0); + QCOMPARE(f.result(), 0); + f = run(reportIntPlusOne, 5); + QCOMPARE(f.result(), 6); + f = run(&pool, reportIntPlusOne, 5); + QCOMPARE(f.result(), 6); + + + AWithPromise a; + f = run(&AWithPromise::member0, &a); + QCOMPARE(f.result(), 10); + f = run(&pool, &AWithPromise::member0, &a); + QCOMPARE(f.result(), 10); + + f = run(&AWithPromise::member1, &a, 20); + QCOMPARE(f.result(), 20); + f = run(&pool, &AWithPromise::member1, &a, 20); + QCOMPARE(f.result(), 20); + + f = run(&AWithPromise::member0, a); + QCOMPARE(f.result(), 10); + f = run(&pool, &AWithPromise::member0, a); + QCOMPARE(f.result(), 10); + + f = run(&AWithPromise::member1, a, 20); + QCOMPARE(f.result(), 20); + f = run(&pool, &AWithPromise::member1, a, 20); + QCOMPARE(f.result(), 20); + + f = run(a); + QCOMPARE(f.result(), 10); + f = run(&pool, a); + QCOMPARE(f.result(), 10); + + f = run(std::ref(a)); + QCOMPARE(f.result(), 10); + f = run(&pool, std::ref(a)); + QCOMPARE(f.result(), 10); + + + const AConstWithPromise aConst = AConstWithPromise(); + f = run(&AConstWithPromise::member0, &aConst); + QCOMPARE(f.result(), 10); + f = run(&pool, &AConstWithPromise::member0, &aConst); + QCOMPARE(f.result(), 10); + + f = run(&AConstWithPromise::member1, &aConst, 20); + QCOMPARE(f.result(), 20); + f = run(&pool, &AConstWithPromise::member1, &aConst, 20); + QCOMPARE(f.result(), 20); + + f = run(&AConstWithPromise::member0, aConst); + QCOMPARE(f.result(), 10); + f = run(&pool, &AConstWithPromise::member0, aConst); + QCOMPARE(f.result(), 10); + + f = run(&AConstWithPromise::member1, aConst, 20); + QCOMPARE(f.result(), 20); + f = run(&pool, &AConstWithPromise::member1, aConst, 20); + QCOMPARE(f.result(), 20); + + f = run(aConst); + QCOMPARE(f.result(), 10); + f = run(&pool, aConst); + QCOMPARE(f.result(), 10); + + f = run(std::ref(a)); + QCOMPARE(f.result(), 10); + f = run(&pool, std::ref(a)); + QCOMPARE(f.result(), 10); + + + ANoExceptWithPromise aNoExcept; + f = run(&ANoExceptWithPromise::member0, &aNoExcept); + QCOMPARE(f.result(), 10); + f = run(&pool, &ANoExceptWithPromise::member0, &aNoExcept); + QCOMPARE(f.result(), 10); + + f = run(&ANoExceptWithPromise::member1, &aNoExcept, 20); + QCOMPARE(f.result(), 20); + f = run(&pool, &ANoExceptWithPromise::member1, &aNoExcept, 20); + QCOMPARE(f.result(), 20); + + f = run(&ANoExceptWithPromise::member0, aNoExcept); + QCOMPARE(f.result(), 10); + f = run(&pool, &ANoExceptWithPromise::member0, aNoExcept); + QCOMPARE(f.result(), 10); + + f = run(&ANoExceptWithPromise::member1, aNoExcept, 20); + QCOMPARE(f.result(), 20); + f = run(&pool, &ANoExceptWithPromise::member1, aNoExcept, 20); + QCOMPARE(f.result(), 20); + + f = run(aNoExcept); + QCOMPARE(f.result(), 10); + f = run(&pool, aNoExcept); + QCOMPARE(f.result(), 10); + + f = run(std::ref(aNoExcept)); + QCOMPARE(f.result(), 10); + f = run(&pool, std::ref(aNoExcept)); + QCOMPARE(f.result(), 10); + + + const AConstNoExceptWithPromise aConstNoExcept = AConstNoExceptWithPromise(); + f = run(&AConstNoExceptWithPromise::member0, &aConstNoExcept); + QCOMPARE(f.result(), 10); + f = run(&pool, &AConstNoExceptWithPromise::member0, &aConstNoExcept); + QCOMPARE(f.result(), 10); + + f = run(&AConstNoExceptWithPromise::member1, &aConstNoExcept, 20); + QCOMPARE(f.result(), 20); + f = run(&pool, &AConstNoExceptWithPromise::member1, &aConstNoExcept, 20); + QCOMPARE(f.result(), 20); + + f = run(&AConstNoExceptWithPromise::member0, aConstNoExcept); + QCOMPARE(f.result(), 10); + f = run(&pool, &AConstNoExceptWithPromise::member0, aConstNoExcept); + QCOMPARE(f.result(), 10); + + f = run(&AConstNoExceptWithPromise::member1, aConstNoExcept, 20); + QCOMPARE(f.result(), 20); + f = run(&pool, &AConstNoExceptWithPromise::member1, aConstNoExcept, 20); + QCOMPARE(f.result(), 20); + + f = run(aConstNoExcept); + QCOMPARE(f.result(), 10); + f = run(&pool, aConstNoExcept); + QCOMPARE(f.result(), 10); + + f = run(std::ref(aConstNoExcept)); + QCOMPARE(f.result(), 10); + f = run(&pool, std::ref(aConstNoExcept)); + QCOMPARE(f.result(), 10); +} + struct TestClass { void foo() { } @@ -417,10 +666,10 @@ void tst_QtConcurrentRun::implicitConvertibleTypes() { QThreadPool pool; - double d; + double d = 0.0; run(doubleFunction, d).waitForFinished(); run(&pool, doubleFunction, d).waitForFinished(); - int i; + int i = 0; run(doubleFunction, d).waitForFinished(); run(&pool, doubleFunction, d).waitForFinished(); run(doubleFunction, i).waitForFinished(); @@ -566,7 +815,7 @@ public: void run() override { int iter = 60; while (--iter && !cancel.loadRelaxed()) - QThread::currentThread()->msleep(25); + QThread::currentThread()->sleep(std::chrono::milliseconds{25}); } }; @@ -637,6 +886,25 @@ void tst_QtConcurrentRun::exceptions() QVERIFY2(caught, "did not get exception"); } + +void tst_QtConcurrentRun::unhandledException() +{ + struct Exception {}; + bool caught = false; + try { + auto f = QtConcurrent::run([] { throw Exception {}; }); + f.waitForFinished(); + } catch (const QUnhandledException &e) { + try { + if (e.exception()) + std::rethrow_exception(e.exception()); + } catch (const Exception &) { + caught = true; + } + } + + QVERIFY(caught); +} #endif // Compiler supports decltype @@ -651,6 +919,17 @@ struct Functor { void operator()(int, int, int, int, int, int) { } }; +struct FunctorWithPromise { + void operator()(QPromise<int> &, double) { } +}; + +struct OverloadedFunctorWithPromise { + void operator()(QPromise<int> &) { } + void operator()(QPromise<double> &) { } + void operator()(QPromise<int> &, int) { } + void operator()(QPromise<double> &, int) { } +}; + // This tests functor without result_type; decltype need to be supported by the compiler. void tst_QtConcurrentRun::functor() { @@ -674,6 +953,17 @@ void tst_QtConcurrentRun::functor() QtConcurrent::run(f, 1,2,3,4).waitForFinished(); QtConcurrent::run(f, 1,2,3,4,5).waitForFinished(); } + FunctorWithPromise fWithPromise; + { + QtConcurrent::run(fWithPromise, 1.5).waitForFinished(); + } + OverloadedFunctorWithPromise ofWithPromise; + { + QtConcurrent::run<int>(ofWithPromise).waitForFinished(); + QtConcurrent::run<double>(ofWithPromise).waitForFinished(); + QtConcurrent::run<int>(ofWithPromise, 1).waitForFinished(); + QtConcurrent::run<double>(ofWithPromise, 1).waitForFinished(); + } // and now with explicit pool: QThreadPool pool; { @@ -700,10 +990,17 @@ void tst_QtConcurrentRun::functor() // Compiler supports lambda void tst_QtConcurrentRun::lambda() { - QCOMPARE(QtConcurrent::run([](){ return 45; }).result(), 45); - QCOMPARE(QtConcurrent::run([](int a){ return a+15; }, 12).result(), 12+15); - QCOMPARE(QtConcurrent::run([](int a, double b){ return a + b; }, 12, 15).result(), double(12+15)); - QCOMPARE(QtConcurrent::run([](int a , int, int, int, int b){ return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); + QCOMPARE(QtConcurrent::run([]() { return 45; }).result(), 45); + QCOMPARE(QtConcurrent::run([](int a) { return a+15; }, 12).result(), 12+15); + QCOMPARE(QtConcurrent::run([](int a, double b) { return a + b; }, 12, 15).result(), + double(12+15)); + QCOMPARE(QtConcurrent::run([](int a , int, int, int, int b) + { return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); + + QCOMPARE(QtConcurrent::run([](QPromise<int> &promise) + { promise.addResult(45); }).result(), 45); + QCOMPARE(QtConcurrent::run([](QPromise<int> &promise, double input) + { promise.addResult(input / 2.0); }, 15.0).result(), 7); { QString str { "Hello World Foo" }; @@ -714,10 +1011,12 @@ void tst_QtConcurrentRun::lambda() // and now with explicit pool: QThreadPool pool; - QCOMPARE(QtConcurrent::run(&pool, [](){ return 45; }).result(), 45); - QCOMPARE(QtConcurrent::run(&pool, [](int a){ return a+15; }, 12).result(), 12+15); - QCOMPARE(QtConcurrent::run(&pool, [](int a, double b){ return a + b; }, 12, 15).result(), double(12+15)); - QCOMPARE(QtConcurrent::run(&pool, [](int a , int, int, int, int b){ return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); + QCOMPARE(QtConcurrent::run(&pool, []() { return 45; }).result(), 45); + QCOMPARE(QtConcurrent::run(&pool, [](int a) { return a + 15; }, 12).result(), 12 + 15); + QCOMPARE(QtConcurrent::run(&pool, [](int a, double b) + { return a + b; }, 12, 15).result(), double(12 + 15)); + QCOMPARE(QtConcurrent::run(&pool, [](int a , int, int, int, int b) + { return a + b; }, 1, 2, 3, 4, 5).result(), 1 + 5); { QString str { "Hello World Foo" }; @@ -736,6 +1035,15 @@ struct CallableWithState int state = defaultState(); }; +struct CallableWithStateWithPromise +{ + void setNewState(QPromise<int> &, int newState) { state = newState; } + void operator()(QPromise<int> &promise, int newState) { state = newState; promise.addResult(newState); } + + static constexpr int defaultState() { return 42; } + int state = defaultState(); +}; + void tst_QtConcurrentRun::callableObjectWithState() { CallableWithState o; @@ -754,6 +1062,572 @@ void tst_QtConcurrentRun::callableObjectWithState() // Explicitly run on a temporary object QCOMPARE(run(CallableWithState(), 15).result(), 15); + + CallableWithStateWithPromise oWithPromise; + + // Run method setNewState explicitly + run(&CallableWithStateWithPromise::setNewState, &oWithPromise, + CallableWithStateWithPromise::defaultState() + 1).waitForFinished(); + QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 1); + + // Run operator()(int) explicitly + run(std::ref(oWithPromise), CallableWithStateWithPromise::defaultState() + 2).waitForFinished(); + QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 2); + + // Run on a copy of object (original object remains unchanged) + run(oWithPromise, CallableWithStateWithPromise::defaultState() + 3).waitForFinished(); + QCOMPARE(oWithPromise.state, CallableWithStateWithPromise::defaultState() + 2); + + // Explicitly run on a temporary object + QCOMPARE(run(CallableWithStateWithPromise(), 15).result(), 15); +} + +void report3(QPromise<int> &promise) +{ + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); +} + +static void staticReport3(QPromise<int> &promise) +{ + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); +} + +void reportN(QPromise<double> &promise, int n) +{ + for (int i = 0; i < n; ++i) + promise.addResult(0); +} + +static void staticReportN(QPromise<double> &promise, int n) +{ + for (int i = 0; i < n; ++i) + promise.addResult(0); +} + +void reportString1(QPromise<QString> &promise, const QString &s) +{ + promise.addResult(s); +} + +void reportString2(QPromise<QString> &promise, QString s) +{ + promise.addResult(s); +} + +class Callable { +public: + void operator()(QPromise<double> &promise, int n) const + { + for (int i = 0; i < n; ++i) + promise.addResult(0); + } +}; + +class MyObject { +public: + static void staticMember0(QPromise<double> &promise) + { + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); + } + + static void staticMember1(QPromise<double> &promise, int n) + { + for (int i = 0; i < n; ++i) + promise.addResult(0); + } + + void member0(QPromise<double> &promise) const + { + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); + } + + void member1(QPromise<double> &promise, int n) const + { + for (int i = 0; i < n; ++i) + promise.addResult(0); + } + + void memberString1(QPromise<QString> &promise, const QString &s) const + { + promise.addResult(s); + } + + void memberString2(QPromise<QString> &promise, QString s) const + { + promise.addResult(s); + } + + void nonConstMember(QPromise<double> &promise) + { + promise.addResult(0); + promise.addResult(2); + promise.addResult(1); + } +}; + +void tst_QtConcurrentRun::withPromise() +{ + // free function pointer + QCOMPARE(run(&report3).results(), + QList<int>({0, 2, 1})); + QCOMPARE(run(report3).results(), + QList<int>({0, 2, 1})); + + QCOMPARE(run(&staticReport3).results(), + QList<int>({0, 2, 1})); + QCOMPARE(run(staticReport3).results(), + QList<int>({0, 2, 1})); + + QCOMPARE(run(&reportN, 2).results(), + QList<double>({0, 0})); + QCOMPARE(run(reportN, 2).results(), + QList<double>({0, 0})); + + QCOMPARE(run(&staticReportN, 2).results(), + QList<double>({0, 0})); + QCOMPARE(run(staticReportN, 2).results(), + QList<double>({0, 0})); + + QString s = QLatin1String("string"); + const QString &crs = QLatin1String("cr string"); + const QString cs = QLatin1String("c string"); + + QCOMPARE(run(reportString1, s).results(), + QList<QString>({s})); + QCOMPARE(run(reportString1, crs).results(), + QList<QString>({crs})); + QCOMPARE(run(reportString1, cs).results(), + QList<QString>({cs})); + QCOMPARE(run(reportString1, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + + QCOMPARE(run(reportString2, s).results(), + QList<QString>({s})); + QCOMPARE(run(reportString2, crs).results(), + QList<QString>({crs})); + QCOMPARE(run(reportString2, cs).results(), + QList<QString>({cs})); + QCOMPARE(run(reportString2, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + + // lambda + QCOMPARE(run([](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }, 3).results(), + QList<double>({0, 0, 0})); + + // std::function + const std::function<void(QPromise<double> &, int)> fun = [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(run(fun, 2).results(), + QList<double>({0, 0})); + + // operator() + QCOMPARE(run(Callable(), 3).results(), + QList<double>({0, 0, 0})); + const Callable c{}; + QCOMPARE(run(c, 2).results(), + QList<double>({0, 0})); + + // static member functions + QCOMPARE(run(&MyObject::staticMember0).results(), + QList<double>({0, 2, 1})); + QCOMPARE(run(&MyObject::staticMember1, 2).results(), + QList<double>({0, 0})); + + // member functions + const MyObject obj{}; + QCOMPARE(run(&MyObject::member0, &obj).results(), + QList<double>({0, 2, 1})); + QCOMPARE(run(&MyObject::member1, &obj, 4).results(), + QList<double>({0, 0, 0, 0})); + QCOMPARE(run(&MyObject::memberString1, &obj, s).results(), + QList<QString>({s})); + QCOMPARE(run(&MyObject::memberString1, &obj, crs).results(), + QList<QString>({crs})); + QCOMPARE(run(&MyObject::memberString1, &obj, cs).results(), + QList<QString>({cs})); + QCOMPARE(run(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + QCOMPARE(run(&MyObject::memberString2, &obj, s).results(), + QList<QString>({s})); + QCOMPARE(run(&MyObject::memberString2, &obj, crs).results(), + QList<QString>({crs})); + QCOMPARE(run(&MyObject::memberString2, &obj, cs).results(), + QList<QString>({cs})); + QCOMPARE(run(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + MyObject nonConstObj{}; + QCOMPARE(run(&MyObject::nonConstMember, &nonConstObj).results(), + QList<double>({0, 2, 1})); +} + +void tst_QtConcurrentRun::withPromiseInThreadPool() +{ + QScopedPointer<QThreadPool> pool(new QThreadPool); + // free function pointer + QCOMPARE(run(pool.data(), &report3).results(), + QList<int>({0, 2, 1})); + QCOMPARE(run(pool.data(), report3).results(), + QList<int>({0, 2, 1})); + + QCOMPARE(run(pool.data(), &staticReport3).results(), + QList<int>({0, 2, 1})); + QCOMPARE(run(pool.data(), staticReport3).results(), + QList<int>({0, 2, 1})); + + QCOMPARE(run(pool.data(), &reportN, 2).results(), + QList<double>({0, 0})); + QCOMPARE(run(pool.data(), reportN, 2).results(), + QList<double>({0, 0})); + + QCOMPARE(run(pool.data(), &staticReportN, 2).results(), + QList<double>({0, 0})); + QCOMPARE(run(pool.data(), staticReportN, 2).results(), + QList<double>({0, 0})); + + QString s = QLatin1String("string"); + const QString &crs = QLatin1String("cr string"); + const QString cs = QLatin1String("c string"); + + QCOMPARE(run(pool.data(), reportString1, s).results(), + QList<QString>({s})); + QCOMPARE(run(pool.data(), reportString1, crs).results(), + QList<QString>({crs})); + QCOMPARE(run(pool.data(), reportString1, cs).results(), + QList<QString>({cs})); + QCOMPARE(run(pool.data(), reportString1, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + + QCOMPARE(run(pool.data(), reportString2, s).results(), + QList<QString>({s})); + QCOMPARE(run(pool.data(), reportString2, crs).results(), + QList<QString>({crs})); + QCOMPARE(run(pool.data(), reportString2, cs).results(), + QList<QString>({cs})); + QCOMPARE(run(pool.data(), reportString2, QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + + // lambda + QCOMPARE(run(pool.data(), [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }, 3).results(), + QList<double>({0, 0, 0})); + + // std::function + const std::function<void(QPromise<double> &, int)> fun = [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(run(pool.data(), fun, 2).results(), + QList<double>({0, 0})); + + // operator() + QCOMPARE(run(pool.data(), Callable(), 3).results(), + QList<double>({0, 0, 0})); + const Callable c{}; + QCOMPARE(run(pool.data(), c, 2).results(), + QList<double>({0, 0})); + + // static member functions + QCOMPARE(run(pool.data(), &MyObject::staticMember0).results(), + QList<double>({0, 2, 1})); + QCOMPARE(run(pool.data(), &MyObject::staticMember1, 2).results(), + QList<double>({0, 0})); + + // member functions + const MyObject obj{}; + QCOMPARE(run(pool.data(), &MyObject::member0, &obj).results(), + QList<double>({0, 2, 1})); + QCOMPARE(run(pool.data(), &MyObject::member1, &obj, 4).results(), + QList<double>({0, 0, 0, 0})); + QCOMPARE(run(pool.data(), &MyObject::memberString1, &obj, s).results(), + QList<QString>({s})); + QCOMPARE(run(pool.data(), &MyObject::memberString1, &obj, crs).results(), + QList<QString>({crs})); + QCOMPARE(run(pool.data(), &MyObject::memberString1, &obj, cs).results(), + QList<QString>({cs})); + QCOMPARE(run(pool.data(), &MyObject::memberString1, &obj, + QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); + QCOMPARE(run(pool.data(), &MyObject::memberString2, &obj, s).results(), + QList<QString>({s})); + QCOMPARE(run(pool.data(), &MyObject::memberString2, &obj, crs).results(), + QList<QString>({crs})); + QCOMPARE(run(pool.data(), &MyObject::memberString2, &obj, cs).results(), + QList<QString>({cs})); + QCOMPARE(run(pool.data(), &MyObject::memberString2, &obj, + QString(QLatin1String("rvalue"))).results(), + QList<QString>({QString(QLatin1String("rvalue"))})); +} + +void tst_QtConcurrentRun::withPromiseAndThen() +{ + bool runExecuted = false; + bool cancelReceivedBeforeSync = false; + bool cancelReceivedAfterSync = false; + + bool syncBegin = false; + bool syncEnd = false; + + QMutex mutex; + QWaitCondition condition; + + auto reset = [&]() { + runExecuted = false; + cancelReceivedBeforeSync = false; + cancelReceivedAfterSync = false; + syncBegin = false; + syncEnd = false; + }; + + auto setFlag = [&mutex, &condition] (bool &flag) { + QMutexLocker locker(&mutex); + flag = true; + condition.wakeOne(); + }; + + auto waitForFlag = [&mutex, &condition] (const bool &flag) { + QMutexLocker locker(&mutex); + while (!flag) + condition.wait(&mutex); + }; + + auto report1WithCancel = [&](QPromise<int> &promise) { + runExecuted = true; + cancelReceivedBeforeSync = promise.isCanceled(); + + setFlag(syncBegin); + waitForFlag(syncEnd); + + cancelReceivedAfterSync = promise.isCanceled(); + if (cancelReceivedAfterSync) + return; + promise.addResult(1); + }; + + { + auto future = run(report1WithCancel); + + waitForFlag(syncBegin); + future.cancel(); + setFlag(syncEnd); + + future.waitForFinished(); + QCOMPARE(future.results().size(), 0); + QVERIFY(runExecuted); + QVERIFY(!cancelReceivedBeforeSync); + QVERIFY(cancelReceivedAfterSync); + } + + reset(); + + { + bool thenExecuted = false; + bool cancelExecuted = false; + auto future = run(report1WithCancel); + auto resultFuture = future.then(QtFuture::Launch::Async, [&](int) { thenExecuted = true; }) + .onCanceled([&]() { cancelExecuted = true; }); + + waitForFlag(syncBegin); + // no cancel this time + setFlag(syncEnd); + + resultFuture.waitForFinished(); + QCOMPARE(future.results().size(), 1); + QCOMPARE(future.result(), 1); + QVERIFY(runExecuted); + QVERIFY(thenExecuted); + QVERIFY(!cancelExecuted); + QVERIFY(!cancelReceivedBeforeSync); + QVERIFY(!cancelReceivedAfterSync); + } + + reset(); + + { + bool thenExecuted = false; + bool cancelExecuted = false; + auto future = run(report1WithCancel); + auto resultFuture = future.then(QtFuture::Launch::Async, [&](int) { thenExecuted = true; }) + .onCanceled([&]() { cancelExecuted = true; }); + + waitForFlag(syncBegin); + future.cancel(); + setFlag(syncEnd); + + resultFuture.waitForFinished(); + QCOMPARE(future.results().size(), 0); + QVERIFY(runExecuted); + QVERIFY(!thenExecuted); + QVERIFY(cancelExecuted); + QVERIFY(!cancelReceivedBeforeSync); + QVERIFY(cancelReceivedAfterSync); + } +} + +class MoveOnlyType +{ +public: + MoveOnlyType() = default; + MoveOnlyType(const MoveOnlyType &) = delete; + MoveOnlyType(MoveOnlyType &&) = default; + MoveOnlyType &operator=(const MoveOnlyType &) = delete; + MoveOnlyType &operator=(MoveOnlyType &&) = default; +}; + +class MoveOnlyCallable : public MoveOnlyType +{ +public: + void operator()(QPromise<int> &promise, const MoveOnlyType &) + { + promise.addResult(1); + } +}; + +void tst_QtConcurrentRun::moveOnlyType() +{ + QCOMPARE(run(MoveOnlyCallable(), MoveOnlyType()).results(), + QList<int>({1})); +} + +void tst_QtConcurrentRun::crefFunction() +{ + { + // free function pointer with promise + auto fun = &returnInt0; + QCOMPARE(run(std::cref(fun)).result(), 10); + + // lambda with promise + auto lambda = [](int n) { + return 2 * n; + }; + QCOMPARE(run(std::cref(lambda), 3).result(), 6); + + // std::function with promise + const std::function<int(int)> funObj = [](int n) { + return 2 * n; + }; + QCOMPARE(run(std::cref(funObj), 2).result(), 4); + + // callable with promise + const AConst c{}; + QCOMPARE(run(std::cref(c), 2).result(), 2); + + // member functions with promise + auto member = &AConst::member0; + const AConst obj{}; + QCOMPARE(run(std::cref(member), &obj).result(), 10); + } + + { + // free function pointer with promise + auto fun = &report3; + QCOMPARE(run(std::cref(fun)).results(), + QList<int>({0, 2, 1})); + + // lambda with promise + auto lambda = [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(run(std::cref(lambda), 3).results(), + QList<double>({0, 0, 0})); + + // std::function with promise + const std::function<void(QPromise<double> &, int)> funObj = + [](QPromise<double> &promise, int n) { + for (int i = 0; i < n; ++i) + promise.addResult(0); + }; + QCOMPARE(run(std::cref(funObj), 2).results(), + QList<double>({0, 0})); + + // callable with promise + const Callable c{}; + QCOMPARE(run(std::cref(c), 2).results(), + QList<double>({0, 0})); + + // member functions with promise + auto member = &MyObject::member0; + const MyObject obj{}; + QCOMPARE(run(std::cref(member), &obj).results(), + QList<double>({0, 2, 1})); + } +} + +int explicitPromise(QPromise<int> &promise, int &i) +{ + promise.setProgressRange(-10, 10); + ++i; + return i * 2; +} + +void tst_QtConcurrentRun::customPromise() +{ + QPromise<int> p; + int i = 4; + QCOMPARE(QtConcurrent::run(explicitPromise, std::ref(p), std::ref(i)).result(), 10); + QCOMPARE(i, 5); + QCOMPARE(p.future().progressMinimum(), -10); + QCOMPARE(p.future().progressMaximum(), 10); +} + +void tst_QtConcurrentRun::nonDefaultConstructibleValue() +{ + struct NonDefaultConstructible + { + explicit NonDefaultConstructible(int v) : value(v) { } + int value = 0; + }; + + auto future = QtConcurrent::run([] { return NonDefaultConstructible(42); }); + QCOMPARE(future.result().value, 42); +} + +// QTBUG-98901 +void tst_QtConcurrentRun::nullThreadPool() +{ + QThreadPool *pool = nullptr; + std::atomic<bool> isInvoked = false; + auto future = run(pool, [&] { isInvoked = true; }); + future.waitForFinished(); + QVERIFY(future.isCanceled()); + QVERIFY(!isInvoked); +} + +struct LifetimeChecker +{ + LifetimeChecker() { ++count; } + LifetimeChecker(const LifetimeChecker &) { ++count; } + ~LifetimeChecker() { --count; } + + void operator()() { } + + static std::atomic<int> count; +}; +std::atomic<int> LifetimeChecker::count = 0; + +void tst_QtConcurrentRun::nullThreadPoolNoLeak() +{ + { + QThreadPool *pool = nullptr; + auto future = run(pool, LifetimeChecker()); + future.waitForFinished(); + } + QCOMPARE(LifetimeChecker::count, 0); } QTEST_MAIN(tst_QtConcurrentRun) |