// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include using namespace Tasking; using namespace std::chrono; using namespace std::chrono_literals; class MyObject { public: static void staticMember(QPromise &promise, int n) { for (int i = 0; i < n; ++i) promise.addResult(0); } void member(QPromise &promise, int n) const { for (int i = 0; i < n; ++i) promise.addResult(0); } }; class tst_ConcurrentCall : public QObject { Q_OBJECT private slots: // void futureSynchonizer(); void taskTree_data(); void taskTree(); private: QThreadPool m_threadPool; MyObject m_myObject; }; struct TestData { Storage storage; Group root; }; void report3(QPromise &promise) { promise.addResult(0); promise.addResult(2); promise.addResult(1); } static void staticReport3(QPromise &promise) { promise.addResult(0); promise.addResult(2); promise.addResult(1); } void reportN(QPromise &promise, int n) { for (int i = 0; i < n; ++i) promise.addResult(0); } static void staticReportN(QPromise &promise, int n) { for (int i = 0; i < n; ++i) promise.addResult(0); } class Functor { public: void operator()(QPromise &promise, int n) const { for (int i = 0; i < n; ++i) promise.addResult(0); } }; // void tst_ConcurrentCall::futureSynchonizer() // { // auto lambda = [](QPromise &promise) { // while (true) { // if (promise.isCanceled()) { // promise.future().cancel(); // promise.finish(); // return; // } // QThread::msleep(100); // } // }; // FutureSynchronizer synchronizer; // synchronizer.setCancelOnWait(false); // { // Async task; // task.setConcurrentCallData(lambda); // task.setFutureSynchronizer(&synchronizer); // task.start(); // QThread::msleep(10); // // We assume here that worker thread will still work for about 90 ms. // QVERIFY(!task.isCanceled()); // QVERIFY(!task.isDone()); // } // synchronizer.flushFinishedFutures(); // QVERIFY(!synchronizer.isEmpty()); // // The destructor of synchronizer should wait for about 90 ms for worker thread to be canceled // } void multiplyBy2(QPromise &promise, int input) { promise.addResult(input * 2); } template struct FutureArgType; template struct FutureArgType> { using Type = Arg; }; template struct ConcurrentResultType; template struct ConcurrentResultType { using Type = typename FutureArgType(), std::declval()...))>::Type; }; template ::Type> TestData createTestData(const QList &expectedResults, Function &&function, Args &&...args) { Storage storage; const auto onSetup = [=](ConcurrentCall &task) { task.setConcurrentCallData(function, args...); }; const auto onDone = [storage, expectedResults](const ConcurrentCall &task) { *storage = task.results() == expectedResults; }; const Group root { storage, ConcurrentCallTask(onSetup, onDone) }; return TestData{storage, root}; } void tst_ConcurrentCall::taskTree_data() { QTest::addColumn("testData"); const QList report3Result{0, 2, 1}; const QList reportNResult{0, 0}; auto lambda = [](QPromise &promise, int n) { for (int i = 0; i < n; ++i) promise.addResult(0); }; const std::function &, int)> fun = [](QPromise &promise, int n) { for (int i = 0; i < n; ++i) promise.addResult(0); }; QTest::newRow("RefGlobalNoArgs") << createTestData(report3Result, &report3); QTest::newRow("GlobalNoArgs") << createTestData(report3Result, report3); QTest::newRow("RefStaticNoArgs") << createTestData(report3Result, &staticReport3); QTest::newRow("StaticNoArgs") << createTestData(report3Result, staticReport3); QTest::newRow("RefGlobalIntArg") << createTestData(reportNResult, &reportN, 2); QTest::newRow("GlobalIntArg") << createTestData(reportNResult, reportN, 2); QTest::newRow("RefStaticIntArg") << createTestData(reportNResult, &staticReportN, 2); QTest::newRow("StaticIntArg") << createTestData(reportNResult, staticReportN, 2); QTest::newRow("Lambda") << createTestData(reportNResult, lambda, 2); QTest::newRow("Function") << createTestData(reportNResult, fun, 2); QTest::newRow("Functor") << createTestData(reportNResult, Functor(), 2); QTest::newRow("StaticMemberFunction") << createTestData(reportNResult, &MyObject::staticMember, 2); QTest::newRow("MemberFunction") << createTestData(reportNResult, &MyObject::member, &m_myObject, 2); { Storage storage; Storage internalStorage; const auto onSetup = [internalStorage](ConcurrentCall &task) { task.setConcurrentCallData(multiplyBy2, *internalStorage); }; const auto onDone = [internalStorage](const ConcurrentCall &task) { *internalStorage = task.result(); }; const Group root { storage, internalStorage, onGroupSetup([internalStorage] { *internalStorage = 1; }), ConcurrentCallTask(onSetup, onDone, CallDoneIf::Success), ConcurrentCallTask(onSetup, onDone, CallDoneIf::Success), ConcurrentCallTask(onSetup, onDone, CallDoneIf::Success), ConcurrentCallTask(onSetup, onDone, CallDoneIf::Success), onGroupDone([storage, internalStorage] { *storage = *internalStorage == 16; }) }; QTest::newRow("Sequential") << TestData{storage, root}; } } void tst_ConcurrentCall::taskTree() { QFETCH(TestData, testData); TaskTree taskTree({testData.root.withTimeout(1000ms)}); bool actualResult = false; const auto collectResult = [&actualResult](const bool &storage) { actualResult = storage; }; taskTree.onStorageDone(testData.storage, collectResult); const DoneWith result = taskTree.runBlocking(); QCOMPARE(taskTree.isRunning(), false); QCOMPARE(result, DoneWith::Success); QVERIFY(actualResult); } QTEST_GUILESS_MAIN(tst_ConcurrentCall) #include "tst_concurrentcall.moc"