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]
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<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())
return QtFuture::makeReadyFuture(QtAndroidPrivate::Denied);
return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>> promise;
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
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()

View File

@ -522,6 +522,16 @@ QFuture<std::variant<std::decay_t<Futures>...>> whenAny(Futures &&... futures);
#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
Q_DECLARE_SEQUENTIAL_ITERATOR(Future)

View File

@ -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 <typename T> QFuture<T>::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<void> 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<typename T> static QFuture<T> QtFuture::makeReadyFuture(const QList<T> &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<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)
@ -1041,7 +1077,8 @@
}
\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)
@ -1066,7 +1103,8 @@
}
\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)
@ -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<typename ValueType> static QFuture<ValueType> QtFuture::makeReadyRangeFuture(std::initializer_list<ValueType> 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<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});
}
template<typename T, typename = QtPrivate::EnableForNonVoid<T>>
static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
template<typename T>
static QFuture<std::decay_t<T>> makeReadyValueFuture(T &&value)
{
QFutureInterface<std::decay_t<T>> promise;
promise.reportStarted();
@ -1007,20 +1007,17 @@ static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
return promise.future();
}
#if defined(Q_QDOC)
static QFuture<void> makeReadyFuture()
#else
template<typename T = void>
static QFuture<T> makeReadyFuture()
#endif
{
QFutureInterface<T> promise;
promise.reportStarted();
promise.reportFinished();
Q_CORE_EXPORT QFuture<void> makeReadyVoidFuture(); // implemented in qfutureinterface.cpp
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>
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);
if (size == 0)
return QtFuture::makeReadyFuture(OutputSequence());
return QtFuture::makeReadyValueFuture(OutputSequence());
const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(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);
if (size == 0) {
return QtFuture::makeReadyFuture(
return QtFuture::makeReadyValueFuture(
QtFuture::WhenAnyResult { qsizetype(-1), QFuture<PackagedType>() });
}

View File

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

View File

@ -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<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);
}
// .then() with thread pool
@ -3309,8 +3310,8 @@ void tst_QFuture::continuationsWithMoveOnlyLambda()
QThreadPool pool;
std::unique_ptr<int> 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<int> 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<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
// using QException
{
@ -4236,7 +4259,7 @@ void tst_QFuture::createReadyFutures()
void tst_QFuture::getFutureInterface()
{
const int val = 42;
QFuture<int> f = QtFuture::makeReadyFuture(val);
QFuture<int> 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<int> f = QtFuture::makeReadyFuture(val);
QFuture<int> f = QtFuture::makeReadyValueFuture(val);
auto variant = QVariant::fromValue(f);
QVERIFY(variant.convert(voidType));

View File

@ -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);