2023-11-09 01:17:07 +01:00
|
|
|
// 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 <tasking/concurrentcall.h>
|
|
|
|
|
|
|
|
|
|
#include <QtTest>
|
|
|
|
|
|
|
|
|
|
using namespace Tasking;
|
|
|
|
|
|
|
|
|
|
using namespace std::chrono;
|
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
|
|
|
|
|
class MyObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static void staticMember(QPromise<double> &promise, int n)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < n; ++i)
|
|
|
|
|
promise.addResult(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void member(QPromise<double> &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
|
|
|
|
|
{
|
2023-11-19 14:50:55 +01:00
|
|
|
Storage<bool> storage;
|
2023-11-09 01:17:07 +01:00
|
|
|
Group root;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Functor {
|
|
|
|
|
public:
|
|
|
|
|
void operator()(QPromise<double> &promise, int n) const
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < n; ++i)
|
|
|
|
|
promise.addResult(0);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// void tst_ConcurrentCall::futureSynchonizer()
|
|
|
|
|
// {
|
|
|
|
|
// auto lambda = [](QPromise<int> &promise) {
|
|
|
|
|
// while (true) {
|
|
|
|
|
// if (promise.isCanceled()) {
|
|
|
|
|
// promise.future().cancel();
|
|
|
|
|
// promise.finish();
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
// QThread::msleep(100);
|
|
|
|
|
// }
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
// FutureSynchronizer synchronizer;
|
|
|
|
|
// synchronizer.setCancelOnWait(false);
|
|
|
|
|
// {
|
|
|
|
|
// Async<int> 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<int> &promise, int input) { promise.addResult(input * 2); }
|
|
|
|
|
|
|
|
|
|
template <typename...>
|
|
|
|
|
struct FutureArgType;
|
|
|
|
|
|
|
|
|
|
template <typename Arg>
|
|
|
|
|
struct FutureArgType<QFuture<Arg>>
|
|
|
|
|
{
|
|
|
|
|
using Type = Arg;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename...>
|
|
|
|
|
struct ConcurrentResultType;
|
|
|
|
|
|
|
|
|
|
template<typename Function, typename ...Args>
|
|
|
|
|
struct ConcurrentResultType<Function, Args...>
|
|
|
|
|
{
|
|
|
|
|
using Type = typename FutureArgType<decltype(QtConcurrent::run(
|
|
|
|
|
std::declval<Function>(), std::declval<Args>()...))>::Type;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename Function, typename ...Args,
|
|
|
|
|
typename ResultType = typename ConcurrentResultType<Function, Args...>::Type>
|
|
|
|
|
TestData createTestData(const QList<ResultType> &expectedResults, Function &&function, Args &&...args)
|
|
|
|
|
{
|
2023-11-19 14:50:55 +01:00
|
|
|
Storage<bool> storage;
|
2023-11-09 01:17:07 +01:00
|
|
|
|
|
|
|
|
const auto onSetup = [=](ConcurrentCall<ResultType> &task) {
|
|
|
|
|
task.setConcurrentCallData(function, args...);
|
|
|
|
|
};
|
|
|
|
|
const auto onDone = [storage, expectedResults](const ConcurrentCall<ResultType> &task) {
|
|
|
|
|
*storage = task.results() == expectedResults;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Group root {
|
2023-11-19 14:18:41 +01:00
|
|
|
storage,
|
2023-11-09 01:17:07 +01:00
|
|
|
ConcurrentCallTask<ResultType>(onSetup, onDone)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return TestData{storage, root};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void tst_ConcurrentCall::taskTree_data()
|
|
|
|
|
{
|
|
|
|
|
QTest::addColumn<TestData>("testData");
|
|
|
|
|
|
|
|
|
|
const QList<int> report3Result{0, 2, 1};
|
|
|
|
|
const QList<double> reportNResult{0, 0};
|
|
|
|
|
|
|
|
|
|
auto lambda = [](QPromise<double> &promise, int n) {
|
|
|
|
|
for (int i = 0; i < n; ++i)
|
|
|
|
|
promise.addResult(0);
|
|
|
|
|
};
|
|
|
|
|
const std::function<void(QPromise<double> &, int)> fun = [](QPromise<double> &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);
|
|
|
|
|
|
|
|
|
|
{
|
2023-11-19 14:50:55 +01:00
|
|
|
Storage<bool> storage;
|
|
|
|
|
Storage<int> internalStorage;
|
2023-11-09 01:17:07 +01:00
|
|
|
|
|
|
|
|
const auto onSetup = [internalStorage](ConcurrentCall<int> &task) {
|
|
|
|
|
task.setConcurrentCallData(multiplyBy2, *internalStorage);
|
|
|
|
|
};
|
|
|
|
|
const auto onDone = [internalStorage](const ConcurrentCall<int> &task) {
|
|
|
|
|
*internalStorage = task.result();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const Group root {
|
2023-11-19 14:18:41 +01:00
|
|
|
storage,
|
|
|
|
|
internalStorage,
|
2023-11-09 01:17:07 +01:00
|
|
|
onGroupSetup([internalStorage] { *internalStorage = 1; }),
|
|
|
|
|
ConcurrentCallTask<int>(onSetup, onDone, CallDoneIf::Success),
|
|
|
|
|
ConcurrentCallTask<int>(onSetup, onDone, CallDoneIf::Success),
|
|
|
|
|
ConcurrentCallTask<int>(onSetup, onDone, CallDoneIf::Success),
|
|
|
|
|
ConcurrentCallTask<int>(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"
|