Make continuations work with move-only types

Use the move-only versions of result reporting and getting operations,
if the type of QFuture is not copyable.

Task-number: QTBUG-81941
Change-Id: Ic9fa978380e2c24e190e68d974051a650b0e5571
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Vitaly Fanaskov <vitaly.fanaskov@qt.io>
This commit is contained in:
Sona Kurazyan 2020-04-15 11:44:46 +02:00
parent 3eed6d76b7
commit a7264d9b8c
2 changed files with 136 additions and 27 deletions

View File

@ -186,6 +186,14 @@ public:
static void create(Function &&func, QFuture<ParentResultType> *f,
QFutureInterface<ResultType> &p, QThreadPool *pool);
private:
void fulfillPromiseWithResult();
void fulfillVoidPromise();
void fulfillPromiseWithVoidResult();
template<class... Args>
void fulfillPromise(Args &&... args);
protected:
virtual void runImpl() = 0;
@ -193,7 +201,7 @@ protected:
protected:
QFutureInterface<ResultType> promise;
const QFuture<ParentResultType> parentFuture;
QFuture<ParentResultType> parentFuture;
Function function;
};
@ -269,7 +277,7 @@ private:
private:
QFutureInterface<ResultType> promise;
const QFuture<ResultType> parentFuture;
QFuture<ResultType> parentFuture;
Function handler;
};
@ -286,32 +294,31 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
try {
#endif
if constexpr (!std::is_void_v<ResultType>) {
if constexpr (std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
promise.reportResult(function(parentFuture));
} else if constexpr (std::is_void_v<ParentResultType>) {
promise.reportResult(function());
if constexpr (std::is_void_v<ParentResultType>) {
fulfillPromiseWithVoidResult();
} else if constexpr (std::is_invocable_v<Function, ParentResultType>) {
fulfillPromiseWithResult();
} else {
// This assert normally should never fail, this is to make sure
// that nothing unexpected happend.
static_assert(
std::is_invocable_v<std::decay_t<Function>, std::decay_t<ParentResultType>>,
"The continuation is not invocable with the provided arguments");
promise.reportResult(function(parentFuture.result()));
static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>,
"The continuation is not invocable with the provided arguments");
fulfillPromise(parentFuture);
}
} else {
if constexpr (std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
function(parentFuture);
} else if constexpr (std::is_void_v<ParentResultType>) {
function();
if constexpr (std::is_void_v<ParentResultType>) {
if constexpr (std::is_invocable_v<Function, QFuture<void>>)
function(parentFuture);
else
function();
} else if constexpr (std::is_invocable_v<Function, ParentResultType>) {
fulfillVoidPromise();
} else {
// This assert normally should never fail, this is to make sure
// that nothing unexpected happend.
static_assert(
std::is_invocable_v<std::decay_t<Function>, std::decay_t<ParentResultType>>,
"The continuation is not invocable with the provided arguments");
function(parentFuture.result());
static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>,
"The continuation is not invocable with the provided arguments");
function(parentFuture);
}
}
#ifndef QT_NO_EXCEPTIONS
@ -426,6 +433,43 @@ void Continuation<Function, ResultType, ParentResultType>::create(Function &&fun
f->d.setContinuation(continuation);
}
template<typename Function, typename ResultType, typename ParentResultType>
void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithResult()
{
if constexpr (std::is_copy_constructible_v<ParentResultType>)
fulfillPromise(parentFuture.result());
else
fulfillPromise(parentFuture.takeResult());
}
template<typename Function, typename ResultType, typename ParentResultType>
void Continuation<Function, ResultType, ParentResultType>::fulfillVoidPromise()
{
if constexpr (std::is_copy_constructible_v<ParentResultType>)
function(parentFuture.result());
else
function(parentFuture.takeResult());
}
template<typename Function, typename ResultType, typename ParentResultType>
void Continuation<Function, ResultType, ParentResultType>::fulfillPromiseWithVoidResult()
{
if constexpr (std::is_invocable_v<Function, QFuture<void>>)
fulfillPromise(parentFuture);
else
fulfillPromise();
}
template<typename Function, typename ResultType, typename ParentResultType>
template<class... Args>
void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args)
{
if constexpr (std::is_copy_constructible_v<ResultType>)
promise.reportResult(std::invoke(function, std::forward<Args>(args)...));
else
promise.reportAndMoveResult(std::invoke(function, std::forward<Args>(args)...));
}
#ifndef QT_NO_EXCEPTIONS
template<class Function, class ResultType>
@ -460,8 +504,12 @@ void FailureHandler<Function, ResultType>::run()
handleException<ArgType>();
}
} else {
if constexpr (!std::is_void_v<ResultType>)
promise.reportResult(parentFuture.result());
if constexpr (!std::is_void_v<ResultType>) {
if constexpr (std::is_copy_constructible_v<ResultType>)
promise.reportResult(parentFuture.result());
else
promise.reportAndMoveResult(parentFuture.takeResult());
}
}
promise.reportFinished();
}
@ -478,7 +526,10 @@ void FailureHandler<Function, ResultType>::handleException()
if constexpr (std::is_void_v<ResultType>) {
handler(e);
} else {
promise.reportResult(handler(e));
if constexpr (std::is_copy_constructible_v<ResultType>)
promise.reportResult(handler(e));
else
promise.reportAndMoveResult(handler(e));
}
} catch (...) {
promise.reportException(std::current_exception());
@ -497,11 +548,12 @@ void FailureHandler<Function, ResultType>::handleAllExceptions()
parentFuture.d.exceptionStore().throwPossibleException();
} catch (...) {
try {
if constexpr (std::is_void_v<ResultType>) {
if constexpr (std::is_void_v<ResultType>)
handler();
} else {
else if constexpr (std::is_copy_constructible_v<ResultType>)
promise.reportResult(handler());
}
else
promise.reportAndMoveResult(handler());
} catch (...) {
promise.reportException(std::current_exception());
}

View File

@ -71,6 +71,8 @@ private:
std::function<void ()> m_fn;
};
using UniquePtr = std::unique_ptr<int>;
class tst_QFuture: public QObject
{
Q_OBJECT
@ -100,12 +102,14 @@ private slots:
void nonGlobalThreadPool();
void then();
void thenForMoveOnlyTypes();
void thenOnCanceledFuture();
#ifndef QT_NO_EXCEPTIONS
void thenOnExceptionFuture();
void thenThrows();
void onFailed();
void onFailedTestCallables();
void onFailedForMoveOnlyTypes();
#endif
void takeResults();
void takeResult();
@ -114,7 +118,6 @@ private slots:
void resultsReadyAt();
private:
using size_type = std::vector<int>::size_type;
using UniquePtr = std::unique_ptr<int>;
static void testSingleResult(const UniquePtr &p);
static void testSingleResult(const std::vector<int> &v);
@ -1874,6 +1877,38 @@ void tst_QFuture::then()
}
}
template<class Type, class Callable>
bool runThenForMoveOnly(Callable &&callable)
{
QFutureInterface<Type> promise;
auto future = promise.future();
auto then = future.then(std::forward<Callable>(callable));
promise.reportStarted();
if constexpr (!std::is_same_v<Type, void>)
promise.reportAndMoveResult(std::make_unique<int>(42));
promise.reportFinished();
then.waitForFinished();
bool success = true;
if constexpr (!std::is_same_v<decltype(then), QFuture<void>>)
success &= *then.takeResult() == 42;
if constexpr (!std::is_same_v<Type, void>)
success &= !future.isValid();
return success;
}
void tst_QFuture::thenForMoveOnlyTypes()
{
QVERIFY(runThenForMoveOnly<UniquePtr>([](UniquePtr res) { return res; }));
QVERIFY(runThenForMoveOnly<UniquePtr>([](UniquePtr res) { Q_UNUSED(res); }));
QVERIFY(runThenForMoveOnly<UniquePtr>([](QFuture<UniquePtr> res) { return res.takeResult(); }));
QVERIFY(runThenForMoveOnly<void>([] { return std::make_unique<int>(42); }));
}
void tst_QFuture::thenOnCanceledFuture()
{
// Continuations on a canceled future
@ -2482,6 +2517,28 @@ void tst_QFuture::onFailedTestCallables()
QVERIFY(runForCallable(Functor3()));
}
template<class Callable>
bool runOnFailedForMoveOnly(Callable &&callable)
{
QFutureInterface<UniquePtr> promise;
auto future = promise.future();
auto failedFuture = future.onFailed(std::forward<Callable>(callable));
promise.reportStarted();
QException e;
promise.reportException(e);
promise.reportFinished();
return *failedFuture.takeResult() == -1;
}
void tst_QFuture::onFailedForMoveOnlyTypes()
{
QVERIFY(runOnFailedForMoveOnly([](const QException &) { return std::make_unique<int>(-1); }));
QVERIFY(runOnFailedForMoveOnly([] { return std::make_unique<int>(-1); }));
}
#endif // QT_NO_EXCEPTIONS
void tst_QFuture::testSingleResult(const UniquePtr &p)