summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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 \