Use QPromise when creating continuations to avoid memory leaks

Continuations were using QFutureInterface to create and return the
associated future to the user. Attaching a continuation to the returned
future could cause memory leaks (described in an earlier commit). Use a
QPromise when saving the continuation, to make sure that the attached
continuation is cleaned in the destructor of the associated QPromise, so
that it doesn't keep any ref-counted copies to the shared data, thus
preventing it from being deleted.

Task-number: QTBUG-99534
Change-Id: I52d5501292095d41d1e060b7dd140c8e5d01335c
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
(cherry picked from commit 614847eae99bd4b6ce375f9f5572acfb3b513307)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Sona Kurazyan 2022-01-18 13:07:43 +01:00 committed by Qt Cherry-pick Bot
parent d9c417ee98
commit 5ce744e085
2 changed files with 103 additions and 136 deletions

View File

@ -332,9 +332,8 @@ class Continuation
{ {
public: public:
template<typename F = Function> template<typename F = Function>
Continuation(F &&func, const QFuture<ParentResultType> &f, Continuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
const QFutureInterface<ResultType> &p) : promise(std::move(p)), parentFuture(f), function(std::forward<F>(func))
: promise(p), parentFuture(f), function(std::forward<F>(func))
{ {
} }
virtual ~Continuation() = default; virtual ~Continuation() = default;
@ -342,15 +341,15 @@ public:
bool execute(); bool execute();
template<typename F = Function> template<typename F = Function>
static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p, static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
QtFuture::Launch policy); QtFuture::Launch policy);
template<typename F = Function> template<typename F = Function>
static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p, static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
QThreadPool *pool); QThreadPool *pool);
template<typename F = Function> template<typename F = Function>
static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p, static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi,
QObject *context); QObject *context);
private: private:
@ -367,7 +366,7 @@ protected:
void runFunction(); void runFunction();
protected: protected:
QFutureInterface<ResultType> promise; QPromise<ResultType> promise;
QFuture<ParentResultType> parentFuture; QFuture<ParentResultType> parentFuture;
Function function; Function function;
}; };
@ -377,9 +376,9 @@ class SyncContinuation final : public Continuation<Function, ResultType, ParentR
{ {
public: public:
template<typename F = Function> template<typename F = Function>
SyncContinuation(F &&func, const QFuture<ParentResultType> &f, SyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
const QFutureInterface<ResultType> &p) : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f,
: Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, p) std::move(p))
{ {
} }
@ -395,12 +394,12 @@ class AsyncContinuation final : public QRunnable,
{ {
public: public:
template<typename F = Function> template<typename F = Function>
AsyncContinuation(F &&func, const QFuture<ParentResultType> &f, AsyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p,
const QFutureInterface<ResultType> &p, QThreadPool *pool = nullptr) QThreadPool *pool = nullptr)
: Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, p), : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f,
std::move(p)),
threadPool(pool) threadPool(pool)
{ {
this->promise.setRunnable(this);
} }
~AsyncContinuation() override = default; ~AsyncContinuation() override = default;
@ -429,15 +428,15 @@ class FailureHandler
public: public:
template<typename F = Function> template<typename F = Function>
static void create(F &&function, QFuture<ResultType> *future, static void create(F &&function, QFuture<ResultType> *future,
const QFutureInterface<ResultType> &promise); const QFutureInterface<ResultType> &fi);
template<typename F = Function> template<typename F = Function>
static void create(F &&function, QFuture<ResultType> *future, static void create(F &&function, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi,
QFutureInterface<ResultType> &promise, QObject *context); QObject *context);
template<typename F = Function> template<typename F = Function>
FailureHandler(F &&func, const QFuture<ResultType> &f, const QFutureInterface<ResultType> &p) FailureHandler(F &&func, const QFuture<ResultType> &f, QPromise<ResultType> &&p)
: promise(p), parentFuture(f), handler(std::forward<F>(func)) : promise(std::move(p)), parentFuture(f), handler(std::forward<F>(func))
{ {
} }
@ -450,7 +449,7 @@ private:
void handleAllExceptions(); void handleAllExceptions();
private: private:
QFutureInterface<ResultType> promise; QPromise<ResultType> promise;
QFuture<ResultType> parentFuture; QFuture<ResultType> parentFuture;
Function handler; Function handler;
}; };
@ -460,7 +459,7 @@ private:
template<typename Function, typename ResultType, typename ParentResultType> template<typename Function, typename ResultType, typename ParentResultType>
void Continuation<Function, ResultType, ParentResultType>::runFunction() void Continuation<Function, ResultType, ParentResultType>::runFunction()
{ {
promise.reportStarted(); promise.start();
Q_ASSERT(parentFuture.isFinished()); Q_ASSERT(parentFuture.isFinished());
@ -497,10 +496,10 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction()
} }
#ifndef QT_NO_EXCEPTIONS #ifndef QT_NO_EXCEPTIONS
} catch (...) { } catch (...) {
promise.reportException(std::current_exception()); promise.setException(std::current_exception());
} }
#endif #endif
promise.reportFinished(); promise.finish();
} }
template<typename Function, typename ResultType, typename ParentResultType> template<typename Function, typename ResultType, typename ParentResultType>
@ -516,17 +515,17 @@ bool Continuation<Function, ResultType, ParentResultType>::execute()
// the user may want to catch the exception inside the continuation, to not // the user may want to catch the exception inside the continuation, to not
// interrupt the continuation chain, so don't report anything yet. // interrupt the continuation chain, so don't report anything yet.
if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) { if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
promise.reportStarted(); promise.start();
promise.reportException(parentFuture.d.exceptionStore().exception()); promise.setException(parentFuture.d.exceptionStore().exception());
promise.reportFinished(); promise.finish();
return false; return false;
} }
} else } else
#endif #endif
{ {
promise.reportStarted(); promise.start();
promise.reportCanceled(); promise.future().cancel();
promise.reportFinished(); promise.finish();
return false; return false;
} }
} }
@ -550,7 +549,7 @@ template<typename Function, typename ResultType, typename ParentResultType>
template<typename F> template<typename F>
void Continuation<Function, ResultType, ParentResultType>::create(F &&func, void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
QFuture<ParentResultType> *f, QFuture<ParentResultType> *f,
QFutureInterface<ResultType> &p, QFutureInterface<ResultType> &fi,
QtFuture::Launch policy) QtFuture::Launch policy)
{ {
Q_ASSERT(f); Q_ASSERT(f);
@ -564,22 +563,24 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
// If the parent future was using a custom thread pool, inherit it as well. // If the parent future was using a custom thread pool, inherit it as well.
if (launchAsync && f->d.threadPool()) { if (launchAsync && f->d.threadPool()) {
pool = f->d.threadPool(); pool = f->d.threadPool();
p.setThreadPool(pool); fi.setThreadPool(pool);
} }
} }
p.setLaunchAsync(launchAsync); fi.setLaunchAsync(launchAsync);
auto continuation = [func = std::forward<F>(func), p, pool, auto continuation = [func = std::forward<F>(func), fi, promise = QPromise(fi), pool,
launchAsync](const QFutureInterfaceBase &parentData) mutable { launchAsync](const QFutureInterfaceBase &parentData) mutable {
const auto parent = QFutureInterface<ParentResultType>(parentData).future(); const auto parent = QFutureInterface<ParentResultType>(parentData).future();
Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr; Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr;
if (launchAsync) { if (launchAsync) {
continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>( auto asyncJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
std::forward<Function>(func), parent, p, pool); std::forward<Function>(func), parent, std::move(promise), pool);
fi.setRunnable(asyncJob);
continuationJob = asyncJob;
} else { } else {
continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>( continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>(
std::forward<Function>(func), parent, p); std::forward<Function>(func), parent, std::move(promise));
} }
bool isLaunched = continuationJob->execute(); bool isLaunched = continuationJob->execute();
@ -591,29 +592,26 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
continuationJob = nullptr; continuationJob = nullptr;
} }
}; };
if constexpr (!std::is_copy_constructible_v<Function>) f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
else
f->d.setContinuation(std::move(continuation), p.d);
} }
template<typename Function, typename ResultType, typename ParentResultType> template<typename Function, typename ResultType, typename ParentResultType>
template<typename F> template<typename F>
void Continuation<Function, ResultType, ParentResultType>::create(F &&func, void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
QFuture<ParentResultType> *f, QFuture<ParentResultType> *f,
QFutureInterface<ResultType> &p, QFutureInterface<ResultType> &fi,
QThreadPool *pool) QThreadPool *pool)
{ {
Q_ASSERT(f); Q_ASSERT(f);
p.setLaunchAsync(true); fi.setLaunchAsync(true);
p.setThreadPool(pool); fi.setThreadPool(pool);
auto continuation = [func = std::forward<F>(func), p, auto continuation = [func = std::forward<F>(func), promise = QPromise(fi),
pool](const QFutureInterfaceBase &parentData) mutable { pool](const QFutureInterfaceBase &parentData) mutable {
const auto parent = QFutureInterface<ParentResultType>(parentData).future(); const auto parent = QFutureInterface<ParentResultType>(parentData).future();
auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>( auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
std::forward<Function>(func), parent, p, pool); std::forward<Function>(func), parent, std::move(promise), pool);
bool isLaunched = continuationJob->execute(); bool isLaunched = continuationJob->execute();
// If continuation is successfully launched, AsyncContinuation will be deleted // If continuation is successfully launched, AsyncContinuation will be deleted
// by the QThreadPool which has started it. // by the QThreadPool which has started it.
@ -622,37 +620,32 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
continuationJob = nullptr; continuationJob = nullptr;
} }
}; };
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
if constexpr (!std::is_copy_constructible_v<Function>)
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
else
f->d.setContinuation(std::move(continuation), p.d);
} }
template<typename Function, typename ResultType, typename ParentResultType> template<typename Function, typename ResultType, typename ParentResultType>
template<typename F> template<typename F>
void Continuation<Function, ResultType, ParentResultType>::create(F &&func, void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
QFuture<ParentResultType> *f, QFuture<ParentResultType> *f,
QFutureInterface<ResultType> &p, QFutureInterface<ResultType> &fi,
QObject *context) QObject *context)
{ {
Q_ASSERT(f); Q_ASSERT(f);
auto continuation = [func = std::forward<F>(func), p, context = QPointer<QObject>(context)]( auto continuation = [func = std::forward<F>(func), promise = QPromise(fi),
context = QPointer<QObject>(context)](
const QFutureInterfaceBase &parentData) mutable { const QFutureInterfaceBase &parentData) mutable {
Q_ASSERT(context); Q_ASSERT(context);
const auto parent = QFutureInterface<ParentResultType>(parentData).future(); const auto parent = QFutureInterface<ParentResultType>(parentData).future();
QMetaObject::invokeMethod(context, [func = std::forward<F>(func), p, parent]() mutable { QMetaObject::invokeMethod(
context,
[func = std::forward<F>(func), promise = std::move(promise), parent]() mutable {
SyncContinuation<Function, ResultType, ParentResultType> continuationJob( SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
std::forward<Function>(func), parent, p); std::forward<Function>(func), parent, std::move(promise));
continuationJob.execute(); continuationJob.execute();
}); });
}; };
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
if constexpr (!std::is_copy_constructible_v<Function>)
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d);
else
f->d.setContinuation(std::move(continuation), p.d);
} }
template<typename Function, typename ResultType, typename ParentResultType> template<typename Function, typename ResultType, typename ParentResultType>
@ -686,32 +679,27 @@ template<typename Function, typename ResultType, typename ParentResultType>
template<class... Args> template<class... Args>
void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args) void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args)
{ {
if constexpr (std::is_copy_constructible_v<ResultType>) promise.addResult(std::invoke(function, std::forward<Args>(args)...));
promise.reportResult(std::invoke(function, std::forward<Args>(args)...));
else
promise.reportAndMoveResult(std::invoke(function, std::forward<Args>(args)...));
} }
template<class T> template<class T>
void fulfillPromise(QFutureInterface<T> &promise, QFuture<T> &future) void fulfillPromise(QPromise<T> &promise, QFuture<T> &future)
{ {
if constexpr (!std::is_void_v<T>) { if constexpr (!std::is_void_v<T>) {
if constexpr (std::is_copy_constructible_v<T>) if constexpr (std::is_copy_constructible_v<T>)
promise.reportResult(future.result()); promise.addResult(future.result());
else else
promise.reportAndMoveResult(future.takeResult()); promise.addResult(future.takeResult());
} }
} }
template<class T, class Function> template<class T, class Function>
void fulfillPromise(QFutureInterface<T> &promise, Function &&handler) void fulfillPromise(QPromise<T> &promise, Function &&handler)
{ {
if constexpr (std::is_void_v<T>) if constexpr (std::is_void_v<T>)
handler(); handler();
else if constexpr (std::is_copy_constructible_v<T>)
promise.reportResult(handler());
else else
promise.reportAndMoveResult(handler()); promise.addResult(handler());
} }
#ifndef QT_NO_EXCEPTIONS #ifndef QT_NO_EXCEPTIONS
@ -719,49 +707,44 @@ void fulfillPromise(QFutureInterface<T> &promise, Function &&handler)
template<class Function, class ResultType> template<class Function, class ResultType>
template<class F> template<class F>
void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future, void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future,
const QFutureInterface<ResultType> &promise) const QFutureInterface<ResultType> &fi)
{ {
Q_ASSERT(future); Q_ASSERT(future);
auto failureContinuation = [function = std::forward<F>(function), auto failureContinuation = [function = std::forward<F>(function), promise = QPromise(fi)](
promise](const QFutureInterfaceBase &parentData) mutable { const QFutureInterfaceBase &parentData) mutable {
const auto parent = QFutureInterface<ResultType>(parentData).future(); const auto parent = QFutureInterface<ResultType>(parentData).future();
FailureHandler<Function, ResultType> failureHandler(std::forward<Function>(function), FailureHandler<Function, ResultType> failureHandler(std::forward<Function>(function),
parent, promise); parent, std::move(promise));
failureHandler.run(); failureHandler.run();
}; };
if constexpr (!std::is_copy_constructible_v<Function>)
future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation))); future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
else
future->d.setContinuation(std::move(failureContinuation));
} }
template<class Function, class ResultType> template<class Function, class ResultType>
template<class F> template<class F>
void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future, void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future,
QFutureInterface<ResultType> &promise, QFutureInterface<ResultType> &fi,
QObject *context) QObject *context)
{ {
Q_ASSERT(future); Q_ASSERT(future);
auto failureContinuation = auto failureContinuation =
[function = std::forward<F>(function), promise, [function = std::forward<F>(function), promise = QPromise(fi),
context = QPointer<QObject>(context)](const QFutureInterfaceBase &parentData) mutable { context = QPointer<QObject>(context)](const QFutureInterfaceBase &parentData) mutable {
Q_ASSERT(context); Q_ASSERT(context);
const auto parent = QFutureInterface<ResultType>(parentData).future(); const auto parent = QFutureInterface<ResultType>(parentData).future();
QMetaObject::invokeMethod( QMetaObject::invokeMethod(context,
context, [function = std::forward<F>(function), promise, parent]() mutable { [function = std::forward<F>(function),
promise = std::move(promise), parent]() mutable {
FailureHandler<Function, ResultType> failureHandler( FailureHandler<Function, ResultType> failureHandler(
std::forward<Function>(function), parent, promise); std::forward<Function>(function), parent, std::move(promise));
failureHandler.run(); failureHandler.run();
}); });
}; };
if constexpr (!std::is_copy_constructible_v<Function>)
future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation))); future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
else
future->d.setContinuation(std::move(failureContinuation));
} }
template<class Function, class ResultType> template<class Function, class ResultType>
@ -769,7 +752,7 @@ void FailureHandler<Function, ResultType>::run()
{ {
Q_ASSERT(parentFuture.isFinished()); Q_ASSERT(parentFuture.isFinished());
promise.reportStarted(); promise.start();
if (parentFuture.d.hasException()) { if (parentFuture.d.hasException()) {
using ArgType = typename QtPrivate::ArgResolver<Function>::First; using ArgType = typename QtPrivate::ArgResolver<Function>::First;
@ -781,7 +764,7 @@ void FailureHandler<Function, ResultType>::run()
} else { } else {
QtPrivate::fulfillPromise(promise, parentFuture); QtPrivate::fulfillPromise(promise, parentFuture);
} }
promise.reportFinished(); promise.finish();
} }
template<class Function, class ResultType> template<class Function, class ResultType>
@ -794,21 +777,17 @@ void FailureHandler<Function, ResultType>::handleException()
} catch (const ArgType &e) { } catch (const ArgType &e) {
try { try {
// Handle exceptions matching with the handler's argument type // Handle exceptions matching with the handler's argument type
if constexpr (std::is_void_v<ResultType>) { if constexpr (std::is_void_v<ResultType>)
handler(e); handler(e);
} else {
if constexpr (std::is_copy_constructible_v<ResultType>)
promise.reportResult(handler(e));
else else
promise.reportAndMoveResult(handler(e)); promise.addResult(handler(e));
}
} catch (...) { } catch (...) {
promise.reportException(std::current_exception()); promise.setException(std::current_exception());
} }
} catch (...) { } catch (...) {
// Exception doesn't match with handler's argument type, propagate // Exception doesn't match with handler's argument type, propagate
// the exception to be handled later. // the exception to be handled later.
promise.reportException(std::current_exception()); promise.setException(std::current_exception());
} }
} }
@ -822,7 +801,7 @@ void FailureHandler<Function, ResultType>::handleAllExceptions()
try { try {
QtPrivate::fulfillPromise(promise, std::forward<Function>(handler)); QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
} catch (...) { } catch (...) {
promise.reportException(std::current_exception()); promise.setException(std::current_exception());
} }
} }
} }
@ -834,68 +813,55 @@ class CanceledHandler
{ {
public: public:
template<class F = Function> template<class F = Function>
static QFuture<ResultType> create(F &&handler, QFuture<ResultType> *future, static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi)
QFutureInterface<ResultType> &promise)
{ {
Q_ASSERT(future); Q_ASSERT(future);
auto canceledContinuation = [promise, handler = std::forward<F>(handler)]( auto canceledContinuation = [promise = QPromise(fi), handler = std::forward<F>(handler)](
const QFutureInterfaceBase &parentData) mutable { const QFutureInterfaceBase &parentData) mutable {
auto parentFuture = QFutureInterface<ResultType>(parentData).future(); auto parentFuture = QFutureInterface<ResultType>(parentData).future();
run(std::forward<F>(handler), parentFuture, promise); run(std::forward<F>(handler), parentFuture, std::move(promise));
}; };
if constexpr (!std::is_copy_constructible_v<Function>)
future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation))); future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
else
future->d.setContinuation(std::move(canceledContinuation));
return promise.future();
} }
template<class F = Function> template<class F = Function>
static QFuture<ResultType> create(F &&handler, QFuture<ResultType> *future, static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi,
QFutureInterface<ResultType> &promise, QObject *context) QObject *context)
{ {
Q_ASSERT(future); Q_ASSERT(future);
auto canceledContinuation = [promise = QPromise(fi), handler = std::forward<F>(handler),
auto canceledContinuation = [promise, handler = std::forward<F>(handler),
context = QPointer<QObject>(context)]( context = QPointer<QObject>(context)](
const QFutureInterfaceBase &parentData) mutable { const QFutureInterfaceBase &parentData) mutable {
Q_ASSERT(context); Q_ASSERT(context);
auto parentFuture = QFutureInterface<ResultType>(parentData).future(); auto parentFuture = QFutureInterface<ResultType>(parentData).future();
QMetaObject::invokeMethod( QMetaObject::invokeMethod(context,
context, [promise, parentFuture, handler = std::forward<F>(handler)]() mutable { [promise = std::move(promise), parentFuture,
run(std::forward<F>(handler), parentFuture, promise); handler = std::forward<F>(handler)]() mutable {
run(std::forward<F>(handler), parentFuture, std::move(promise));
}); });
}; };
if constexpr (!std::is_copy_constructible_v<Function>)
future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation))); future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
else
future->d.setContinuation(std::move(canceledContinuation));
return promise.future();
} }
template<class F = Function> template<class F = Function>
static void run(F &&handler, QFuture<ResultType> &parentFuture, static void run(F &&handler, QFuture<ResultType> &parentFuture, QPromise<ResultType> &&promise)
QFutureInterface<ResultType> &promise)
{ {
promise.reportStarted(); promise.start();
if (parentFuture.isCanceled()) { if (parentFuture.isCanceled()) {
#ifndef QT_NO_EXCEPTIONS #ifndef QT_NO_EXCEPTIONS
if (parentFuture.d.hasException()) { if (parentFuture.d.hasException()) {
// Propagate the exception to the result future // Propagate the exception to the result future
promise.reportException(parentFuture.d.exceptionStore().exception()); promise.setException(parentFuture.d.exceptionStore().exception());
} else { } else {
try { try {
#endif #endif
QtPrivate::fulfillPromise(promise, std::forward<F>(handler)); QtPrivate::fulfillPromise(promise, std::forward<F>(handler));
#ifndef QT_NO_EXCEPTIONS #ifndef QT_NO_EXCEPTIONS
} catch (...) { } catch (...) {
promise.reportException(std::current_exception()); promise.setException(std::current_exception());
} }
} }
#endif #endif
@ -903,7 +869,7 @@ public:
QtPrivate::fulfillPromise(promise, parentFuture); QtPrivate::fulfillPromise(promise, parentFuture);
} }
promise.reportFinished(); promise.finish();
} }
}; };

View File

@ -30,6 +30,7 @@
#include <qexception.h> #include <qexception.h>
#include <qfuture.h> #include <qfuture.h>
#include <qpromise.h>
#include <qsemaphore.h> #include <qsemaphore.h>
class tst_QFuture : public QObject class tst_QFuture : public QObject