summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSona Kurazyan <sona.kurazyan@qt.io>2020-02-27 17:30:07 +0100
committerSona Kurazyan <sona.kurazyan@qt.io>2020-03-05 13:24:32 +0100
commitdfaca09e85a49d2983bb89893bfbe1ba4c19eab4 (patch)
treec01c0acca4e8c183b555876d203fb4faa261d893 /src
parent1057d568bb921e378b9fc5eb6c95b39dd6dc94fa (diff)
Add support for attaching continuations to QFuture
Added QFuture::then() methods to allow chaining multiple asynchronous computations. Continuations can use the following execution policies: * QtFuture::Launch::Sync - the continuation will be launched in the same thread in which the parent has been executing. * QtFuture::Launch::Async - the continuation will be launched in a new thread. * QtFuture::Launch::Inherit - the continuation will inherit the launch policy of the parent, or its thread pool (if it was using a custom one). * Additionally then() also accepts a custom QThreadPool* instance. Note, that if the parent future gets canceled, its continuation(s) will be also canceled. If the parent throws an exception, it will be propagated to the continuation's future, unless it is caught inside the continuation (if it has a QFuture arg). Some example usages: QFuture<int> future = ...; future.then([](int res1){ ... }).then([](int res2){ ... })... QFuture<int> future = ...; future.then([](QFuture<int> fut1){ /* do something with fut1 */ })... In the examples above all continuations will run in the same thread as future. QFuture<int> future = ...; future.then(QtFuture::Launch::Async, [](int res1){ ... }) .then([](int res2){ ... }).. In this example the continuations will run in a new thread (but on the same one). QThreadPool pool; QFuture<int> future = ...; future.then(&pool, [](int res1){ ... }) .then([](int res2){ ... }).. In this example the continuations will run in the given thread pool. [ChangeLog][QtCore] Added support for attaching continuations to QFuture. Task-number: QTBUG-81587 Change-Id: I5b2e176694f7ae8ce00404aca725e9a170818955 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/.prev_CMakeLists.txt1
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/thread/qfuture.h86
-rw-r--r--src/corelib/thread/qfuture.qdoc141
-rw-r--r--src/corelib/thread/qfuture_impl.h339
-rw-r--r--src/corelib/thread/qfutureinterface.cpp50
-rw-r--r--src/corelib/thread/qfutureinterface.h29
-rw-r--r--src/corelib/thread/qfutureinterface_p.h6
-rw-r--r--src/corelib/thread/thread.pri1
9 files changed, 647 insertions, 7 deletions
diff --git a/src/corelib/.prev_CMakeLists.txt b/src/corelib/.prev_CMakeLists.txt
index 51885497bf..a9fb15f505 100644
--- a/src/corelib/.prev_CMakeLists.txt
+++ b/src/corelib/.prev_CMakeLists.txt
@@ -574,6 +574,7 @@ qt_extend_target(Core CONDITION QT_FEATURE_future
SOURCES
thread/qexception.cpp thread/qexception.h
thread/qfuture.h
+ thread/qfuture_impl.h
thread/qfutureinterface.cpp thread/qfutureinterface.h thread/qfutureinterface_p.h
thread/qfuturesynchronizer.h
thread/qfuturewatcher.cpp thread/qfuturewatcher.h thread/qfuturewatcher_p.h
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index b62ce89f4d..49bc440ea9 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -677,6 +677,7 @@ qt_extend_target(Core CONDITION QT_FEATURE_future
SOURCES
thread/qexception.cpp thread/qexception.h
thread/qfuture.h
+ thread/qfuture_impl.h
thread/qfutureinterface.cpp thread/qfutureinterface.h thread/qfutureinterface_p.h
thread/qfuturesynchronizer.h
thread/qfuturewatcher.cpp thread/qfuturewatcher.h thread/qfuturewatcher_p.h
diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h
index d3135510b3..e103cfb552 100644
--- a/src/corelib/thread/qfuture.h
+++ b/src/corelib/thread/qfuture.h
@@ -45,11 +45,12 @@
#include <QtCore/qfutureinterface.h>
#include <QtCore/qstring.h>
+#include <QtCore/qfuture_impl.h>
+
QT_REQUIRE_CONFIG(future);
QT_BEGIN_NAMESPACE
-
template <typename T>
class QFutureWatcher;
template <>
@@ -101,6 +102,18 @@ public:
operator T() const { return result(); }
QList<T> results() const { return d.results(); }
+ template<class Function>
+ using ResultType = typename QtPrivate::ResultTypeHelper<Function, T>::ResultType;
+
+ template<class Function>
+ QFuture<ResultType<Function>> then(Function &&function);
+
+ template<class Function>
+ QFuture<ResultType<Function>> then(QtFuture::Launch policy, Function &&function);
+
+ template<class Function>
+ QFuture<ResultType<Function>> then(QThreadPool *pool, Function &&function);
+
class const_iterator
{
public:
@@ -199,6 +212,7 @@ private:
friend class QFutureWatcher<T>;
public: // Warning: the d pointer is not documented and is considered private.
+ // TODO: make this private
mutable QFutureInterface<T> d;
};
@@ -222,6 +236,35 @@ inline QFuture<T> QFutureInterface<T>::future()
return QFuture<T>(this);
}
+template<class T>
+template<class Function>
+QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(Function &&function)
+{
+ return then(QtFuture::Launch::Sync, std::forward<Function>(function));
+}
+
+template<class T>
+template<class Function>
+QFuture<typename QFuture<T>::template ResultType<Function>>
+QFuture<T>::then(QtFuture::Launch policy, Function &&function)
+{
+ QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::Continuation<Function, ResultType<Function>, T>::create(
+ std::forward<Function>(function), this, promise, policy);
+ return promise.future();
+}
+
+template<class T>
+template<class Function>
+QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(QThreadPool *pool,
+ Function &&function)
+{
+ QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::Continuation<Function, ResultType<Function>, T>::create(
+ std::forward<Function>(function), this, promise, pool);
+ return promise.future();
+}
+
Q_DECLARE_SEQUENTIAL_ITERATOR(Future)
template <>
@@ -272,6 +315,18 @@ public:
QString progressText() const { return d.progressText(); }
void waitForFinished() { d.waitForFinished(); }
+ template<class Function>
+ using ResultType = typename QtPrivate::ResultTypeHelper<Function, void>::ResultType;
+
+ template<class Function>
+ QFuture<ResultType<Function>> then(Function &&function);
+
+ template<class Function>
+ QFuture<ResultType<Function>> then(QtFuture::Launch policy, Function &&function);
+
+ template<class Function>
+ QFuture<ResultType<Function>> then(QThreadPool *pool, Function &&function);
+
private:
friend class QFutureWatcher<void>;
@@ -279,6 +334,9 @@ private:
public:
#endif
mutable QFutureInterfaceBase d;
+
+ template<typename Function, typename ResultType, typename ParentResultType>
+ friend class QtPrivate::Continuation;
};
inline QFuture<void> QFutureInterface<void>::future()
@@ -292,6 +350,32 @@ QFuture<void> qToVoidFuture(const QFuture<T> &future)
return QFuture<void>(future.d);
}
+template<class Function>
+QFuture<QFuture<void>::ResultType<Function>> QFuture<void>::then(Function &&function)
+{
+ return then(QtFuture::Launch::Sync, std::forward<Function>(function));
+}
+
+template<class Function>
+QFuture<QFuture<void>::ResultType<Function>> QFuture<void>::then(QtFuture::Launch policy,
+ Function &&function)
+{
+ QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::Continuation<Function, ResultType<Function>, void>::create(
+ std::forward<Function>(function), this, promise, policy);
+ return promise.future();
+}
+
+template<class Function>
+QFuture<QFuture<void>::ResultType<Function>> QFuture<void>::then(QThreadPool *pool,
+ Function &&function)
+{
+ QFutureInterface<ResultType<Function>> promise(QFutureInterfaceBase::State::Pending);
+ QtPrivate::Continuation<Function, ResultType<Function>, void>::create(
+ std::forward<Function>(function), this, promise, pool);
+ return promise.future();
+}
+
QT_END_NAMESPACE
#endif // QFUTURE_H
diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc
index 076725e19c..989ffa36c8 100644
--- a/src/corelib/thread/qfuture.qdoc
+++ b/src/corelib/thread/qfuture.qdoc
@@ -682,3 +682,144 @@
\sa findNext()
*/
+
+/*!
+ \namespace QtFuture
+
+ \inmodule QtCore
+ \brief Contains miscellaneous identifiers used by the QFuture class.
+*/
+
+
+/*!
+ \enum QtFuture::Launch
+
+ \since 6.0
+
+ Represents execution policies for running a QFuture continuation.
+
+ \value Sync The continuation will be launched in the same thread in
+ which the parent has been executing.
+
+ \value Async The continuation will be launched in in a separate thread taken from
+ the global QThreadPool.
+
+ \value Inherit The continuation will inherit the launch policy of the parent or its
+ thread pool, if it was using a custom one.
+
+ \sa QFuture::then(), QThreadPool::globalInstance()
+
+*/
+
+/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(Function &&function)
+
+ \since 6.0
+ \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 same thread in which this
+ future has been running. A new QFuture representing the result of the continuation
+ is returned.
+
+ \note Use other overloads of this method if you need to launch the continuation in
+ a separate thread.
+
+ If this future has a result (is not a QFuture<void>), \a function takes the result
+ of this future as its argument.
+
+ You can chain multiple operations like this:
+
+ \code
+ QFuture<int> future = ...;
+ future.then([](int res1){ ... }).then([](int res2){ ... })...
+ \endcode
+
+ Or:
+ \code
+ QFuture<void> future = ...;
+ future.then([](){ ... }).then([](){ ... })...
+ \endcode
+
+ The continuation can also take a QFuture argument (instead of its value), representing
+ the previous future. This can be useful if, for example, QFuture has multiple results,
+ and the user wants to access them inside the continuation. Or the user needs to handle
+ the exception of the previous future inside the continuation, to not interrupt the chain
+ of multiple continuations. For example:
+
+ \code
+ QFuture<int> future = ...;
+ future.then([](QFuture<int> f) {
+ try {
+ ...
+ auto result = f.result();
+ ...
+ } catch (QException &e) {
+ // handle the exception
+ }
+ }).then(...);
+ \endcode
+
+ If the previous future throws an exception and it is not handled inside the
+ continuation, the exception will be propagated to the continuation future, to
+ allow the caller to handle it:
+
+ \code
+ QFuture<int> parentFuture = ...;
+ auto continuation = parentFuture.then([](int res1){ ... }).then([](int res2){ ... })...
+ ...
+ // parentFuture throws an exception
+ try {
+ auto result = continuation.result();
+ } catch (QException &e) {
+ // handle the exception
+ }
+ \endcode
+
+ In this case the whole chain of continuations will be interrupted.
+
+ \note If the parent future gets canceled, its continuations will
+ also be canceled.
+*/
+
+/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QtFuture::Launch policy, Function &&function)
+
+ \since 6.0
+ \overload
+
+ Attaches a continuation to this future, allowing to chain multiple asynchronous
+ computations. When the asynchronous computation represented by this future
+ finishes, \a function will be invoked according to the given launch \a policy.
+ A new QFuture representing the result of the continuation is returned.
+
+ Depending on the \a policy, continuation will run in the same thread as the parent,
+ run in a new thread, or inherit the launch policy and thread pool of the parent.
+
+ In the following example both continuations will run in a new thread (but in
+ the same one).
+
+ \code
+ QFuture<int> future = ...;
+ future.then(QtFuture::Launch::Async, [](int res){ ... }).then([](int res2){ ... });
+ \endcode
+
+ In the following example both continuations will run in new threads using the same
+ thread pool.
+
+ \code
+ QFuture<int> future = ...;
+ future.then(QtFuture::Launch::Async, [](int res){ ... })
+ .then(QtFuture::Launch::Inherit, [](int res2){ ... });
+ \endcode
+*/
+
+/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QThreadPool *pool, Function &&function)
+
+ \since 6.0
+ \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 a separate thread taken from the
+ QThreadPool \a pool.
+*/
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
new file mode 100644
index 0000000000..eed7828c35
--- /dev/null
+++ b/src/corelib/thread/qfuture_impl.h
@@ -0,0 +1,339 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFUTURE_H
+#error Do not include qfuture_impl.h directly
+#endif
+
+#if 0
+#pragma qt_sync_skip_header_check
+#pragma qt_sync_stop_processing
+#endif
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qfutureinterface.h>
+#include <QtCore/qthreadpool.h>
+
+QT_BEGIN_NAMESPACE
+
+//
+// forward declarations
+//
+template<class T>
+class QFuture;
+template<class T>
+class QFutureInterface;
+
+namespace QtFuture {
+enum class Launch { Sync, Async, Inherit };
+}
+
+namespace QtPrivate {
+
+template<typename F, typename Arg, typename Enable = void>
+struct ResultTypeHelper
+{
+};
+
+// The callable takes an argument of type Arg
+template<typename F, typename Arg>
+struct ResultTypeHelper<
+ F, Arg, typename std::enable_if_t<!std::is_invocable_v<std::decay_t<F>, QFuture<Arg>>>>
+{
+ using ResultType = std::invoke_result_t<std::decay_t<F>, std::decay_t<Arg>>;
+};
+
+// The callable takes an argument of type QFuture<Arg>
+template<class F, class Arg>
+struct ResultTypeHelper<
+ F, Arg, typename std::enable_if_t<std::is_invocable_v<std::decay_t<F>, QFuture<Arg>>>>
+{
+ using ResultType = std::invoke_result_t<std::decay_t<F>, QFuture<Arg>>;
+};
+
+// The callable takes an argument of type QFuture<void>
+template<class F>
+struct ResultTypeHelper<
+ F, void, typename std::enable_if_t<std::is_invocable_v<std::decay_t<F>, QFuture<void>>>>
+{
+ using ResultType = std::invoke_result_t<std::decay_t<F>, QFuture<void>>;
+};
+
+// The callable doesn't take argument
+template<class F>
+struct ResultTypeHelper<
+ F, void, typename std::enable_if_t<!std::is_invocable_v<std::decay_t<F>, QFuture<void>>>>
+{
+ using ResultType = std::invoke_result_t<std::decay_t<F>>;
+};
+
+template<typename Function, typename ResultType, typename ParentResultType>
+class Continuation
+{
+public:
+ Continuation(Function &&func, const QFuture<ParentResultType> &f,
+ const QFutureInterface<ResultType> &p)
+ : promise(p), parentFuture(f), function(std::forward<Function>(func))
+ {
+ }
+ virtual ~Continuation() = default;
+
+ bool execute();
+
+ static void create(Function &&func, QFuture<ParentResultType> *f,
+ QFutureInterface<ResultType> &p, QtFuture::Launch policy);
+
+ static void create(Function &&func, QFuture<ParentResultType> *f,
+ QFutureInterface<ResultType> &p, QThreadPool *pool);
+
+protected:
+ virtual void runImpl() = 0;
+
+ void runFunction();
+
+protected:
+ QFutureInterface<ResultType> promise;
+ const QFuture<ParentResultType> parentFuture;
+ Function function;
+};
+
+template<typename Function, typename ResultType, typename ParentResultType>
+class SyncContinuation final : public Continuation<Function, ResultType, ParentResultType>
+{
+public:
+ SyncContinuation(Function &&func, const QFuture<ParentResultType> &f,
+ const QFutureInterface<ResultType> &p)
+ : Continuation<Function, ResultType, ParentResultType>(std::forward<Function>(func), f, p)
+ {
+ }
+
+ ~SyncContinuation() override = default;
+
+private:
+ void runImpl() override { this->runFunction(); }
+};
+
+template<typename Function, typename ResultType, typename ParentResultType>
+class AsyncContinuation final : public QRunnable,
+ public Continuation<Function, ResultType, ParentResultType>
+{
+public:
+ AsyncContinuation(Function &&func, const QFuture<ParentResultType> &f,
+ const QFutureInterface<ResultType> &p, QThreadPool *pool = nullptr)
+ : Continuation<Function, ResultType, ParentResultType>(std::forward<Function>(func), f, p),
+ threadPool(pool)
+ {
+ this->promise.setRunnable(this);
+ }
+
+ ~AsyncContinuation() override = default;
+
+private:
+ void runImpl() override // from Continuation
+ {
+ QThreadPool *pool = threadPool ? threadPool : QThreadPool::globalInstance();
+ pool->start(this);
+ }
+
+ void run() override // from QRunnable
+ {
+ this->runFunction();
+ }
+
+private:
+ QThreadPool *threadPool;
+};
+
+template<typename Function, typename ResultType, typename ParentResultType>
+void Continuation<Function, ResultType, ParentResultType>::runFunction()
+{
+ promise.reportStarted();
+
+ Q_ASSERT(parentFuture.isFinished());
+
+#ifndef QT_NO_EXCEPTIONS
+ try {
+#endif
+ if constexpr (!std::is_void_v<ResultType>) {
+ if constexpr (std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
+ promise.reportResult(function(parentFuture));
+ } else if constexpr (std::is_void_v<ParentResultType>) {
+ promise.reportResult(function());
+ } else {
+ // This assert normally should never fail, this is to make sure
+ // that nothing unexpected happend.
+ static_assert(
+ std::is_invocable_v<std::decay_t<Function>, std::decay_t<ParentResultType>>,
+ "The continuation is not invocable with the provided arguments");
+
+ promise.reportResult(function(parentFuture.result()));
+ }
+ } else {
+ if constexpr (std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
+ function(parentFuture);
+ } else if constexpr (std::is_void_v<ParentResultType>) {
+ function();
+ } else {
+ // This assert normally should never fail, this is to make sure
+ // that nothing unexpected happend.
+ static_assert(
+ std::is_invocable_v<std::decay_t<Function>, std::decay_t<ParentResultType>>,
+ "The continuation is not invocable with the provided arguments");
+
+ function(parentFuture.result());
+ }
+ }
+#ifndef QT_NO_EXCEPTIONS
+ } catch (QException &e) {
+ promise.reportException(e);
+ } catch (...) {
+ promise.reportException(QUnhandledException());
+ }
+#endif
+ promise.reportFinished();
+}
+
+template<typename Function, typename ResultType, typename ParentResultType>
+bool Continuation<Function, ResultType, ParentResultType>::execute()
+{
+ Q_ASSERT(parentFuture.isFinished());
+
+ if (parentFuture.isCanceled()) {
+#ifndef QT_NO_EXCEPTIONS
+ if (parentFuture.d.exceptionStore().hasException()) {
+ // If the continuation doesn't take a QFuture argument, propagate the exception
+ // to the caller, by reporting it. If the continuation takes a QFuture argument,
+ // the user may want to catch the exception inside the continuation, to not
+ // interrupt the continuation chain, so don't report anything yet.
+ if constexpr (!std::is_invocable_v<std::decay_t<Function>, QFuture<ParentResultType>>) {
+ promise.reportStarted();
+ const QException *e = parentFuture.d.exceptionStore().exception().exception();
+ promise.reportException(*e);
+ promise.reportFinished();
+ return false;
+ }
+ } else
+#endif
+ {
+ promise.reportStarted();
+ promise.reportCanceled();
+ promise.reportFinished();
+ return false;
+ }
+ }
+
+ runImpl();
+ return true;
+}
+
+template<typename Function, typename ResultType, typename ParentResultType>
+void Continuation<Function, ResultType, ParentResultType>::create(Function &&func,
+ QFuture<ParentResultType> *f,
+ QFutureInterface<ResultType> &p,
+ QtFuture::Launch policy)
+{
+ Q_ASSERT(f);
+
+ QThreadPool *pool = nullptr;
+
+ bool launchAsync = (policy == QtFuture::Launch::Async);
+ if (policy == QtFuture::Launch::Inherit) {
+ launchAsync = f->d.launchAsync();
+
+ // If the parent future was using a custom thread pool, inherit it as well.
+ if (launchAsync && f->d.threadPool()) {
+ pool = f->d.threadPool();
+ p.setThreadPool(pool);
+ }
+ }
+
+ Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr;
+ if (launchAsync) {
+ continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
+ std::forward<Function>(func), *f, p, pool);
+ } else {
+ continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>(
+ std::forward<Function>(func), *f, p);
+ }
+
+ p.setLaunchAsync(launchAsync);
+
+ auto continuation = [continuationJob, policy, launchAsync]() mutable {
+ bool isLaunched = continuationJob->execute();
+ // If continuation is successfully launched, AsyncContinuation will be deleted
+ // by the QThreadPool which has started it. Synchronous continuation will be
+ // executed immediately, so it's safe to always delete it here.
+ if (!(launchAsync && isLaunched)) {
+ delete continuationJob;
+ continuationJob = nullptr;
+ }
+ };
+
+ f->d.setContinuation(std::move(continuation));
+}
+
+template<typename Function, typename ResultType, typename ParentResultType>
+void Continuation<Function, ResultType, ParentResultType>::create(Function &&func,
+ QFuture<ParentResultType> *f,
+ QFutureInterface<ResultType> &p,
+ QThreadPool *pool)
+{
+ Q_ASSERT(f);
+
+ auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
+ std::forward<Function>(func), *f, p, pool);
+ p.setLaunchAsync(true);
+ p.setThreadPool(pool);
+
+ auto continuation = [continuationJob]() mutable {
+ bool isLaunched = continuationJob->execute();
+ // If continuation is successfully launched, AsyncContinuation will be deleted
+ // by the QThreadPool which has started it.
+ if (!isLaunched) {
+ delete continuationJob;
+ continuationJob = nullptr;
+ }
+ };
+
+ f->d.setContinuation(continuation);
+}
+
+} // namespace QtPrivate
+
+QT_END_NAMESPACE
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp
index e6380a8732..074e28d8df 100644
--- a/src/corelib/thread/qfutureinterface.cpp
+++ b/src/corelib/thread/qfutureinterface.cpp
@@ -191,6 +191,11 @@ bool QFutureInterfaceBase::isResultReadyAt(int index) const
return d->internal_isResultReadyAt(index);
}
+bool QFutureInterfaceBase::isRunningOrPending() const
+{
+ return queryState(static_cast<State>(Running | Pending));
+}
+
bool QFutureInterfaceBase::waitForNextResult()
{
QMutexLocker lock(&d->m_mutex);
@@ -315,7 +320,7 @@ void QFutureInterfaceBase::waitForResult(int resultIndex)
d->m_exceptionStore.throwPossibleException();
QMutexLocker lock(&d->m_mutex);
- if (!isRunning())
+ if (!isRunningOrPending())
return;
lock.unlock();
@@ -326,7 +331,7 @@ void QFutureInterfaceBase::waitForResult(int resultIndex)
lock.relock();
const int waitIndex = (resultIndex == -1) ? INT_MAX : resultIndex;
- while (isRunning() && !d->internal_isResultReadyAt(waitIndex))
+ while (isRunningOrPending() && !d->internal_isResultReadyAt(waitIndex))
d->waitCondition.wait(&d->m_mutex);
d->m_exceptionStore.throwPossibleException();
@@ -335,7 +340,7 @@ void QFutureInterfaceBase::waitForResult(int resultIndex)
void QFutureInterfaceBase::waitForFinished()
{
QMutexLocker lock(&d->m_mutex);
- const bool alreadyFinished = !isRunning();
+ const bool alreadyFinished = !isRunningOrPending();
lock.unlock();
if (!alreadyFinished) {
@@ -343,7 +348,7 @@ void QFutureInterfaceBase::waitForFinished()
lock.relock();
- while (isRunning())
+ while (isRunningOrPending())
d->waitCondition.wait(&d->m_mutex);
}
@@ -386,6 +391,11 @@ void QFutureInterfaceBase::setThreadPool(QThreadPool *pool)
d->m_pool = pool;
}
+QThreadPool *QFutureInterfaceBase::threadPool() const
+{
+ return d->m_pool;
+}
+
void QFutureInterfaceBase::setFilterMode(bool enable)
{
QMutexLocker locker(&d->m_mutex);
@@ -604,4 +614,36 @@ void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState)
state.storeRelaxed(newState);
}
+void QFutureInterfaceBase::setContinuation(std::function<void()> func)
+{
+ QMutexLocker lock(&d->continuationMutex);
+ // If the state is ready, run continuation immediately,
+ // otherwise save it for later.
+ if (isFinished()) {
+ lock.unlock();
+ func();
+ } else {
+ d->continuation = std::move(func);
+ }
+}
+
+void QFutureInterfaceBase::runContinuation() const
+{
+ QMutexLocker lock(&d->continuationMutex);
+ if (d->continuation) {
+ lock.unlock();
+ d->continuation();
+ }
+}
+
+void QFutureInterfaceBase::setLaunchAsync(bool value)
+{
+ d->launchAsync = value;
+}
+
+bool QFutureInterfaceBase::launchAsync() const
+{
+ return d->launchAsync;
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h
index 43dfd6bac4..c2e884911f 100644
--- a/src/corelib/thread/qfutureinterface.h
+++ b/src/corelib/thread/qfutureinterface.h
@@ -58,6 +58,11 @@ class QFutureInterfaceBasePrivate;
class QFutureWatcherBase;
class QFutureWatcherBasePrivate;
+namespace QtPrivate {
+template<typename Function, typename ResultType, typename ParentResultType>
+class Continuation;
+}
+
class Q_CORE_EXPORT QFutureInterfaceBase
{
public:
@@ -68,7 +73,9 @@ public:
Finished = 0x04,
Canceled = 0x08,
Paused = 0x10,
- Throttled = 0x20
+ Throttled = 0x20,
+ // Pending means that the future depends on another one, which is not finished yet
+ Pending = 0x40
};
QFutureInterfaceBase(State initialState = NoState);
@@ -86,6 +93,7 @@ public:
void setRunnable(QRunnable *runnable);
void setThreadPool(QThreadPool *pool);
+ QThreadPool *threadPool() const;
void setFilterMode(bool enable);
void setProgressRange(int minimum, int maximum);
int progressMinimum() const;
@@ -141,6 +149,18 @@ private:
private:
friend class QFutureWatcherBase;
friend class QFutureWatcherBasePrivate;
+
+ template<typename Function, typename ResultType, typename ParentResultType>
+ friend class QtPrivate::Continuation;
+
+protected:
+ void setContinuation(std::function<void()> func);
+ void runContinuation() const;
+
+ void setLaunchAsync(bool value);
+ bool launchAsync() const;
+
+ bool isRunningOrPending() const;
};
template <typename T>
@@ -239,6 +259,7 @@ inline void QFutureInterface<T>::reportFinished(const T *result)
if (result)
reportResult(result);
QFutureInterfaceBase::reportFinished();
+ QFutureInterfaceBase::runContinuation();
}
template <typename T>
@@ -292,7 +313,11 @@ public:
void reportResult(const void *, int) { }
void reportResults(const QVector<void> &, int) { }
- void reportFinished(const void * = nullptr) { QFutureInterfaceBase::reportFinished(); }
+ void reportFinished(const void * = nullptr)
+ {
+ QFutureInterfaceBase::reportFinished();
+ QFutureInterfaceBase::runContinuation();
+ }
};
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h
index b297dff633..306b02e269 100644
--- a/src/corelib/thread/qfutureinterface_p.h
+++ b/src/corelib/thread/qfutureinterface_p.h
@@ -191,6 +191,12 @@ public:
void disconnectOutputInterface(QFutureCallOutInterface *iface);
void setState(QFutureInterfaceBase::State state);
+
+ // Wrapper for continuation
+ std::function<void()> continuation;
+ QBasicMutex continuationMutex;
+
+ bool launchAsync = false;
};
QT_END_NAMESPACE
diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri
index 25cf68a324..e3d791fee7 100644
--- a/src/corelib/thread/thread.pri
+++ b/src/corelib/thread/thread.pri
@@ -66,6 +66,7 @@ qtConfig(future) {
HEADERS += \
thread/qexception.h \
thread/qfuture.h \
+ thread/qfuture_impl.h \
thread/qfutureinterface.h \
thread/qfutureinterface_p.h \
thread/qfuturesynchronizer.h \