/**************************************************************************** ** ** 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 #include #include #include using namespace QtConcurrent; class tst_QtConcurrentRun: public QObject { Q_OBJECT private slots: void runLightFunction(); void runHeavyFunction(); void returnValue(); void functionObject(); void memberFunctions(); void implicitConvertibleTypes(); void runWaitLoop(); void pollForIsFinished(); void recursive(); #ifndef QT_NO_EXCEPTIONS void exceptions(); #endif void functor(); void lambda(); }; void light() { qDebug("in function"); qDebug("done function"); } void heavy() { qDebug("in function"); QString str; for (int i = 0; i < 1000000; ++i) str.append("a"); qDebug("done function"); } void tst_QtConcurrentRun::runLightFunction() { qDebug("starting function"); QFuture future = run(light); qDebug("waiting"); future.waitForFinished(); qDebug("done"); } void tst_QtConcurrentRun::runHeavyFunction() { QThreadPool pool; qDebug("starting function"); QFuture future = run(&pool, heavy); qDebug("waiting"); future.waitForFinished(); qDebug("done"); } int returnInt0() { return 10; } int returnInt1(int i) { return i; } class A { public: int member0() { return 10; } int member1(int in) { return in; } typedef int result_type; int operator()() { return 10; } int operator()(int in) { return in; } }; class AConst { public: int member0() const { return 10; } int member1(int in) const { return in; } typedef int result_type; int operator()() const { return 10; } int operator()(int in) const { return in; } }; class ANoExcept { public: int member0() noexcept { return 10; } int member1(int in) noexcept { return in; } typedef int result_type; int operator()() noexcept { return 10; } int operator()(int in) noexcept { return in; } }; class AConstNoExcept { public: int member0() const noexcept { return 10; } int member1(int in) const noexcept { return in; } typedef int result_type; int operator()() const noexcept { return 10; } int operator()(int in) const noexcept { return in; } }; void tst_QtConcurrentRun::returnValue() { QThreadPool pool; QFuture f; f = run(returnInt0); QCOMPARE(f.result(), 10); f = run(&pool, returnInt0); QCOMPARE(f.result(), 10); A a; f = run(&a, &A::member0); QCOMPARE(f.result(), 10); f = run(&pool, &a, &A::member0); QCOMPARE(f.result(), 10); f = run(&a, &A::member1, 20); QCOMPARE(f.result(), 20); f = run(&pool, &a, &A::member1, 20); QCOMPARE(f.result(), 20); f = run(a, &A::member0); QCOMPARE(f.result(), 10); f = run(&pool, a, &A::member0); QCOMPARE(f.result(), 10); f = run(a, &A::member1, 20); QCOMPARE(f.result(), 20); f = run(&pool, a, &A::member1, 20); QCOMPARE(f.result(), 20); f = run(a); QCOMPARE(f.result(), 10); f = run(&pool, a); QCOMPARE(f.result(), 10); f = run(&a); QCOMPARE(f.result(), 10); f = run(&pool, &a); QCOMPARE(f.result(), 10); f = run(a, 20); QCOMPARE(f.result(), 20); f = run(&pool, a, 20); QCOMPARE(f.result(), 20); f = run(&a, 20); QCOMPARE(f.result(), 20); f = run(&pool, &a, 20); QCOMPARE(f.result(), 20); const AConst aConst = AConst(); f = run(&aConst, &AConst::member0); QCOMPARE(f.result(), 10); f = run(&pool, &aConst, &AConst::member0); QCOMPARE(f.result(), 10); f = run(&aConst, &AConst::member1, 20); QCOMPARE(f.result(), 20); f = run(&pool, &aConst, &AConst::member1, 20); QCOMPARE(f.result(), 20); f = run(aConst, &AConst::member0); QCOMPARE(f.result(), 10); f = run(&pool, aConst, &AConst::member0); QCOMPARE(f.result(), 10); f = run(aConst, &AConst::member1, 20); QCOMPARE(f.result(), 20); f = run(&pool, aConst, &AConst::member1, 20); QCOMPARE(f.result(), 20); f = run(aConst); QCOMPARE(f.result(), 10); f = run(&pool, aConst); QCOMPARE(f.result(), 10); f = run(&aConst); QCOMPARE(f.result(), 10); f = run(&pool, &aConst); QCOMPARE(f.result(), 10); f = run(aConst, 20); QCOMPARE(f.result(), 20); f = run(&pool, aConst, 20); QCOMPARE(f.result(), 20); f = run(&aConst, 20); QCOMPARE(f.result(), 20); f = run(&pool, &aConst, 20); QCOMPARE(f.result(), 20); ANoExcept aNoExcept; f = run(&aNoExcept, &ANoExcept::member0); QCOMPARE(f.result(), 10); f = run(&pool, &aNoExcept, &ANoExcept::member0); QCOMPARE(f.result(), 10); f = run(&aNoExcept, &ANoExcept::member1, 20); QCOMPARE(f.result(), 20); f = run(&pool, &aNoExcept, &ANoExcept::member1, 20); QCOMPARE(f.result(), 20); f = run(aNoExcept, &ANoExcept::member0); QCOMPARE(f.result(), 10); f = run(&pool, aNoExcept, &ANoExcept::member0); QCOMPARE(f.result(), 10); f = run(aNoExcept, &ANoExcept::member1, 20); QCOMPARE(f.result(), 20); f = run(&pool, aNoExcept, &ANoExcept::member1, 20); QCOMPARE(f.result(), 20); f = run(aNoExcept); QCOMPARE(f.result(), 10); f = run(&pool, aNoExcept); QCOMPARE(f.result(), 10); f = run(&aNoExcept); QCOMPARE(f.result(), 10); f = run(&pool, &aNoExcept); QCOMPARE(f.result(), 10); f = run(aNoExcept, 20); QCOMPARE(f.result(), 20); f = run(&pool, aNoExcept, 20); QCOMPARE(f.result(), 20); f = run(&aNoExcept, 20); QCOMPARE(f.result(), 20); f = run(&pool, &aNoExcept, 20); QCOMPARE(f.result(), 20); const AConstNoExcept aConstNoExcept = AConstNoExcept(); f = run(&aConstNoExcept, &AConstNoExcept::member0); QCOMPARE(f.result(), 10); f = run(&pool, &aConstNoExcept, &AConstNoExcept::member0); QCOMPARE(f.result(), 10); f = run(&aConstNoExcept, &AConstNoExcept::member1, 20); QCOMPARE(f.result(), 20); f = run(&pool, &aConstNoExcept, &AConstNoExcept::member1, 20); QCOMPARE(f.result(), 20); f = run(aConstNoExcept, &AConstNoExcept::member0); QCOMPARE(f.result(), 10); f = run(&pool, aConstNoExcept, &AConstNoExcept::member0); QCOMPARE(f.result(), 10); f = run(aConstNoExcept, &AConstNoExcept::member1, 20); QCOMPARE(f.result(), 20); f = run(&pool, aConstNoExcept, &AConstNoExcept::member1, 20); QCOMPARE(f.result(), 20); f = run(aConstNoExcept); QCOMPARE(f.result(), 10); f = run(&pool, aConstNoExcept); QCOMPARE(f.result(), 10); f = run(&aConstNoExcept); QCOMPARE(f.result(), 10); f = run(&pool, &aConstNoExcept); QCOMPARE(f.result(), 10); f = run(aConstNoExcept, 20); QCOMPARE(f.result(), 20); f = run(&pool, aConstNoExcept, 20); QCOMPARE(f.result(), 20); f = run(&aConstNoExcept, 20); QCOMPARE(f.result(), 20); f = run(&pool, &aConstNoExcept, 20); QCOMPARE(f.result(), 20); } struct TestClass { void foo() { } typedef void result_type; void operator()() { } void operator()(int) { } void fooInt(int){ }; }; struct TestConstClass { void foo() const { } typedef void result_type; void operator()() const { } void operator()(int) const { } void fooInt(int) const { }; }; void tst_QtConcurrentRun::functionObject() { QThreadPool pool; QFuture f; TestClass c; f = run(c); f = run(&c); f = run(c, 10); f = run(&c, 10); f = run(&pool, c); f = run(&pool, &c); f = run(&pool, c, 10); f = run(&pool, &c, 10); const TestConstClass cc = TestConstClass(); f = run(cc); f = run(&cc); f = run(cc, 10); f = run(&cc, 10); f = run(&pool, cc); f = run(&pool, &cc); f = run(&pool, cc, 10); f = run(&pool, &cc, 10); } void tst_QtConcurrentRun::memberFunctions() { QThreadPool pool; TestClass c; run(c, &TestClass::foo).waitForFinished(); run(&c, &TestClass::foo).waitForFinished(); run(c, &TestClass::fooInt, 10).waitForFinished(); run(&c, &TestClass::fooInt, 10).waitForFinished(); run(&pool, c, &TestClass::foo).waitForFinished(); run(&pool, &c, &TestClass::foo).waitForFinished(); run(&pool, c, &TestClass::fooInt, 10).waitForFinished(); run(&pool, &c, &TestClass::fooInt, 10).waitForFinished(); const TestConstClass cc = TestConstClass(); run(cc, &TestConstClass::foo).waitForFinished(); run(&cc, &TestConstClass::foo).waitForFinished(); run(cc, &TestConstClass::fooInt, 10).waitForFinished(); run(&cc, &TestConstClass::fooInt, 10).waitForFinished(); run(&pool, cc, &TestConstClass::foo).waitForFinished(); run(&pool, &cc, &TestConstClass::foo).waitForFinished(); run(&pool, cc, &TestConstClass::fooInt, 10).waitForFinished(); run(&pool, &cc, &TestConstClass::fooInt, 10).waitForFinished(); } void doubleFunction(double) { } void stringConstRefFunction(const QString &) { } void stringRefFunction(QString &) { } void stringFunction(QString) { } void stringIntFunction(QString) { } void tst_QtConcurrentRun::implicitConvertibleTypes() { QThreadPool pool; double d; run(doubleFunction, d).waitForFinished(); run(&pool, doubleFunction, d).waitForFinished(); int i; run(doubleFunction, d).waitForFinished(); run(&pool, doubleFunction, d).waitForFinished(); run(doubleFunction, i).waitForFinished(); run(&pool, doubleFunction, i).waitForFinished(); run(doubleFunction, 10).waitForFinished(); run(&pool, doubleFunction, 10).waitForFinished(); run(stringFunction, QLatin1String("Foo")).waitForFinished(); run(&pool, stringFunction, QLatin1String("Foo")).waitForFinished(); run(stringConstRefFunction, QLatin1String("Foo")).waitForFinished(); run(&pool, stringConstRefFunction, QLatin1String("Foo")).waitForFinished(); QString string; run(stringRefFunction, string).waitForFinished(); run(&pool, stringRefFunction, string).waitForFinished(); } void fn() { } void tst_QtConcurrentRun::runWaitLoop() { for (int i = 0; i < 1000; ++i) run(fn).waitForFinished(); } static bool allFinished(const QList > &futures) { auto hasNotFinished = [](const QFuture &future) { return !future.isFinished(); }; return std::find_if(futures.cbegin(), futures.cend(), hasNotFinished) == futures.constEnd(); } static void runFunction() { QEventLoop loop; QTimer::singleShot(20, &loop, &QEventLoop::quit); loop.exec(); } void tst_QtConcurrentRun::pollForIsFinished() { const int numThreads = std::max(4, 2 * QThread::idealThreadCount()); QThreadPool::globalInstance()->setMaxThreadCount(numThreads); QFutureSynchronizer synchronizer; for (int i = 0; i < numThreads; ++i) synchronizer.addFuture(QtConcurrent::run(&runFunction)); // same as synchronizer.waitForFinished() but with a timeout QTRY_VERIFY(allFinished(synchronizer.futures())); } QAtomicInt count; void recursiveRun(int level) { count.ref(); if (--level > 0) { QFuture f1 = run(recursiveRun, level); QFuture f2 = run(recursiveRun, level); f1.waitForFinished(); f2.waitForFinished(); } } int recursiveResult(int level) { count.ref(); if (--level > 0) { QFuture f1 = run(recursiveResult, level); QFuture f2 = run(recursiveResult, level); return f1.result() + f2.result(); } return 1; } void tst_QtConcurrentRun::recursive() { int levels = 15; for (int i = 0; i < QThread::idealThreadCount(); ++i) { count.storeRelaxed(0); QThreadPool::globalInstance()->setMaxThreadCount(i); recursiveRun(levels); QCOMPARE(count.loadRelaxed(), (int)std::pow(2.0, levels) - 1); } for (int i = 0; i < QThread::idealThreadCount(); ++i) { count.storeRelaxed(0); QThreadPool::globalInstance()->setMaxThreadCount(i); recursiveResult(levels); QCOMPARE(count.loadRelaxed(), (int)std::pow(2.0, levels) - 1); } } int e; void vfn0() { ++e; } int fn0() { return 1; } void vfn1(double) { ++e; } int fn1(int) { return 1; } void vfn2(double, int *) { ++e; } int fn2(double, int *) { return 1; } #ifndef QT_NO_EXCEPTIONS void throwFunction() { throw QException(); } int throwFunctionReturn() { throw QException(); return 0; } class SlowTask : public QRunnable { public: static QAtomicInt cancel; void run() override { int iter = 60; while (--iter && !cancel.loadRelaxed()) QThread::currentThread()->msleep(25); } }; QAtomicInt SlowTask::cancel; void tst_QtConcurrentRun::exceptions() { QThreadPool pool; bool caught; caught = false; try { QtConcurrent::run(throwFunction).waitForFinished(); } catch (QException &) { caught = true; } if (!caught) QFAIL("did not get exception"); caught = false; try { QtConcurrent::run(&pool, throwFunction).waitForFinished(); } catch (QException &) { caught = true; } if (!caught) QFAIL("did not get exception"); caught = false; try { QtConcurrent::run(throwFunctionReturn).waitForFinished(); } catch (QException &) { caught = true; } if (!caught) QFAIL("did not get exception"); caught = false; try { QtConcurrent::run(&pool, throwFunctionReturn).waitForFinished(); } catch (QException &) { caught = true; } if (!caught) QFAIL("did not get exception"); caught = false; try { QtConcurrent::run(&pool, throwFunctionReturn).result(); } catch (QException &) { caught = true; } QVERIFY2(caught, "did not get exception"); // Force the task to be run on this thread. caught = false; QThreadPool shortPool; shortPool.setMaxThreadCount(1); SlowTask *st = new SlowTask(); try { shortPool.start(st); QtConcurrent::run(&shortPool, throwFunctionReturn).result(); } catch (QException &) { caught = true; } SlowTask::cancel.storeRelaxed(true); QVERIFY2(caught, "did not get exception"); } #endif // Compiler supports decltype struct Functor { int operator()() { return 42; } double operator()(double a, double b) { return a/b; } int operator()(int a, int b) { return a/b; } void operator()(int) { } void operator()(int, int, int) { } void operator()(int, int, int, int) { } void operator()(int, int, int, int, int) { } void operator()(int, int, int, int, int, int) { } }; // This tests functor without result_type; decltype need to be supported by the compiler. void tst_QtConcurrentRun::functor() { Functor f; { QFuture fut = QtConcurrent::run(f); QCOMPARE(fut.result(), 42); } { QFuture fut = QtConcurrent::run(f, 8.5, 1.8); QCOMPARE(fut.result(), (8.5/1.8)); } { QFuture fut = QtConcurrent::run(f, 19, 3); QCOMPARE(fut.result(), int(19/3)); } { QtConcurrent::run(f, 1).waitForFinished(); QtConcurrent::run(f, 1,2).waitForFinished(); QtConcurrent::run(f, 1,2,3).waitForFinished(); QtConcurrent::run(f, 1,2,3,4).waitForFinished(); QtConcurrent::run(f, 1,2,3,4,5).waitForFinished(); } // and now with explicit pool: QThreadPool pool; { QFuture fut = QtConcurrent::run(&pool, f); QCOMPARE(fut.result(), 42); } { QFuture fut = QtConcurrent::run(&pool, f, 8.5, 1.8); QCOMPARE(fut.result(), (8.5/1.8)); } { QFuture fut = QtConcurrent::run(&pool, f, 19, 3); QCOMPARE(fut.result(), int(19/3)); } { QtConcurrent::run(&pool, f, 1).waitForFinished(); QtConcurrent::run(&pool, f, 1,2).waitForFinished(); QtConcurrent::run(&pool, f, 1,2,3).waitForFinished(); QtConcurrent::run(&pool, f, 1,2,3,4).waitForFinished(); QtConcurrent::run(&pool, f, 1,2,3,4,5).waitForFinished(); } } // 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); { QString str { "Hello World Foo" }; QFuture f1 = QtConcurrent::run([&](){ return str.split(' '); }); auto r = f1.result(); QCOMPARE(r, QStringList({"Hello", "World", "Foo"})); } // 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); { QString str { "Hello World Foo" }; QFuture f1 = QtConcurrent::run(&pool, [&](){ return str.split(' '); }); auto r = f1.result(); QCOMPARE(r, QStringList({"Hello", "World", "Foo"})); } } QTEST_MAIN(tst_QtConcurrentRun) #include "tst_qtconcurrentrun.moc"