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<T> &"
specialization returning QFuture<T> instead of QFuture<QList<T>>,
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 <marc.mutz@qt.io>
This commit is contained in:
Ivan Solovev 2023-03-20 16:11:15 +01:00
parent 7f38f9f394
commit 9b4b32ec98
8 changed files with 147 additions and 50 deletions

View File

@ -239,9 +239,9 @@ auto future = QtConcurrent::run([] {
//! [20] //! [20]
QObject *context = ...; QObject *context = ...;
auto future = cachedResultsReady ? QtFuture::makeReadyFuture(results) auto future = cachedResultsReady ? QtFuture::makeReadyValueFuture(result)
: QtConcurrent::run([] { /* compute results */}); : QtConcurrent::run([] { /* compute result */});
auto continuation = future.then(context, [] (Results results) { auto continuation = future.then(context, [] (Result result) {
// Runs in the context's thread // Runs in the context's thread
}).then([] { }).then([] {
// May or may not run in the context's thread // 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 int count = f.resultCount(); // count == 3
const auto results = f.results(); // results == { 1, 2, 3 } const auto results = f.results(); // results == { 1, 2, 3 }
//! [34] //! [34]
//! [35]
auto f = QtFuture::makeReadyValueFuture(std::make_unique<int>(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]

View File

@ -1095,7 +1095,7 @@ requestPermissionsInternal(const QStringList &permissions)
} }
if (!QtAndroidPrivate::acquireAndroidDeadlockProtector()) if (!QtAndroidPrivate::acquireAndroidDeadlockProtector())
return QtFuture::makeReadyFuture(QtAndroidPrivate::Denied); return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>> promise; QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>> promise;
promise.reset(new QPromise<QtAndroidPrivate::PermissionResult>()); promise.reset(new QPromise<QtAndroidPrivate::PermissionResult>());
@ -1145,7 +1145,7 @@ QtAndroidPrivate::requestPermissions(const QStringList &permissions)
{ {
// avoid the uneccessary call and response to an empty permission string // avoid the uneccessary call and response to an empty permission string
if (permissions.isEmpty()) if (permissions.isEmpty())
return QtFuture::makeReadyFuture(QtAndroidPrivate::Denied); return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
return requestPermissionsInternal(permissions); return requestPermissionsInternal(permissions);
} }
@ -1168,7 +1168,7 @@ QtAndroidPrivate::checkPermission(const QString &permission)
QJniObject::fromString(permission).object()); QJniObject::fromString(permission).object());
result = resultFromAndroid(res); result = resultFromAndroid(res);
} }
return QtFuture::makeReadyFuture(result); return QtFuture::makeReadyValueFuture(result);
} }
bool QtAndroidPrivate::registerPermissionNatives() bool QtAndroidPrivate::registerPermissionNatives()

View File

@ -522,6 +522,16 @@ QFuture<std::variant<std::decay_t<Futures>...>> whenAny(Futures &&... futures);
#endif // Q_QDOC #endif // Q_QDOC
#if defined(Q_QDOC)
static QFuture<void> makeReadyFuture()
#else
template<typename T = void>
static QFuture<T> makeReadyFuture()
#endif
{
return makeReadyVoidFuture();
}
} // namespace QtFuture } // namespace QtFuture
Q_DECLARE_SEQUENTIAL_ITERATOR(Future) Q_DECLARE_SEQUENTIAL_ITERATOR(Future)

View File

@ -118,15 +118,16 @@
combine several futures and track when the last or first of them completes. 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 A ready QFuture object with a value or a QFuture object holding exception can
be created using convenience functions QtFuture::makeReadyFuture(), be created using convenience functions QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyRangeFuture(), and QtFuture::makeExceptionalFuture(). QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(), and
QtFuture::makeExceptionalFuture().
\note To start a computation and store results in a QFuture, use QPromise or \note To start a computation and store results in a QFuture, use QPromise or
one of the APIs in the \l {Qt Concurrent} framework. one of the APIs in the \l {Qt Concurrent} framework.
\sa QPromise, QtFuture::connect(), QtFuture::makeReadyFuture(), \sa QPromise, QtFuture::connect(), QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyRangeFuture(), QtFuture::makeExceptionalFuture(), QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
QFutureWatcher, {Qt Concurrent} QtFuture::makeExceptionalFuture(), QFutureWatcher, {Qt Concurrent}
*/ */
/*! \fn template <typename T> QFuture<T>::QFuture() /*! \fn template <typename T> QFuture<T>::QFuture()
@ -982,7 +983,9 @@
const int result = *f.takeResult(); // result == 42 const int result = *f.takeResult(); // result == 42
\endcode \endcode
\sa QFuture, QtFuture::makeExceptionalFuture() \sa QFuture, QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
QtFuture::makeExceptionalFuture()
*/ */
/*! \fn QFuture<void> QtFuture::makeReadyFuture() /*! \fn QFuture<void> QtFuture::makeReadyFuture()
@ -1003,7 +1006,9 @@
\endcode \endcode
\sa QFuture, QFuture::isStarted(), QFuture::isRunning(), \sa QFuture, QFuture::isStarted(), QFuture::isRunning(),
QFuture::isFinished(), QtFuture::makeExceptionalFuture() QFuture::isFinished(), QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
QtFuture::makeExceptionalFuture()
*/ */
/*! \fn template<typename T> static QFuture<T> QtFuture::makeReadyFuture(const QList<T> &values) /*! \fn template<typename T> static QFuture<T> QtFuture::makeReadyFuture(const QList<T> &values)
@ -1021,7 +1026,38 @@
const auto results = f.results(); // results == { 1, 2, 3 } const auto results = f.results(); // results == { 1, 2, 3 }
\endcode \endcode
\sa QFuture, QtFuture::makeExceptionalFuture() \sa QFuture, QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyValueFuture(), QtFuture::makeReadyRangeFuture(),
QtFuture::makeExceptionalFuture()
*/
/*! \fn template<typename T> static QFuture<std::decay_t<T>> 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<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<void> 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<typename T> static QFuture<T> QtFuture::makeExceptionalFuture(const QException &exception) /*! \fn template<typename T> static QFuture<T> QtFuture::makeExceptionalFuture(const QException &exception)
@ -1041,7 +1077,8 @@
} }
\endcode \endcode
\sa QFuture, QException, QtFuture::makeReadyFuture() \sa QFuture, QException, QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyValueFuture()
*/ */
/*! \fn template<typename T> static QFuture<T> QtFuture::makeExceptionalFuture(std::exception_ptr exception) /*! \fn template<typename T> static QFuture<T> QtFuture::makeExceptionalFuture(std::exception_ptr exception)
@ -1066,7 +1103,8 @@
} }
\endcode \endcode
\sa QFuture, QException, QtFuture::makeReadyFuture() \sa QFuture, QException, QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyValueFuture()
*/ */
/*! \fn template<typename Container, QtFuture::if_container_with_input_iterators<Container>> static QFuture<QtFuture::ContainedType<Container>> QtFuture::makeReadyRangeFuture(Container &&container) /*! \fn template<typename Container, QtFuture::if_container_with_input_iterators<Container>> static QFuture<QtFuture::ContainedType<Container>> QtFuture::makeReadyRangeFuture(Container &&container)
@ -1085,7 +1123,8 @@
\dots \dots
\snippet code/src_corelib_thread_qfuture.cpp 34 \snippet code/src_corelib_thread_qfuture.cpp 34
\sa QFuture, QtFuture::makeReadyFuture(), QtFuture::makeExceptionalFuture() \sa QFuture, QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyValueFuture(), QtFuture::makeExceptionalFuture()
*/ */
/*! \fn template<typename ValueType> static QFuture<ValueType> QtFuture::makeReadyRangeFuture(std::initializer_list<ValueType> values) /*! \fn template<typename ValueType> static QFuture<ValueType> QtFuture::makeReadyRangeFuture(std::initializer_list<ValueType> values)
@ -1100,7 +1139,8 @@
\dots \dots
\snippet code/src_corelib_thread_qfuture.cpp 34 \snippet code/src_corelib_thread_qfuture.cpp 34
\sa QFuture, QtFuture::makeReadyFuture(), QtFuture::makeExceptionalFuture() \sa QFuture, QtFuture::makeReadyVoidFuture(),
QtFuture::makeReadyValueFuture(), QtFuture::makeExceptionalFuture()
*/ */
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(Function &&function) /*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(Function &&function)

View File

@ -996,8 +996,8 @@ static QFuture<ValueType> makeReadyRangeFuture(std::initializer_list<ValueType>
return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{values}); return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{values});
} }
template<typename T, typename = QtPrivate::EnableForNonVoid<T>> template<typename T>
static QFuture<std::decay_t<T>> makeReadyFuture(T &&value) static QFuture<std::decay_t<T>> makeReadyValueFuture(T &&value)
{ {
QFutureInterface<std::decay_t<T>> promise; QFutureInterface<std::decay_t<T>> promise;
promise.reportStarted(); promise.reportStarted();
@ -1007,20 +1007,17 @@ static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
return promise.future(); return promise.future();
} }
#if defined(Q_QDOC) Q_CORE_EXPORT QFuture<void> makeReadyVoidFuture(); // implemented in qfutureinterface.cpp
static QFuture<void> makeReadyFuture()
#else
template<typename T = void>
static QFuture<T> makeReadyFuture()
#endif
{
QFutureInterface<T> promise;
promise.reportStarted();
promise.reportFinished();
return promise.future(); template<typename T, typename = QtPrivate::EnableForNonVoid<T>>
static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
{
return makeReadyValueFuture(std::forward<T>(value));
} }
// the void specialization is moved to the end of qfuture.h, because it now
// uses makeReadyVoidFuture() and required QFuture<void> to be defined.
template<typename T> template<typename T>
static QFuture<T> makeReadyFuture(const QList<T> &values) static QFuture<T> makeReadyFuture(const QList<T> &values)
{ {
@ -1127,7 +1124,7 @@ QFuture<OutputSequence> whenAllImpl(InputIt first, InputIt last)
{ {
const qsizetype size = std::distance(first, last); const qsizetype size = std::distance(first, last);
if (size == 0) if (size == 0)
return QtFuture::makeReadyFuture(OutputSequence()); return QtFuture::makeReadyValueFuture(OutputSequence());
const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size); const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
context->futures.resize(size); context->futures.resize(size);
@ -1166,7 +1163,7 @@ QFuture<QtFuture::WhenAnyResult<typename Future<ValueType>::type>> whenAnyImpl(I
const qsizetype size = std::distance(first, last); const qsizetype size = std::distance(first, last);
if (size == 0) { if (size == 0) {
return QtFuture::makeReadyFuture( return QtFuture::makeReadyValueFuture(
QtFuture::WhenAnyResult { qsizetype(-1), QFuture<PackagedType>() }); QtFuture::WhenAnyResult { qsizetype(-1), QFuture<PackagedType>() });
} }

View File

@ -889,4 +889,17 @@ bool QFutureInterfaceBase::launchAsync() const
return d->launchAsync; return d->launchAsync;
} }
namespace QtFuture {
QFuture<void> makeReadyVoidFuture()
{
QFutureInterface<void> promise;
promise.reportStarted();
promise.reportFinished();
return promise.future();
}
} // namespace QtFuture
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -3052,7 +3052,7 @@ void tst_QFuture::cancelContinuations()
// The chain is cancelled before the execution of continuations // The chain is cancelled before the execution of continuations
{ {
auto f = QtFuture::makeReadyFuture(42); auto f = QtFuture::makeReadyValueFuture(42);
f.cancel(); f.cancel();
int checkpoint = 0; int checkpoint = 0;
@ -3301,7 +3301,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda()
// .then() // .then()
{ {
std::unique_ptr<int> uniquePtr(new int(42)); std::unique_ptr<int> 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); QCOMPARE(future.result(), 42);
} }
// .then() with thread pool // .then() with thread pool
@ -3309,8 +3310,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda()
QThreadPool pool; QThreadPool pool;
std::unique_ptr<int> uniquePtr(new int(42)); std::unique_ptr<int> uniquePtr(new int(42));
auto future = auto future = QtFuture::makeReadyVoidFuture()
QtFuture::makeReadyFuture().then(&pool, [p = std::move(uniquePtr)] { return *p; }); .then(&pool, [p = std::move(uniquePtr)] { return *p; });
QCOMPARE(future.result(), 42); QCOMPARE(future.result(), 42);
} }
// .then() with context // .then() with context
@ -3318,8 +3319,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda()
QObject object; QObject object;
std::unique_ptr<int> uniquePtr(new int(42)); std::unique_ptr<int> uniquePtr(new int(42));
auto future = QtFuture::makeReadyFuture().then(&object, auto future = QtFuture::makeReadyVoidFuture()
[p = std::move(uniquePtr)] { return *p; }); .then(&object, [p = std::move(uniquePtr)] { return *p; });
QCOMPARE(future.result(), 42); QCOMPARE(future.result(), 42);
} }
@ -4113,6 +4114,28 @@ void tst_QFuture::createReadyFutures()
QCOMPARE(f.results(), values); QCOMPARE(f.results(), values);
} }
// test makeReadyValueFuture<T>()
{
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<int>(42));
QCOMPARE(*f.takeResult(), 42);
}
// test makeReadyVoidFuture()
{
auto f = QtFuture::makeReadyVoidFuture();
QVERIFY(f.isStarted());
QVERIFY(!f.isRunning());
QVERIFY(f.isFinished());
}
#ifndef QT_NO_EXCEPTIONS #ifndef QT_NO_EXCEPTIONS
// using QException // using QException
{ {
@ -4236,7 +4259,7 @@ void tst_QFuture::createReadyFutures()
void tst_QFuture::getFutureInterface() void tst_QFuture::getFutureInterface()
{ {
const int val = 42; const int val = 42;
QFuture<int> f = QtFuture::makeReadyFuture(val); QFuture<int> f = QtFuture::makeReadyValueFuture(val);
auto interface = QFutureInterfaceBase::get(f); auto interface = QFutureInterfaceBase::get(f);
QCOMPARE(interface.resultCount(), 1); QCOMPARE(interface.resultCount(), 1);
@ -4250,7 +4273,7 @@ void tst_QFuture::convertQMetaType()
QVERIFY(QMetaType::canConvert(intType, voidType)); QVERIFY(QMetaType::canConvert(intType, voidType));
const int val = 42; const int val = 42;
QFuture<int> f = QtFuture::makeReadyFuture(val); QFuture<int> f = QtFuture::makeReadyValueFuture(val);
auto variant = QVariant::fromValue(f); auto variant = QVariant::fromValue(f);
QVERIFY(variant.convert(voidType)); QVERIFY(variant.convert(voidType));

View File

@ -13,7 +13,7 @@ class tst_QFuture : public QObject
Q_OBJECT Q_OBJECT
private slots: private slots:
void makeReadyfuture(); void makeReadyValueFuture();
#ifndef QT_NO_EXCEPTIONS #ifndef QT_NO_EXCEPTIONS
void makeExceptionalFuture(); void makeExceptionalFuture();
#endif #endif
@ -43,10 +43,10 @@ private slots:
void progressText(); void progressText();
}; };
void tst_QFuture::makeReadyfuture() void tst_QFuture::makeReadyValueFuture()
{ {
QBENCHMARK { QBENCHMARK {
auto future = QtFuture::makeReadyFuture(42); auto future = QtFuture::makeReadyValueFuture(42);
Q_UNUSED(future); Q_UNUSED(future);
} }
} }
@ -64,7 +64,7 @@ void tst_QFuture::makeExceptionalFuture()
void tst_QFuture::result() void tst_QFuture::result()
{ {
auto future = QtFuture::makeReadyFuture(42); auto future = QtFuture::makeReadyValueFuture(42);
QBENCHMARK { QBENCHMARK {
auto value = future.result(); auto value = future.result();
@ -92,7 +92,7 @@ void tst_QFuture::results()
void tst_QFuture::takeResult() void tst_QFuture::takeResult()
{ {
QBENCHMARK { QBENCHMARK {
auto future = QtFuture::makeReadyFuture(42); auto future = QtFuture::makeReadyValueFuture(42);
auto value = future.takeResult(); auto value = future.takeResult();
Q_UNUSED(value); Q_UNUSED(value);
} }
@ -140,7 +140,7 @@ void tst_QFuture::reportException()
void tst_QFuture::then() void tst_QFuture::then()
{ {
auto f = QtFuture::makeReadyFuture(42); auto f = QtFuture::makeReadyValueFuture(42);
QBENCHMARK { QBENCHMARK {
auto future = f.then([](int value) { return value; }); auto future = f.then([](int value) { return value; });
Q_UNUSED(future); Q_UNUSED(future);
@ -149,7 +149,7 @@ void tst_QFuture::then()
void tst_QFuture::thenVoid() void tst_QFuture::thenVoid()
{ {
auto f = QtFuture::makeReadyFuture(); auto f = QtFuture::makeReadyVoidFuture();
QBENCHMARK { QBENCHMARK {
auto future = f.then([] {}); auto future = f.then([] {});
Q_UNUSED(future); Q_UNUSED(future);
@ -205,7 +205,7 @@ void tst_QFuture::onFailedVoid()
void tst_QFuture::thenOnFailed() void tst_QFuture::thenOnFailed()
{ {
auto f = QtFuture::makeReadyFuture(42); auto f = QtFuture::makeReadyValueFuture(42);
QBENCHMARK { QBENCHMARK {
auto future = auto future =
f.then([](int) { throw std::runtime_error("error"); }).onFailed([] { return 0; }); f.then([](int) { throw std::runtime_error("error"); }).onFailed([] { return 0; });
@ -215,7 +215,7 @@ void tst_QFuture::thenOnFailed()
void tst_QFuture::thenOnFailedVoid() void tst_QFuture::thenOnFailedVoid()
{ {
auto f = QtFuture::makeReadyFuture(); auto f = QtFuture::makeReadyVoidFuture();
QBENCHMARK { QBENCHMARK {
auto future = f.then([] { throw std::runtime_error("error"); }).onFailed([] {}); auto future = f.then([] { throw std::runtime_error("error"); }).onFailed([] {});
Q_UNUSED(future); Q_UNUSED(future);