summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qfuture_impl.h
diff options
context:
space:
mode:
authorArno Rehn <a.rehn@menlosystems.com>2023-05-08 15:28:11 +0200
committerArno Rehn <a.rehn@menlosystems.com>2023-05-30 21:42:46 +0200
commit07d6d31a4c0c17d8c897d783a9b0841df6834b02 (patch)
tree851fa5478368fb537523eed5ced7fb7581e5cc96 /src/corelib/thread/qfuture_impl.h
parent86c044176f16674616a20cd8e6a88b5a2dcf3775 (diff)
QFuture: Gracefully handle a destroyed context in continuations
This patch relaxes the requirements on the context object of continuations. Instead of having to stay alive during execution of the whole chain, it now only has to stay alive during setup of the chain. If the context object is destroyed before the chain finishes, the respective future is canceled. This patch works by using QFutureCallOutInterface and signals instead of direct invocation of the continuation by the parent future, similar to how QFutureWatcher is implemented. If a continuation is used with a context object, a QBasicFutureWatcher is connected to the QFuture via a QFutureCallOutInterface. When the future finishes, QBasicFutureWatcher::finished() triggers the continuation with a signal/slot connection. This way, we require the context object to stay alive only during setup; the required synchronization is guaranteed by the existing event and signal-slot mechanisms. The continuation itself does not need to know about the context object anymore. [ChangeLog][QtCore][QFuture] Added support for context objects of continuations being destroyed before the continuation finishes. In these cases the future is cancelled immediately. Fixes: QTBUG-112958 Change-Id: Ie0ef3470b2a0ccfa789d2ae7604b92e509c14591 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Diffstat (limited to 'src/corelib/thread/qfuture_impl.h')
-rw-r--r--src/corelib/thread/qfuture_impl.h85
1 files changed, 45 insertions, 40 deletions
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
index 47a07801c7..53f2326cde 100644
--- a/src/corelib/thread/qfuture_impl.h
+++ b/src/corelib/thread/qfuture_impl.h
@@ -11,6 +11,7 @@
#endif
#include <QtCore/qglobal.h>
+#include <QtCore/qbasicfuturewatcher.h>
#include <QtCore/qfutureinterface.h>
#include <QtCore/qthreadpool.h>
#include <QtCore/qexception.h>
@@ -597,21 +598,27 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
QObject *context)
{
Q_ASSERT(f);
-
- auto continuation = [func = std::forward<F>(func), fi,
- context = QPointer<QObject>(context)](
- const QFutureInterfaceBase &parentData) mutable {
- Q_ASSERT(context);
- const auto parent = QFutureInterface<ParentResultType>(parentData).future();
- QMetaObject::invokeMethod(
- context,
- [func = std::forward<F>(func), promise = QPromise(fi), parent]() mutable {
- SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
- std::forward<Function>(func), parent, std::move(promise));
- continuationJob.execute();
- });
+ Q_ASSERT(context);
+
+ // When the context object is destroyed, the signal-slot connection is broken and the
+ // continuation callback is destroyed. The promise that is created in the capture list is
+ // destroyed and, if it is not yet finished, cancelled.
+ auto continuation = [func = std::forward<F>(func), parent = *f,
+ promise = QPromise(fi)]() mutable {
+ SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
+ std::forward<Function>(func), parent, std::move(promise));
+ continuationJob.execute();
};
- f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
+
+ auto *watcher = new QBasicFutureWatcher;
+ watcher->moveToThread(context->thread());
+ QObject::connect(watcher, &QBasicFutureWatcher::finished,
+ context, std::move(continuation));
+ QObject::connect(watcher, &QBasicFutureWatcher::finished,
+ watcher, &QObject::deleteLater);
+ QObject::connect(context, &QObject::destroyed,
+ watcher, &QObject::deleteLater);
+ watcher->setFuture(f->d);
}
template<typename Function, typename ResultType, typename ParentResultType>
@@ -695,22 +702,20 @@ void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultTy
QObject *context)
{
Q_ASSERT(future);
+ Q_ASSERT(context);
+ auto failureContinuation = [function = std::forward<F>(function),
+ parent = *future, promise = QPromise(fi)]() mutable {
+ FailureHandler<Function, ResultType> failureHandler(
+ std::forward<Function>(function), parent, std::move(promise));
+ failureHandler.run();
+ };
- auto failureContinuation =
- [function = std::forward<F>(function), fi,
- context = QPointer<QObject>(context)](const QFutureInterfaceBase &parentData) mutable {
- Q_ASSERT(context);
- const auto parent = QFutureInterface<ResultType>(parentData).future();
- QMetaObject::invokeMethod(context,
- [function = std::forward<F>(function),
- promise = QPromise(fi), parent]() mutable {
- FailureHandler<Function, ResultType> failureHandler(
- std::forward<Function>(function), parent, std::move(promise));
- failureHandler.run();
- });
- };
-
- future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
+ auto *watcher = new QBasicFutureWatcher;
+ watcher->moveToThread(context->thread());
+ QObject::connect(watcher, &QBasicFutureWatcher::finished, context, std::move(failureContinuation));
+ QObject::connect(watcher, &QBasicFutureWatcher::finished, watcher, &QObject::deleteLater);
+ QObject::connect(context, &QObject::destroyed, watcher, &QObject::deleteLater);
+ watcher->setFuture(future->d);
}
template<class Function, class ResultType>
@@ -796,19 +801,19 @@ public:
QObject *context)
{
Q_ASSERT(future);
- auto canceledContinuation = [fi, handler = std::forward<F>(handler),
- context = QPointer<QObject>(context)](
- const QFutureInterfaceBase &parentData) mutable {
- Q_ASSERT(context);
- auto parentFuture = QFutureInterface<ResultType>(parentData).future();
- QMetaObject::invokeMethod(context,
- [promise = QPromise(fi), parentFuture,
- handler = std::forward<F>(handler)]() mutable {
- run(std::forward<F>(handler), parentFuture, std::move(promise));
- });
+ Q_ASSERT(context);
+ auto canceledContinuation = [handler = std::forward<F>(handler),
+ parentFuture = *future, promise = QPromise(fi)]() mutable {
+ run(std::forward<F>(handler), parentFuture, std::move(promise));
};
- future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
+ auto *watcher = new QBasicFutureWatcher;
+ watcher->moveToThread(context->thread());
+ QObject::connect(watcher, &QBasicFutureWatcher::finished,
+ context, std::move(canceledContinuation));
+ QObject::connect(watcher, &QBasicFutureWatcher::finished, watcher, &QObject::deleteLater);
+ QObject::connect(context, &QObject::destroyed, watcher, &QObject::deleteLater);
+ watcher->setFuture(future->d);
}
template<class F = Function>