From 9b4b32ec98bf80023d2233a061564dfe59b783c7 Mon Sep 17 00:00:00 2001 From: Ivan Solovev Date: Mon, 20 Mar 2023 16:11:15 +0100 Subject: [PATCH] Long live QtFuture::makeReadyVoidFuture() and QtFuture::makeReadyValueFuture() [ChangeLog][QtCore][QFuture] Added QtFuture::makeReadyVoidFuture() and QtFuture::makeReadyValueFuture(). Basically, these methods behave like QtFuture::makeReadyFuture(), but QtFuture::makeReadyValueFuture() does not have a "const QList &" specialization returning QFuture instead of QFuture>, which allows it to always behave consistently. This patch also introduces usage of the new methods around qtbase. Task-number: QTBUG-109677 Change-Id: I89df8b26d82c192baad69efb5df517a8b182995f Reviewed-by: Marc Mutz --- .../code/src_corelib_thread_qfuture.cpp | 20 +++++- .../platform/android/qandroidextras.cpp | 6 +- src/corelib/thread/qfuture.h | 10 +++ src/corelib/thread/qfuture.qdoc | 64 +++++++++++++++---- src/corelib/thread/qfuture_impl.h | 27 ++++---- src/corelib/thread/qfutureinterface.cpp | 13 ++++ .../corelib/thread/qfuture/tst_qfuture.cpp | 39 ++++++++--- .../thread/qfuture/tst_bench_qfuture.cpp | 18 +++--- 8 files changed, 147 insertions(+), 50 deletions(-) diff --git a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp index 494719d988..ea104ec1a9 100644 --- a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp @@ -239,9 +239,9 @@ auto future = QtConcurrent::run([] { //! [20] QObject *context = ...; -auto future = cachedResultsReady ? QtFuture::makeReadyFuture(results) - : QtConcurrent::run([] { /* compute results */}); -auto continuation = future.then(context, [] (Results results) { +auto future = cachedResultsReady ? QtFuture::makeReadyValueFuture(result) + : QtConcurrent::run([] { /* compute result */}); +auto continuation = future.then(context, [] (Result result) { // Runs in the context's thread }).then([] { // May or may not run in the context's thread @@ -413,3 +413,17 @@ auto f = QtFuture::makeReadyRangeFuture({1, 2, 3}); const int count = f.resultCount(); // count == 3 const auto results = f.results(); // results == { 1, 2, 3 } //! [34] + +//! [35] +auto f = QtFuture::makeReadyValueFuture(std::make_unique(42)); +... +const int result = *f.takeResult(); // result == 42 +//! [35] + +//! [36] +auto f = QtFuture::makeReadyVoidFuture(); +... +const bool started = f.isStarted(); // started == true +const bool running = f.isRunning(); // running == false +const bool finished = f.isFinished(); // finished == true +//! [36] diff --git a/src/corelib/platform/android/qandroidextras.cpp b/src/corelib/platform/android/qandroidextras.cpp index b7c07a8f03..85f8a7e808 100644 --- a/src/corelib/platform/android/qandroidextras.cpp +++ b/src/corelib/platform/android/qandroidextras.cpp @@ -1095,7 +1095,7 @@ requestPermissionsInternal(const QStringList &permissions) } if (!QtAndroidPrivate::acquireAndroidDeadlockProtector()) - return QtFuture::makeReadyFuture(QtAndroidPrivate::Denied); + return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied); QSharedPointer> promise; promise.reset(new QPromise()); @@ -1145,7 +1145,7 @@ QtAndroidPrivate::requestPermissions(const QStringList &permissions) { // avoid the uneccessary call and response to an empty permission string if (permissions.isEmpty()) - return QtFuture::makeReadyFuture(QtAndroidPrivate::Denied); + return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied); return requestPermissionsInternal(permissions); } @@ -1168,7 +1168,7 @@ QtAndroidPrivate::checkPermission(const QString &permission) QJniObject::fromString(permission).object()); result = resultFromAndroid(res); } - return QtFuture::makeReadyFuture(result); + return QtFuture::makeReadyValueFuture(result); } bool QtAndroidPrivate::registerPermissionNatives() diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h index 34fb8a039d..219d3368f0 100644 --- a/src/corelib/thread/qfuture.h +++ b/src/corelib/thread/qfuture.h @@ -522,6 +522,16 @@ QFuture...>> whenAny(Futures &&... futures); #endif // Q_QDOC +#if defined(Q_QDOC) +static QFuture makeReadyFuture() +#else +template +static QFuture makeReadyFuture() +#endif +{ + return makeReadyVoidFuture(); +} + } // namespace QtFuture Q_DECLARE_SEQUENTIAL_ITERATOR(Future) diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc index 7ba4debeea..e9aabdda2d 100644 --- a/src/corelib/thread/qfuture.qdoc +++ b/src/corelib/thread/qfuture.qdoc @@ -118,15 +118,16 @@ combine several futures and track when the last or first of them completes. A ready QFuture object with a value or a QFuture object holding exception can - be created using convenience functions QtFuture::makeReadyFuture(), - QtFuture::makeReadyRangeFuture(), and QtFuture::makeExceptionalFuture(). + be created using convenience functions QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(), and + QtFuture::makeExceptionalFuture(). \note To start a computation and store results in a QFuture, use QPromise or one of the APIs in the \l {Qt Concurrent} framework. - \sa QPromise, QtFuture::connect(), QtFuture::makeReadyFuture(), - QtFuture::makeReadyRangeFuture(), QtFuture::makeExceptionalFuture(), - QFutureWatcher, {Qt Concurrent} + \sa QPromise, QtFuture::connect(), QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(), + QtFuture::makeExceptionalFuture(), QFutureWatcher, {Qt Concurrent} */ /*! \fn template QFuture::QFuture() @@ -982,7 +983,9 @@ const int result = *f.takeResult(); // result == 42 \endcode - \sa QFuture, QtFuture::makeExceptionalFuture() + \sa QFuture, QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(), + QtFuture::makeExceptionalFuture() */ /*! \fn QFuture QtFuture::makeReadyFuture() @@ -1003,7 +1006,9 @@ \endcode \sa QFuture, QFuture::isStarted(), QFuture::isRunning(), - QFuture::isFinished(), QtFuture::makeExceptionalFuture() + QFuture::isFinished(), QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(), + QtFuture::makeExceptionalFuture() */ /*! \fn template static QFuture QtFuture::makeReadyFuture(const QList &values) @@ -1021,7 +1026,38 @@ const auto results = f.results(); // results == { 1, 2, 3 } \endcode - \sa QFuture, QtFuture::makeExceptionalFuture() + \sa QFuture, QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(), + QtFuture::makeExceptionalFuture() +*/ + +/*! \fn template static QFuture> QtFuture::makeReadyValueFuture(T &&value) + + \since 6.6 + + Creates and returns a QFuture which already has a result \a value. + The returned QFuture has a type of std::decay_t, where T is not void. + The returned QFuture will already be in the finished state. + + \snippet code/src_corelib_thread_qfuture.cpp 35 + + \sa QFuture, QtFuture::makeReadyRangeFuture(), + QtFuture::makeReadyVoidFuture(), QtFuture::makeExceptionalFuture() +*/ + +/*! \fn QFuture QtFuture::makeReadyVoidFuture() + + \since 6.6 + + Creates and returns a void QFuture. Such QFuture can't store any result. + One can use it to query the state of the computation. + The returned QFuture will already be in the finished state. + + \snippet code/src_corelib_thread_qfuture.cpp 36 + + \sa QFuture, QFuture::isStarted(), QFuture::isRunning(), + QFuture::isFinished(), QtFuture::makeReadyValueFuture(), + QtFuture::makeReadyRangeFuture(), QtFuture::makeExceptionalFuture() */ /*! \fn template static QFuture QtFuture::makeExceptionalFuture(const QException &exception) @@ -1041,7 +1077,8 @@ } \endcode - \sa QFuture, QException, QtFuture::makeReadyFuture() + \sa QFuture, QException, QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture() */ /*! \fn template static QFuture QtFuture::makeExceptionalFuture(std::exception_ptr exception) @@ -1066,7 +1103,8 @@ } \endcode - \sa QFuture, QException, QtFuture::makeReadyFuture() + \sa QFuture, QException, QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture() */ /*! \fn template> static QFuture> QtFuture::makeReadyRangeFuture(Container &&container) @@ -1085,7 +1123,8 @@ \dots \snippet code/src_corelib_thread_qfuture.cpp 34 - \sa QFuture, QtFuture::makeReadyFuture(), QtFuture::makeExceptionalFuture() + \sa QFuture, QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture(), QtFuture::makeExceptionalFuture() */ /*! \fn template static QFuture QtFuture::makeReadyRangeFuture(std::initializer_list values) @@ -1100,7 +1139,8 @@ \dots \snippet code/src_corelib_thread_qfuture.cpp 34 - \sa QFuture, QtFuture::makeReadyFuture(), QtFuture::makeExceptionalFuture() + \sa QFuture, QtFuture::makeReadyVoidFuture(), + QtFuture::makeReadyValueFuture(), QtFuture::makeExceptionalFuture() */ /*! \fn template template QFuture::ResultType> QFuture::then(Function &&function) diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index 5ed7a4d27f..b282903405 100644 --- a/src/corelib/thread/qfuture_impl.h +++ b/src/corelib/thread/qfuture_impl.h @@ -996,8 +996,8 @@ static QFuture makeReadyRangeFuture(std::initializer_list return QtPrivate::makeReadyRangeFutureImpl(QList{values}); } -template> -static QFuture> makeReadyFuture(T &&value) +template +static QFuture> makeReadyValueFuture(T &&value) { QFutureInterface> promise; promise.reportStarted(); @@ -1007,20 +1007,17 @@ static QFuture> makeReadyFuture(T &&value) return promise.future(); } -#if defined(Q_QDOC) -static QFuture makeReadyFuture() -#else -template -static QFuture makeReadyFuture() -#endif -{ - QFutureInterface promise; - promise.reportStarted(); - promise.reportFinished(); +Q_CORE_EXPORT QFuture makeReadyVoidFuture(); // implemented in qfutureinterface.cpp - return promise.future(); +template> +static QFuture> makeReadyFuture(T &&value) +{ + return makeReadyValueFuture(std::forward(value)); } +// the void specialization is moved to the end of qfuture.h, because it now +// uses makeReadyVoidFuture() and required QFuture to be defined. + template static QFuture makeReadyFuture(const QList &values) { @@ -1127,7 +1124,7 @@ QFuture whenAllImpl(InputIt first, InputIt last) { const qsizetype size = std::distance(first, last); if (size == 0) - return QtFuture::makeReadyFuture(OutputSequence()); + return QtFuture::makeReadyValueFuture(OutputSequence()); const auto context = std::make_shared>(size); context->futures.resize(size); @@ -1166,7 +1163,7 @@ QFuture::type>> whenAnyImpl(I const qsizetype size = std::distance(first, last); if (size == 0) { - return QtFuture::makeReadyFuture( + return QtFuture::makeReadyValueFuture( QtFuture::WhenAnyResult { qsizetype(-1), QFuture() }); } diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index e656527e04..790963547c 100644 --- a/src/corelib/thread/qfutureinterface.cpp +++ b/src/corelib/thread/qfutureinterface.cpp @@ -889,4 +889,17 @@ bool QFutureInterfaceBase::launchAsync() const return d->launchAsync; } +namespace QtFuture { + +QFuture makeReadyVoidFuture() +{ + QFutureInterface promise; + promise.reportStarted(); + promise.reportFinished(); + + return promise.future(); +} + +} // namespace QtFuture + QT_END_NAMESPACE diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index f457ba4668..bb78898836 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -3052,7 +3052,7 @@ void tst_QFuture::cancelContinuations() // The chain is cancelled before the execution of continuations { - auto f = QtFuture::makeReadyFuture(42); + auto f = QtFuture::makeReadyValueFuture(42); f.cancel(); int checkpoint = 0; @@ -3301,7 +3301,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda() // .then() { std::unique_ptr uniquePtr(new int(42)); - auto future = QtFuture::makeReadyFuture().then([p = std::move(uniquePtr)] { return *p; }); + auto future = QtFuture::makeReadyVoidFuture() + .then([p = std::move(uniquePtr)] { return *p; }); QCOMPARE(future.result(), 42); } // .then() with thread pool @@ -3309,8 +3310,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda() QThreadPool pool; std::unique_ptr uniquePtr(new int(42)); - auto future = - QtFuture::makeReadyFuture().then(&pool, [p = std::move(uniquePtr)] { return *p; }); + auto future = QtFuture::makeReadyVoidFuture() + .then(&pool, [p = std::move(uniquePtr)] { return *p; }); QCOMPARE(future.result(), 42); } // .then() with context @@ -3318,8 +3319,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda() QObject object; std::unique_ptr uniquePtr(new int(42)); - auto future = QtFuture::makeReadyFuture().then(&object, - [p = std::move(uniquePtr)] { return *p; }); + auto future = QtFuture::makeReadyVoidFuture() + .then(&object, [p = std::move(uniquePtr)] { return *p; }); QCOMPARE(future.result(), 42); } @@ -4113,6 +4114,28 @@ void tst_QFuture::createReadyFutures() QCOMPARE(f.results(), values); } + // test makeReadyValueFuture() + { + const int val = 42; + auto f = QtFuture::makeReadyValueFuture(val); + QCOMPARE_EQ(f.result(), val); + + int otherVal = 42; + f = QtFuture::makeReadyValueFuture(otherVal); + QCOMPARE_EQ(f.result(), otherVal); + } + { + auto f = QtFuture::makeReadyValueFuture(std::make_unique(42)); + QCOMPARE(*f.takeResult(), 42); + } + // test makeReadyVoidFuture() + { + auto f = QtFuture::makeReadyVoidFuture(); + QVERIFY(f.isStarted()); + QVERIFY(!f.isRunning()); + QVERIFY(f.isFinished()); + } + #ifndef QT_NO_EXCEPTIONS // using QException { @@ -4236,7 +4259,7 @@ void tst_QFuture::createReadyFutures() void tst_QFuture::getFutureInterface() { const int val = 42; - QFuture f = QtFuture::makeReadyFuture(val); + QFuture f = QtFuture::makeReadyValueFuture(val); auto interface = QFutureInterfaceBase::get(f); QCOMPARE(interface.resultCount(), 1); @@ -4250,7 +4273,7 @@ void tst_QFuture::convertQMetaType() QVERIFY(QMetaType::canConvert(intType, voidType)); const int val = 42; - QFuture f = QtFuture::makeReadyFuture(val); + QFuture f = QtFuture::makeReadyValueFuture(val); auto variant = QVariant::fromValue(f); QVERIFY(variant.convert(voidType)); diff --git a/tests/benchmarks/corelib/thread/qfuture/tst_bench_qfuture.cpp b/tests/benchmarks/corelib/thread/qfuture/tst_bench_qfuture.cpp index 7c0e3b2fcf..74dd549462 100644 --- a/tests/benchmarks/corelib/thread/qfuture/tst_bench_qfuture.cpp +++ b/tests/benchmarks/corelib/thread/qfuture/tst_bench_qfuture.cpp @@ -13,7 +13,7 @@ class tst_QFuture : public QObject Q_OBJECT private slots: - void makeReadyfuture(); + void makeReadyValueFuture(); #ifndef QT_NO_EXCEPTIONS void makeExceptionalFuture(); #endif @@ -43,10 +43,10 @@ private slots: void progressText(); }; -void tst_QFuture::makeReadyfuture() +void tst_QFuture::makeReadyValueFuture() { QBENCHMARK { - auto future = QtFuture::makeReadyFuture(42); + auto future = QtFuture::makeReadyValueFuture(42); Q_UNUSED(future); } } @@ -64,7 +64,7 @@ void tst_QFuture::makeExceptionalFuture() void tst_QFuture::result() { - auto future = QtFuture::makeReadyFuture(42); + auto future = QtFuture::makeReadyValueFuture(42); QBENCHMARK { auto value = future.result(); @@ -92,7 +92,7 @@ void tst_QFuture::results() void tst_QFuture::takeResult() { QBENCHMARK { - auto future = QtFuture::makeReadyFuture(42); + auto future = QtFuture::makeReadyValueFuture(42); auto value = future.takeResult(); Q_UNUSED(value); } @@ -140,7 +140,7 @@ void tst_QFuture::reportException() void tst_QFuture::then() { - auto f = QtFuture::makeReadyFuture(42); + auto f = QtFuture::makeReadyValueFuture(42); QBENCHMARK { auto future = f.then([](int value) { return value; }); Q_UNUSED(future); @@ -149,7 +149,7 @@ void tst_QFuture::then() void tst_QFuture::thenVoid() { - auto f = QtFuture::makeReadyFuture(); + auto f = QtFuture::makeReadyVoidFuture(); QBENCHMARK { auto future = f.then([] {}); Q_UNUSED(future); @@ -205,7 +205,7 @@ void tst_QFuture::onFailedVoid() void tst_QFuture::thenOnFailed() { - auto f = QtFuture::makeReadyFuture(42); + auto f = QtFuture::makeReadyValueFuture(42); QBENCHMARK { auto future = f.then([](int) { throw std::runtime_error("error"); }).onFailed([] { return 0; }); @@ -215,7 +215,7 @@ void tst_QFuture::thenOnFailed() void tst_QFuture::thenOnFailedVoid() { - auto f = QtFuture::makeReadyFuture(); + auto f = QtFuture::makeReadyVoidFuture(); QBENCHMARK { auto future = f.then([] { throw std::runtime_error("error"); }).onFailed([] {}); Q_UNUSED(future);