QPromise: Propagate cancellation through failure handlers

Previously, failure handlers did not propagate cancellation. This would
lead to crashes when a QPromise was cancelled without having generated
any result. Subsequent continuations would be invoked and try to access
the result (which was nonexistent) and then crash.
This patch propagates cancellation through failure handlers to prevent
subsequent continuations from being called in the first place.

Fixes: QTBUG-114606
Pick-to: 6.6
Pick-to: 6.5
Change-Id: I23b28a8e70a76e1ba6416be4440360c6dbaef2a3
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Arno Rehn 2023-06-15 17:12:02 +02:00
parent 1f6cac0da9
commit 855c448469
2 changed files with 35 additions and 0 deletions

View File

@ -732,6 +732,8 @@ void FailureHandler<Function, ResultType>::run()
} else {
handleException<ArgType>();
}
} else if (parentFuture.d.isChainCanceled()) {
promise.future().cancel();
} else {
QtPrivate::fulfillPromise(promise, parentFuture);
}

View File

@ -44,6 +44,7 @@ private slots:
void cancelWhenReassigned();
void cancelWhenDestroyedWithoutStarting();
void cancelWhenDestroyedRunsContinuations();
void cancelWhenDestroyedWithFailureHandler(); // QTBUG-114606
void continuationsRunWhenFinished();
void finishWhenSwapped();
void cancelWhenMoved();
@ -577,6 +578,38 @@ void tst_QPromise::cancelWhenDestroyedRunsContinuations()
testCancelWhenDestroyedRunsContinuations<MoveOnlyType>();
}
template <typename T>
static inline void testCancelWhenDestroyedWithFailureHandler()
{
QFuture<T> future;
bool onFailedCalled = false;
bool thenCalled = false;
{
QPromise<T> promise;
future = promise.future();
future
.onFailed([&] () {
onFailedCalled = true;
if constexpr (!std::is_same_v<void, T>)
return T{};
})
.then([&] (auto&&) {
thenCalled = true;
});
}
QVERIFY(future.isFinished());
QVERIFY(!onFailedCalled);
QVERIFY(!thenCalled);
}
void tst_QPromise::cancelWhenDestroyedWithFailureHandler()
{
testCancelWhenDestroyedWithFailureHandler<void>();
testCancelWhenDestroyedWithFailureHandler<int>();
testCancelWhenDestroyedWithFailureHandler<CopyOnlyType>();
testCancelWhenDestroyedWithFailureHandler<MoveOnlyType>();
}
template <typename T>
static inline void testContinuationsRunWhenFinished()
{