diff options
author | Sona Kurazyan <sona.kurazyan@qt.io> | 2020-12-04 17:20:29 +0100 |
---|---|---|
committer | Sona Kurazyan <sona.kurazyan@qt.io> | 2020-12-11 09:36:45 +0100 |
commit | 335acffe1d955b5fe4535dc1d99ec2c3dbe1b978 (patch) | |
tree | 4648a3814a956bbe5d0dc8c39bf4f120ce30c591 /src/corelib/thread | |
parent | b283ce1e836ab08e602a11ea255ee3d8e537902e (diff) |
Add support of invoking QFuture continuations in a given context
Added overloads of .then()/.onFailed()/.onCanceled() which take a
pointer of a context object, and invoke the continuations in the
object's thread.
Task-number: QTBUG-86794
Change-Id: I0f3cbb0500695673fc4087af5d4b96b416e3e1ce
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Diffstat (limited to 'src/corelib/thread')
-rw-r--r-- | src/corelib/thread/qfuture.h | 42 | ||||
-rw-r--r-- | src/corelib/thread/qfuture.qdoc | 69 | ||||
-rw-r--r-- | src/corelib/thread/qfuture_impl.h | 120 |
3 files changed, 211 insertions, 20 deletions
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(); } }; |