From c34673e5d918e1882401572f81d8c2d6f54c5459 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 30 Sep 2020 16:42:47 +0200 Subject: [PATCH] QtConcurrent: Handle running with promise inside QTaskBuilder Please note, that in case of run with promise it doesn't make sense to provide the overload taking the FutureResult, as if we are going to ignore the returned QFuture object, we can't communicate with the passed QPromise. Fixes: QTBUG-87083 Change-Id: I4066506736c2bbeea3e42030b9495f13e06cb27e Reviewed-by: Sona Kurazyan --- .../code/src_concurrent_qtconcurrenttask.cpp | 9 +++ src/concurrent/qtaskbuilder.h | 5 +- src/concurrent/qtconcurrenttask.qdoc | 8 +++ .../qtconcurrenttask/tst_qtconcurrenttask.cpp | 55 +++++++++++++++++-- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/concurrent/doc/snippets/code/src_concurrent_qtconcurrenttask.cpp b/src/concurrent/doc/snippets/code/src_concurrent_qtconcurrenttask.cpp index b067b49e7e0..267d6ebc1ef 100644 --- a/src/concurrent/doc/snippets/code/src_concurrent_qtconcurrenttask.cpp +++ b/src/concurrent/doc/snippets/code/src_concurrent_qtconcurrenttask.cpp @@ -129,3 +129,12 @@ QtConcurrent::task([]{ return 42; }).withPriority(10).spawn(); //! [11] QtConcurrent::task([]{ qDebug("Hello, world!"); }).spawn(FutureResult::Ignore); //! [11] + +//! [12] +void increment(QPromise &promise, int i) +{ + promise.addResult(i + 1); +} + +int result = QtConcurrent::task(&increment).withArguments(10).spawn().result(); // result == 11 +//! [12] diff --git a/src/concurrent/qtaskbuilder.h b/src/concurrent/qtaskbuilder.h index e64df242c37..16b21f103b4 100644 --- a/src/concurrent/qtaskbuilder.h +++ b/src/concurrent/qtaskbuilder.h @@ -89,10 +89,11 @@ public: [[nodiscard]] auto spawn() { - return (new StoredFunctionCall(std::move(taskWithArgs))) - ->start(startParameters); + return TaskResolver, std::decay_t...>::run( + std::move(taskWithArgs), startParameters); } + // We don't want to run with promise when we don't return a QFuture void spawn(FutureResult) { (new StoredFunctionCall(std::move(taskWithArgs))) diff --git a/src/concurrent/qtconcurrenttask.qdoc b/src/concurrent/qtconcurrenttask.qdoc index 9000b424fe7..72951f8f117 100644 --- a/src/concurrent/qtconcurrenttask.qdoc +++ b/src/concurrent/qtconcurrenttask.qdoc @@ -148,6 +148,14 @@ QtConcurrent::QTaskBuilder::spawn(QtConcurrent::FutureResult::Ignore): \snippet code/src_concurrent_qtconcurrenttask.cpp 11 + + You can access the promise object associated with the task by defining an + additional argument of \c {QPromise &} type inside the function. + This additional argument must be the first argument passed to the function, and + like in \l {Concurrent Run With Promise} mode, the function is expected to return void type. + Result reporting is done through QPromise API: + + \snippet code/src_concurrent_qtconcurrenttask.cpp 12 */ /*! diff --git a/tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp b/tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp index f4e6f996604..3ad34c33693 100644 --- a/tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp +++ b/tests/auto/concurrent/qtconcurrenttask/tst_qtconcurrenttask.cpp @@ -41,9 +41,11 @@ private Q_SLOTS: void taskWithLambda(); void taskWithArguments(); void useCustomThreadPool(); + void setPriority_data(); void setPriority(); void adjustAllSettings(); void ignoreFutureResult(); + void withPromise(); }; using namespace QtConcurrent; @@ -59,6 +61,7 @@ void tst_QtConcurrentTask::taskWithFreeFunction() QCOMPARE(result, 42); } + void tst_QtConcurrentTask::taskWithClassMethod() { QString result("foobar"); @@ -67,6 +70,7 @@ void tst_QtConcurrentTask::taskWithClassMethod() QCOMPARE(result, "foo"); } + void tst_QtConcurrentTask::taskWithCallableObject() { QCOMPARE(task(std::plus()) @@ -100,8 +104,18 @@ void tst_QtConcurrentTask::useCustomThreadPool() QCOMPARE(result, 42); } +void tst_QtConcurrentTask::setPriority_data() +{ + QTest::addColumn("runWithPromise"); + + QTest::addRow("without promise") << false; + QTest::addRow("with promise") << true; +} + void tst_QtConcurrentTask::setPriority() { + QFETCH(bool, runWithPromise); + QThreadPool pool; pool.setMaxThreadCount(1); @@ -115,15 +129,25 @@ void tst_QtConcurrentTask::setPriority() const int tasksCount = 10; QList priorities(tasksCount); std::iota(priorities.begin(), priorities.end(), 1); - auto seed = std::chrono::system_clock::now().time_since_epoch().count(); + auto seed = std::random_device {}(); std::shuffle(priorities.begin(), priorities.end(), std::default_random_engine(seed)); + qDebug() << "Generated priorities list" << priorities << "using seed" << seed; + QList actual; - for (int priority : priorities) - futureResults << task([priority, &actual] { actual << priority; }) - .onThreadPool(pool) - .withPriority(priority) - .spawn(); + for (int priority : priorities) { + if (runWithPromise) { + futureResults << task([priority, &actual] (QPromise &) { actual << priority; }) + .onThreadPool(pool) + .withPriority(priority) + .spawn(); + } else { + futureResults << task([priority, &actual] { actual << priority; }) + .onThreadPool(pool) + .withPriority(priority) + .spawn(); + } + } sem.release(); pool.waitForDone(); @@ -156,6 +180,7 @@ void tst_QtConcurrentTask::adjustAllSettings() QCOMPARE(result, QList({ 1, 2, 3 })); } + void tst_QtConcurrentTask::ignoreFutureResult() { QThreadPool pool; @@ -171,5 +196,23 @@ void tst_QtConcurrentTask::ignoreFutureResult() QCOMPARE(value, 10); } +void incrementWithPromise(QPromise &promise, int i) +{ + promise.addResult(i + 1); +} + +void return0WithPromise(QPromise &promise) +{ + promise.addResult(0); +} + +void tst_QtConcurrentTask::withPromise() +{ + QCOMPARE(task(&return0WithPromise).spawn().result(), 0); + QCOMPARE(task(&return0WithPromise).withPriority(7).spawn().result(), 0); + QCOMPARE(task(&incrementWithPromise).withArguments(1).spawn().result(), 2); + QCOMPARE(task(&incrementWithPromise).withArguments(1).withPriority(7).spawn().result(), 2); +} + QTEST_MAIN(tst_QtConcurrentTask) #include "tst_qtconcurrenttask.moc"