summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorSona Kurazyan <sona.kurazyan@qt.io>2020-05-05 17:44:47 +0200
committerSona Kurazyan <sona.kurazyan@qt.io>2020-05-15 22:35:30 +0200
commit1a5cc8d13d0d348a571cab0d24dc814b896c8db7 (patch)
treef61154e733d74c5709549971bb7ce2edeb4ab453 /src/corelib
parent612f6999c81a500a024f128bdf739342d659754a (diff)
Add support of cancellation handler callbacks to QFuture
Added QFuture::onCanceled() method, for attaching handlers to be called when the QFuture gets canceled. Change-Id: I1f01647d6173ba0c1db6641e14140108b33ac7c4 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp32
-rw-r--r--src/corelib/thread/qfuture.h15
-rw-r--r--src/corelib/thread/qfuture.qdoc49
-rw-r--r--src/corelib/thread/qfuture_impl.h75
-rw-r--r--src/corelib/thread/qfutureinterface.h6
5 files changed, 159 insertions, 18 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 0580f142d7..da17b390fd 100644
--- a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
@@ -220,3 +220,35 @@ QtFuture::connect(&object, &Object::singleArgSignal).then([](int value) {
// handle other exceptions
});
//! [14]
+
+//! [15]
+QFuture<int> testFuture = ...;
+auto resultFuture = testFuture.then([](int res) {
+ // Block 1
+}).onCanceled([] {
+ // Block 2
+}).onFailed([] {
+ // Block 3
+}).then([] {
+ // Block 4
+}).onFailed([] {
+ // Block 5
+}).onCanceled([] {
+ // Block 6
+});
+//! [15]
+
+//! [16]
+QFuture<int> testFuture = ...;
+auto resultFuture = testFuture.then([](int res) {
+ // Block 1
+}).onFailed([] {
+ // Block 3
+}).then([] {
+ // Block 4
+}).onFailed([] {
+ // Block 5
+}).onCanceled([] {
+ // Block 6
+});
+//! [16]
diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h
index bfca16b641..22ab0c13de 100644
--- a/src/corelib/thread/qfuture.h
+++ b/src/corelib/thread/qfuture.h
@@ -168,6 +168,9 @@ public:
QFuture<T> onFailed(Function &&handler);
#endif
+ template<class Function, typename = std::enable_if_t<std::is_invocable_r_v<T, Function>>>
+ QFuture<T> onCanceled(Function &&handler);
+
class const_iterator
{
public:
@@ -281,6 +284,9 @@ private:
template<class Function, class ResultType, class ParentResultType>
friend class QtPrivate::Continuation;
+ template<class Function, class ResultType>
+ friend class QtPrivate::CanceledHandler;
+
#ifndef QT_NO_EXCEPTIONS
template<class Function, class ResultType>
friend class QtPrivate::FailureHandler;
@@ -359,6 +365,15 @@ QFuture<T> QFuture<T>::onFailed(Function &&handler)
#endif
+template<class T>
+template<class Function, typename>
+QFuture<T> QFuture<T>::onCanceled(Function &&handler)
+{
+ QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::CanceledHandler<Function, T>::create(std::forward<Function>(handler), this, promise);
+ 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 f977a48f96..1846aa377d 100644
--- a/src/corelib/thread/qfuture.qdoc
+++ b/src/corelib/thread/qfuture.qdoc
@@ -55,8 +55,9 @@
results in the future.
If the result of one asynchronous computation needs to be passed
- to another, QFuture provides a convenient way of chaining multiple
- sequential computations using then(). Additionally, onFailed() can be used
+ to another, QFuture provides a convenient way of chaining multiple sequential
+ computations using then(). onCanceled() can be used for adding a handler to
+ be called if the QFuture is canceled. Additionally, onFailed() can be used
to handle any failures that occurred in the chain. Note that QFuture relies
on exceptions for the error handling. If using exceptions is not an option,
you can still indicate the error state of QFuture, by making the error type
@@ -76,6 +77,31 @@
\snippet code/src_corelib_thread_qfuture.cpp 4
+ It's possible to chain multiple continuations and handlers in any order.
+ The first handler that can handle the state of its parent is invoked first.
+ If there's no proper handler, the state is propagated to the next continuation
+ or handler. For example:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 15
+
+ If \c testFuture is successfully fulfilled \c {Block 1} will be called. If
+ it succeeds as well, the next then() (\c {Block 4}) is called. If \c testFuture
+ gets canceled or fails with an exception, either \c {Block 2} or \c {Block 3}
+ will be called respectively. The next then() will be called afterwards, and the
+ story repeats.
+
+ \note If \c {Block 2} is invoked and throws an exception, the following
+ onFailed() (\c {Block 3}) will handle it. If the order of onFailed() and
+ onCanceled() were reversed, the exception state would propagate to the
+ next continuations and eventually would be caught in \c {Block 5}.
+
+ In the next example the first onCanceled() (\c {Block 2}) is removed:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 16
+
+ If \c testFuture gets canceled, its state is propagated to the next then(),
+ which will be also canceled. So in this case \c {Block 6} will be called.
+
QFuture also offers ways to interact with a runnning computation. For
instance, the computation can be canceled with the cancel() function. To
pause the computation, use the setPaused() function or one of the pause(),
@@ -893,7 +919,7 @@
\note If the parent future gets canceled, its continuations will
also be canceled.
- \sa onFailed()
+ \sa onFailed(), onCanceled()
*/
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QtFuture::Launch policy, Function &&function)
@@ -926,7 +952,7 @@
.then(QtFuture::Launch::Inherit, [](int res2){ ... });
\endcode
- \sa onFailed()
+ \sa onFailed(), onCanceled()
*/
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QThreadPool *pool, Function &&function)
@@ -939,7 +965,7 @@
future finishes, \a function will be invoked in a separate thread taken from the
QThreadPool \a pool.
- \sa onFailed()
+ \sa onFailed(), onCanceled()
*/
/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(Function &&handler)
@@ -970,5 +996,16 @@
\note You can always attach a handler taking no argument, to handle all exception
types and avoid writing the try-catch block.
- \sa then()
+ \sa then(), onCanceled()
+*/
+
+/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onCanceled(Function &&handler)
+
+ \since 6.0
+
+ 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 same thread in which this future has been running.
+
+ \sa then(), onFailed()
*/
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
index 0e0cef0dbf..82c89cfa38 100644
--- a/src/corelib/thread/qfuture_impl.h
+++ b/src/corelib/thread/qfuture_impl.h
@@ -495,6 +495,28 @@ void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &
promise.reportAndMoveResult(std::invoke(function, std::forward<Args>(args)...));
}
+template<class T>
+void fulfillPromise(QFutureInterface<T> &promise, QFuture<T> &future)
+{
+ if constexpr (!std::is_void_v<T>) {
+ if constexpr (std::is_copy_constructible_v<T>)
+ promise.reportResult(future.result());
+ else
+ promise.reportAndMoveResult(future.takeResult());
+ }
+}
+
+template<class T, class Function>
+void fulfillPromise(QFutureInterface<T> &promise, Function &&handler)
+{
+ if constexpr (std::is_void_v<T>)
+ handler();
+ else if constexpr (std::is_copy_constructible_v<T>)
+ promise.reportResult(handler());
+ else
+ promise.reportAndMoveResult(handler());
+}
+
#ifndef QT_NO_EXCEPTIONS
template<class Function, class ResultType>
@@ -529,12 +551,7 @@ void FailureHandler<Function, ResultType>::run()
handleException<ArgType>();
}
} else {
- if constexpr (!std::is_void_v<ResultType>) {
- if constexpr (std::is_copy_constructible_v<ResultType>)
- promise.reportResult(parentFuture.result());
- else
- promise.reportAndMoveResult(parentFuture.takeResult());
- }
+ QtPrivate::fulfillPromise(promise, parentFuture);
}
promise.reportFinished();
}
@@ -573,12 +590,7 @@ void FailureHandler<Function, ResultType>::handleAllExceptions()
parentFuture.d.exceptionStore().throwPossibleException();
} catch (...) {
try {
- if constexpr (std::is_void_v<ResultType>)
- handler();
- else if constexpr (std::is_copy_constructible_v<ResultType>)
- promise.reportResult(handler());
- else
- promise.reportAndMoveResult(handler());
+ QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
} catch (...) {
promise.reportException(std::current_exception());
}
@@ -587,6 +599,45 @@ void FailureHandler<Function, ResultType>::handleAllExceptions()
#endif // QT_NO_EXCEPTIONS
+template<class Function, class ResultType>
+class CanceledHandler
+{
+public:
+ static QFuture<ResultType> create(Function &&handler, QFuture<ResultType> *future,
+ QFutureInterface<ResultType> promise)
+ {
+ Q_ASSERT(future);
+
+ auto canceledContinuation = [parentFuture = *future, promise,
+ handler = std::move(handler)]() mutable {
+ 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 {
+#endif
+ QtPrivate::fulfillPromise(promise, std::forward<Function>(handler));
+#ifndef QT_NO_EXCEPTIONS
+ } catch (...) {
+ promise.reportException(std::current_exception());
+ }
+ }
+#endif
+ } else {
+ QtPrivate::fulfillPromise(promise, parentFuture);
+ }
+
+ promise.reportFinished();
+ };
+ future->d.setContinuation(std::move(canceledContinuation));
+ return promise.future();
+ }
+};
+
} // namespace QtPrivate
namespace QtFuture {
diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h
index 02f854e0de..2f9b455d8d 100644
--- a/src/corelib/thread/qfutureinterface.h
+++ b/src/corelib/thread/qfutureinterface.h
@@ -64,6 +64,9 @@ namespace QtPrivate {
template<typename Function, typename ResultType, typename ParentResultType>
class Continuation;
+template<class Function, class ResultType>
+class CanceledHandler;
+
#ifndef QT_NO_EXCEPTIONS
template<class Function, class ResultType>
class FailureHandler;
@@ -163,6 +166,9 @@ private:
template<typename Function, typename ResultType, typename ParentResultType>
friend class QtPrivate::Continuation;
+ template<class Function, class ResultType>
+ friend class QtPrivate::CanceledHandler;
+
#ifndef QT_NO_EXCEPTIONS
template<class Function, class ResultType>
friend class QtPrivate::FailureHandler;