summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp31
-rw-r--r--src/corelib/thread/qfuture.h42
-rw-r--r--src/corelib/thread/qfuture.qdoc69
-rw-r--r--src/corelib/thread/qfuture_impl.h120
-rw-r--r--tests/auto/corelib/thread/qfuture/tst_qfuture.cpp80
5 files changed, 322 insertions, 20 deletions
diff --git a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
index da17b390fd..a5e1a7f6e4 100644
--- a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
@@ -252,3 +252,34 @@ auto resultFuture = testFuture.then([](int res) {
// Block 6
});
//! [16]
+
+//! [17]
+// somewhere in the main thread
+auto future = QtConcurrent::run([] {
+ // This will run in a separate thread
+ ...
+}).then(this, [] {
+ // Update UI elements
+});
+//! [17]
+
+//! [18]
+auto future = QtConcurrent::run([] {
+ ...
+}).then(this, [] {
+ // Update UI elements
+}).then([] {
+ // This will also run in the main thread
+});
+//! [18]
+
+//! [19]
+// somewhere in the main thread
+auto future = QtConcurrent::run([] {
+ // This will run in a separate thread
+ ...
+ throw std::exception();
+}).onFailed(this, [] {
+ // Update UI elements
+});
+//! [19]
diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h
index 101bdc7808..a56e9ee3ff 100644
--- a/src/corelib/thread/qfuture.h
+++ b/src/corelib/thread/qfuture.h
@@ -173,15 +173,25 @@ QT_WARNING_POP
template<class Function>
QFuture<ResultType<Function>> then(QThreadPool *pool, Function &&function);
+ template<class Function>
+ QFuture<ResultType<Function>> then(QObject *context, Function &&function);
+
#ifndef QT_NO_EXCEPTIONS
template<class Function,
typename = std::enable_if_t<!QtPrivate::ArgResolver<Function>::HasExtraArgs>>
QFuture<T> onFailed(Function &&handler);
+
+ template<class Function,
+ typename = std::enable_if_t<!QtPrivate::ArgResolver<Function>::HasExtraArgs>>
+ QFuture<T> onFailed(QObject *context, Function &&handler);
#endif
template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>>
QFuture<T> onCanceled(Function &&handler);
+ template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>>
+ QFuture<T> onCanceled(QObject *context, Function &&handler);
+
class const_iterator
{
public:
@@ -363,8 +373,18 @@ QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(QTh
return promise.future();
}
-#ifndef QT_NO_EXCEPTIONS
+template<class T>
+template<class Function>
+QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(QObject *context,
+ Function &&function)
+{
+ QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::Continuation<std::decay_t<Function>, ResultType<Function>, T>::create(
+ std::forward<Function>(function), this, promise, context);
+ return promise.future();
+}
+#ifndef QT_NO_EXCEPTIONS
template<class T>
template<class Function, typename>
QFuture<T> QFuture<T>::onFailed(Function &&handler)
@@ -375,6 +395,16 @@ QFuture<T> QFuture<T>::onFailed(Function &&handler)
return promise.future();
}
+template<class T>
+template<class Function, typename>
+QFuture<T> QFuture<T>::onFailed(QObject *context, Function &&handler)
+{
+ QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::FailureHandler<std::decay_t<Function>, T>::create(std::forward<Function>(handler),
+ this, promise, context);
+ return promise.future();
+}
+
#endif
template<class T>
@@ -387,6 +417,16 @@ QFuture<T> QFuture<T>::onCanceled(Function &&handler)
return promise.future();
}
+template<class T>
+template<class Function, typename>
+QFuture<T> QFuture<T>::onCanceled(QObject *context, Function &&handler)
+{
+ QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::CanceledHandler<std::decay_t<Function>, T>::create(std::forward<Function>(handler),
+ this, promise, context);
+ return promise.future();
+}
+
inline QFuture<void> QFutureInterface<void>::future()
{
return QFuture<void>(this);
diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc
index 7603fe2230..0b7e59d9e1 100644
--- a/src/corelib/thread/qfuture.qdoc
+++ b/src/corelib/thread/qfuture.qdoc
@@ -1149,6 +1149,36 @@
\sa onFailed(), onCanceled()
*/
+/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QObject *context, Function &&function)
+
+ \since 6.1
+ \overload
+
+ Attaches a continuation to this future, allowing to chain multiple asynchronous
+ computations if desired. When the asynchronous computation represented by this
+ future finishes, \a function will be invoked in the thread of the \a context object.
+ This can be useful if the continuation needs to be invoked in a specific thread.
+ For example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 17
+
+ The continuation attached into QtConcurrent::run updates the UI elements and cannot
+ be invoked from a non-gui thread. So \c this is provided as a context to \c .then(),
+ to make sure that it will be invoked in the main thread.
+
+ The following continuations will be also invoked from the same context,
+ unless a different context or launch policy is specified:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 18
+
+ This is because by default \c .then() is invoked from the same thread as the parent.
+
+ \note When calling this method, it should be guaranteed that the \a context stays alive
+ throughout the execution of the chain.
+
+ \sa onFailed(), onCanceled()
+*/
+
/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(Function &&handler)
\since 6.0
@@ -1182,6 +1212,29 @@
\sa then(), onCanceled()
*/
+/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(QObject *context, Function &&handler)
+
+ \since 6.1
+ \overload
+
+ Attaches a failure handler to this future, to handle any exceptions that may
+ have been generated. Returns a QFuture of the parent type. The handler will
+ be invoked only in case of an exception, in the thread of the \a context object.
+ This can be useful if the failure needs to be handled in a specific thread.
+ For example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 19
+
+ The failure handler attached into QtConcurrent::run updates the UI elements and cannot
+ be invoked from a non-gui thread. So \c this is provided as a context to \c .onFailed(),
+ to make sure that it will be invoked in the main thread.
+
+ \note When calling this method, it should be guaranteed that the \a context stays alive
+ throughout the execution of the chain.
+
+ \sa then(), onCanceled()
+*/
+
/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onCanceled(Function &&handler)
\since 6.0
@@ -1194,3 +1247,19 @@
\sa then(), onFailed()
*/
+
+/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onCanceled(QObject *context, Function &&handler)
+
+ \since 6.1
+ \overload
+
+ Attaches a cancellation \a handler to this future, to be called when the future is
+ canceled. The \a handler is a callable which doesn't take any arguments. It will be
+ invoked in the thread of the \a context object. This can be useful if the cancellation
+ needs to be handled in a specific thread.
+
+ \note When calling this method, it should be guaranteed that the \a context stays alive
+ throughout the execution of the chain.
+
+ \sa then(), onFailed()
+*/
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
index 3f95d61b99..8e7ba0c4b5 100644
--- a/src/corelib/thread/qfuture_impl.h
+++ b/src/corelib/thread/qfuture_impl.h
@@ -50,6 +50,7 @@
#include <QtCore/qfutureinterface.h>
#include <QtCore/qthreadpool.h>
#include <QtCore/qexception.h>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -265,6 +266,10 @@ public:
static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p,
QThreadPool *pool);
+ template<typename F = Function>
+ static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p,
+ QObject *context);
+
private:
void fulfillPromiseWithResult();
void fulfillVoidPromise();
@@ -344,6 +349,10 @@ public:
const QFutureInterface<ResultType> &promise);
template<typename F = Function>
+ static void create(F &&function, QFuture<ResultType> *future,
+ QFutureInterface<ResultType> &promise, QObject *context);
+
+ template<typename F = Function>
FailureHandler(F &&func, const QFuture<ResultType> &f, const QFutureInterface<ResultType> &p)
: promise(p), parentFuture(f), handler(std::forward<F>(func))
{
@@ -518,7 +527,30 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
}
};
- f->d.setContinuation(continuation);
+ f->d.setContinuation(std::move(continuation));
+}
+
+template<typename Function, typename ResultType, typename ParentResultType>
+template<typename F>
+void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
+ QFuture<ParentResultType> *f,
+ QFutureInterface<ResultType> &p,
+ QObject *context)
+{
+ Q_ASSERT(f);
+
+ auto continuation = [func = std::forward<F>(func), p, 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), p, parent]() mutable {
+ SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
+ std::forward<Function>(func), parent, p);
+ continuationJob.execute();
+ });
+ };
+
+ f->d.setContinuation(std::move(continuation));
}
template<typename Function, typename ResultType, typename ParentResultType>
@@ -601,6 +633,30 @@ void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultTy
}
template<class Function, class ResultType>
+template<class F>
+void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future,
+ QFutureInterface<ResultType> &promise,
+ QObject *context)
+{
+ Q_ASSERT(future);
+
+ auto failureContinuation =
+ [function = std::forward<F>(function), promise,
+ 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, parent]() mutable {
+ FailureHandler<Function, ResultType> failureHandler(
+ std::forward<Function>(function), parent, promise);
+ failureHandler.run();
+ });
+ };
+
+ future->d.setContinuation(std::move(failureContinuation));
+}
+
+template<class Function, class ResultType>
void FailureHandler<Function, ResultType>::run()
{
Q_ASSERT(parentFuture.isFinished());
@@ -676,32 +732,58 @@ public:
auto canceledContinuation = [promise, handler = std::forward<F>(handler)](
const QFutureInterfaceBase &parentData) mutable {
auto parentFuture = QFutureInterface<ResultType>(parentData).future();
+ run(std::forward<F>(handler), parentFuture, promise);
+ };
+ future->d.setContinuation(std::move(canceledContinuation));
+ return promise.future();
+ }
- promise.reportStarted();
+ template<class F = Function>
+ static QFuture<ResultType> create(F &&handler, QFuture<ResultType> *future,
+ QFutureInterface<ResultType> &promise, QObject *context)
+ {
+ Q_ASSERT(future);
- if (parentFuture.isCanceled()) {
+ auto canceledContinuation = [promise, 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, parentFuture, handler = std::forward<F>(handler)]() mutable {
+ run(std::forward<F>(handler), parentFuture, promise);
+ });
+ };
+ future->d.setContinuation(std::move(canceledContinuation));
+ return promise.future();
+ }
+
+ template<class F = Function>
+ static void run(F &&handler, QFuture<ResultType> &parentFuture,
+ QFutureInterface<ResultType> &promise)
+ {
+ promise.reportStarted();
+
+ if (parentFuture.isCanceled()) {
#ifndef QT_NO_EXCEPTIONS
- if (parentFuture.d.exceptionStore().hasException()) {
- // Propagate the exception to the result future
- promise.reportException(parentFuture.d.exceptionStore().exception());
- } else {
- try {
+ if (parentFuture.d.exceptionStore().hasException()) {
+ // Propagate the exception to the result future
+ promise.reportException(parentFuture.d.exceptionStore().exception());
+ } else {
+ try {
#endif
- QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
+ QtPrivate::fulfillPromise(promise, std::forward<F>(handler));
#ifndef QT_NO_EXCEPTIONS
- } catch (...) {
- promise.reportException(std::current_exception());
- }
+ } catch (...) {
+ promise.reportException(std::current_exception());
}
-#endif
- } else {
- QtPrivate::fulfillPromise(promise, parentFuture);
}
+#endif
+ } else {
+ QtPrivate::fulfillPromise(promise, parentFuture);
+ }
- promise.reportFinished();
- };
- future->d.setContinuation(std::move(canceledContinuation));
- return promise.future();
+ promise.reportFinished();
}
};
diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
index c7d552cb30..3616500af8 100644
--- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
+++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp
@@ -138,6 +138,7 @@ private slots:
void onFailedForMoveOnlyTypes();
#endif
void onCanceled();
+ void continuationsWithContext();
#if 0
// TODO: enable when QFuture::takeResults() is enabled
void takeResults();
@@ -2820,6 +2821,85 @@ void tst_QFuture::onCanceled()
#endif // QT_NO_EXCEPTIONS
}
+void tst_QFuture::continuationsWithContext()
+{
+ QThread thread;
+ thread.start();
+
+ auto context = new QObject();
+ context->moveToThread(&thread);
+
+ auto tstThread = QThread::currentThread();
+
+ // .then()
+ {
+ auto future = QtFuture::makeReadyFuture(0)
+ .then([&](int val) {
+ if (QThread::currentThread() != tstThread)
+ return 0;
+ return val + 1;
+ })
+ .then(context,
+ [&](int val) {
+ if (QThread::currentThread() != &thread)
+ return 0;
+ return val + 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != &thread)
+ return 0;
+ return val + 1;
+ });
+ QCOMPARE(future.result(), 3);
+ }
+
+ // .onCanceled
+ {
+ auto future = createCanceledFuture<int>()
+ .onCanceled(context,
+ [&] {
+ if (QThread::currentThread() != &thread)
+ return 0;
+ return 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != &thread)
+ return 0;
+ return val + 1;
+ });
+ QCOMPARE(future.result(), 2);
+ }
+
+#ifndef QT_NO_EXCEPTIONS
+ // .onFaled()
+ {
+ auto future = QtFuture::makeReadyFuture()
+ .then([&] {
+ if (QThread::currentThread() != tstThread)
+ return 0;
+ throw std::runtime_error("error");
+ })
+ .onFailed(context,
+ [&] {
+ if (QThread::currentThread() != &thread)
+ return 0;
+ return 1;
+ })
+ .then([&](int val) {
+ if (QThread::currentThread() != &thread)
+ return 0;
+ return val + 1;
+ });
+ QCOMPARE(future.result(), 2);
+ }
+#endif // QT_NO_EXCEPTIONS
+
+ context->deleteLater();
+
+ thread.quit();
+ thread.wait();
+}
+
void tst_QFuture::testSingleResult(const UniquePtr &p)
{
QVERIFY(p.get() != nullptr);