summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qfuture_impl.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread/qfuture_impl.h')
-rw-r--r--src/corelib/thread/qfuture_impl.h204
1 files changed, 123 insertions, 81 deletions
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
index 8e96b943ef..351093adc7 100644
--- a/src/corelib/thread/qfuture_impl.h
+++ b/src/corelib/thread/qfuture_impl.h
@@ -14,9 +14,10 @@
#include <QtCore/qfutureinterface.h>
#include <QtCore/qthreadpool.h>
#include <QtCore/qexception.h>
-#include <QtCore/qpointer.h>
#include <QtCore/qpromise.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
//
@@ -272,6 +273,12 @@ using IsRandomAccessible =
std::begin(std::declval<Sequence>()))>>::iterator_category,
std::random_access_iterator_tag>;
+template<class Sequence>
+using HasInputIterator =
+ std::is_convertible<typename std::iterator_traits<std::decay_t<decltype(
+ std::begin(std::declval<Sequence>()))>>::iterator_category,
+ std::input_iterator_tag>;
+
template<class Iterator>
using IsForwardIterable =
std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category,
@@ -280,6 +287,7 @@ using IsForwardIterable =
template<typename Function, typename ResultType, typename ParentResultType>
class Continuation
{
+ Q_DISABLE_COPY_MOVE(Continuation)
public:
template<typename F = Function>
Continuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
@@ -527,18 +535,18 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
fi.setLaunchAsync(launchAsync);
- auto continuation = [func = std::forward<F>(func), fi, promise = QPromise(fi), pool,
+ auto continuation = [func = std::forward<F>(func), fi, promise_ = QPromise(fi), pool,
launchAsync](const QFutureInterfaceBase &parentData) mutable {
const auto parent = QFutureInterface<ParentResultType>(parentData).future();
Continuation<Function, ResultType, ParentResultType> *continuationJob = nullptr;
if (launchAsync) {
auto asyncJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
- std::forward<Function>(func), parent, std::move(promise), pool);
+ std::forward<Function>(func), parent, std::move(promise_), pool);
fi.setRunnable(asyncJob);
continuationJob = asyncJob;
} else {
continuationJob = new SyncContinuation<Function, ResultType, ParentResultType>(
- std::forward<Function>(func), parent, std::move(promise));
+ std::forward<Function>(func), parent, std::move(promise_));
}
bool isLaunched = continuationJob->execute();
@@ -565,11 +573,11 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
fi.setLaunchAsync(true);
fi.setThreadPool(pool);
- auto continuation = [func = std::forward<F>(func), promise = QPromise(fi),
+ auto continuation = [func = std::forward<F>(func), promise_ = QPromise(fi),
pool](const QFutureInterfaceBase &parentData) mutable {
const auto parent = QFutureInterface<ParentResultType>(parentData).future();
auto continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>(
- std::forward<Function>(func), parent, std::move(promise), pool);
+ std::forward<Function>(func), parent, std::move(promise_), pool);
bool isLaunched = continuationJob->execute();
// If continuation is successfully launched, AsyncContinuation will be deleted
// by the QThreadPool which has started it.
@@ -581,6 +589,15 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
}
+template <typename Continuation>
+void watchContinuation(const QObject *context, Continuation &&c, QFutureInterfaceBase &fi)
+{
+ using Prototype = typename QtPrivate::Callable<Continuation>::Function;
+ watchContinuationImpl(context,
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Continuation>(c)),
+ fi);
+}
+
template<typename Function, typename ResultType, typename ParentResultType>
template<typename F>
void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
@@ -589,21 +606,19 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
QObject *context)
{
Q_ASSERT(f);
-
- auto continuation = [func = std::forward<F>(func), promise = QPromise(fi),
- 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), promise = std::move(promise), parent]() mutable {
- SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
- std::forward<Function>(func), parent, std::move(promise));
- continuationJob.execute();
- });
+ Q_ASSERT(context);
+
+ // When the context object is destroyed, the signal-slot connection is broken and the
+ // continuation callback is destroyed. The promise that is created in the capture list is
+ // destroyed and, if it is not yet finished, cancelled.
+ auto continuation = [func = std::forward<F>(func), parent = *f,
+ promise_ = QPromise(fi)]() mutable {
+ SyncContinuation<Function, ResultType, ParentResultType> continuationJob(
+ std::forward<Function>(func), parent, std::move(promise_));
+ continuationJob.execute();
};
- f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
+
+ QtPrivate::watchContinuation(context, std::move(continuation), f->d);
}
template<typename Function, typename ResultType, typename ParentResultType>
@@ -669,11 +684,11 @@ void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultTy
{
Q_ASSERT(future);
- auto failureContinuation = [function = std::forward<F>(function), promise = QPromise(fi)](
+ auto failureContinuation = [function = std::forward<F>(function), promise_ = QPromise(fi)](
const QFutureInterfaceBase &parentData) mutable {
const auto parent = QFutureInterface<ResultType>(parentData).future();
FailureHandler<Function, ResultType> failureHandler(std::forward<Function>(function),
- parent, std::move(promise));
+ parent, std::move(promise_));
failureHandler.run();
};
@@ -687,22 +702,15 @@ void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultTy
QObject *context)
{
Q_ASSERT(future);
+ Q_ASSERT(context);
+ auto failureContinuation = [function = std::forward<F>(function),
+ parent = *future, promise_ = QPromise(fi)]() mutable {
+ FailureHandler<Function, ResultType> failureHandler(
+ std::forward<Function>(function), parent, std::move(promise_));
+ failureHandler.run();
+ };
- auto failureContinuation =
- [function = std::forward<F>(function), promise = QPromise(fi),
- 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 = std::move(promise), parent]() mutable {
- FailureHandler<Function, ResultType> failureHandler(
- std::forward<Function>(function), parent, std::move(promise));
- failureHandler.run();
- });
- };
-
- future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation)));
+ QtPrivate::watchContinuation(context, std::move(failureContinuation), future->d);
}
template<class Function, class ResultType>
@@ -719,6 +727,8 @@ void FailureHandler<Function, ResultType>::run()
} else {
handleException<ArgType>();
}
+ } else if (parentFuture.d.isChainCanceled()) {
+ promise.future().cancel();
} else {
QtPrivate::fulfillPromise(promise, parentFuture);
}
@@ -788,19 +798,13 @@ public:
QObject *context)
{
Q_ASSERT(future);
- auto canceledContinuation = [promise = QPromise(fi), 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 = std::move(promise), parentFuture,
- handler = std::forward<F>(handler)]() mutable {
- run(std::forward<F>(handler), parentFuture, std::move(promise));
- });
+ Q_ASSERT(context);
+ auto canceledContinuation = [handler = std::forward<F>(handler),
+ parentFuture = *future, promise = QPromise(fi)]() mutable {
+ run(std::forward<F>(handler), parentFuture, std::move(promise));
};
- future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation)));
+ QtPrivate::watchContinuation(context, std::move(canceledContinuation), future->d);
}
template<class F = Function>
@@ -884,6 +888,16 @@ struct UnwrapHandler
}
};
+template<typename ValueType>
+QFuture<ValueType> makeReadyRangeFutureImpl(const QList<ValueType> &values)
+{
+ QFutureInterface<ValueType> promise;
+ promise.reportStarted();
+ promise.reportResults(values);
+ promise.reportFinished();
+ return promise.future();
+}
+
} // namespace QtPrivate
namespace QtFuture {
@@ -949,8 +963,37 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal)
return promise.future();
}
-template<typename T, typename = QtPrivate::EnableForNonVoid<T>>
-static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
+template<typename Container>
+using if_container_with_input_iterators =
+ std::enable_if_t<QtPrivate::HasInputIterator<Container>::value, bool>;
+
+template<typename Container>
+using ContainedType =
+ typename std::iterator_traits<decltype(
+ std::cbegin(std::declval<Container&>()))>::value_type;
+
+template<typename Container, if_container_with_input_iterators<Container> = true>
+static QFuture<ContainedType<Container>> makeReadyRangeFuture(Container &&container)
+{
+ // handle QList<T> separately, because reportResults() takes a QList
+ // as an input
+ using ValueType = ContainedType<Container>;
+ if constexpr (std::is_convertible_v<q20::remove_cvref_t<Container>, QList<ValueType>>) {
+ return QtPrivate::makeReadyRangeFutureImpl(container);
+ } else {
+ return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{std::cbegin(container),
+ std::cend(container)});
+ }
+}
+
+template<typename ValueType>
+static QFuture<ValueType> makeReadyRangeFuture(std::initializer_list<ValueType> values)
+{
+ return QtPrivate::makeReadyRangeFutureImpl(QList<ValueType>{values});
+}
+
+template<typename T>
+static QFuture<std::decay_t<T>> makeReadyValueFuture(T &&value)
{
QFutureInterface<std::decay_t<T>> promise;
promise.reportStarted();
@@ -960,30 +1003,26 @@ static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
return promise.future();
}
-#if defined(Q_CLANG_QDOC)
-static QFuture<void> makeReadyFuture()
-#else
-template<typename T = void>
-static QFuture<T> makeReadyFuture()
-#endif
-{
- QFutureInterface<T> promise;
- promise.reportStarted();
- promise.reportFinished();
+Q_CORE_EXPORT QFuture<void> makeReadyVoidFuture(); // implemented in qfutureinterface.cpp
- return promise.future();
+#if QT_DEPRECATED_SINCE(6, 10)
+template<typename T, typename = QtPrivate::EnableForNonVoid<T>>
+QT_DEPRECATED_VERSION_X(6, 10, "Use makeReadyValueFuture() instead.")
+static QFuture<std::decay_t<T>> makeReadyFuture(T &&value)
+{
+ return makeReadyValueFuture(std::forward<T>(value));
}
+// the void specialization is moved to the end of qfuture.h, because it now
+// uses makeReadyVoidFuture() and required QFuture<void> to be defined.
+
template<typename T>
+QT_DEPRECATED_VERSION_X(6, 10, "Use makeReadyRangeFuture() instead.")
static QFuture<T> makeReadyFuture(const QList<T> &values)
{
- QFutureInterface<T> promise;
- promise.reportStarted();
- promise.reportResults(values);
- promise.reportFinished();
-
- return promise.future();
+ return makeReadyRangeFuture(values);
}
+#endif // QT_DEPRECATED_SINCE(6, 10)
#ifndef QT_NO_EXCEPTIONS
@@ -1058,14 +1097,15 @@ struct WhenAnyContext
};
template<qsizetype Index, typename ContextType, typename... Ts>
-void addCompletionHandlersImpl(const QSharedPointer<ContextType> &context,
+void addCompletionHandlersImpl(const std::shared_ptr<ContextType> &context,
const std::tuple<Ts...> &t)
{
auto future = std::get<Index>(t);
using ResultType = typename ContextType::ValueType;
- future.then([context](const std::tuple_element_t<Index, std::tuple<Ts...>> &f) {
+ // Need context=context so that the compiler does not infer the captured variable's type as 'const'
+ future.then([context=context](const std::tuple_element_t<Index, std::tuple<Ts...>> &f) {
context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, f });
- }).onCanceled([context, future]() {
+ }).onCanceled([context=context, future]() {
context->checkForCompletion(Index, ResultType { std::in_place_index<Index>, future });
});
@@ -1074,7 +1114,7 @@ void addCompletionHandlersImpl(const QSharedPointer<ContextType> &context,
}
template<typename ContextType, typename... Ts>
-void addCompletionHandlers(const QSharedPointer<ContextType> &context, const std::tuple<Ts...> &t)
+void addCompletionHandlers(const std::shared_ptr<ContextType> &context, const std::tuple<Ts...> &t)
{
constexpr qsizetype size = std::tuple_size<std::tuple<Ts...>>::value;
addCompletionHandlersImpl<size - 1, ContextType, Ts...>(context, t);
@@ -1085,17 +1125,18 @@ QFuture<OutputSequence> whenAllImpl(InputIt first, InputIt last)
{
const qsizetype size = std::distance(first, last);
if (size == 0)
- return QtFuture::makeReadyFuture(OutputSequence());
+ return QtFuture::makeReadyValueFuture(OutputSequence());
- auto context = QSharedPointer<QtPrivate::WhenAllContext<OutputSequence>>::create(size);
+ const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
context->futures.resize(size);
context->promise.start();
qsizetype idx = 0;
for (auto it = first; it != last; ++it, ++idx) {
- it->then([context, idx](const ValueType &f) {
+ // Need context=context so that the compiler does not infer the captured variable's type as 'const'
+ it->then([context=context, idx](const ValueType &f) {
context->checkForCompletion(idx, f);
- }).onCanceled([context, idx, f = *it] {
+ }).onCanceled([context=context, idx, f = *it] {
context->checkForCompletion(idx, f);
});
}
@@ -1106,7 +1147,7 @@ template<typename OutputSequence, typename... Futures>
QFuture<OutputSequence> whenAllImpl(Futures &&... futures)
{
constexpr qsizetype size = sizeof...(Futures);
- auto context = QSharedPointer<QtPrivate::WhenAllContext<OutputSequence>>::create(size);
+ const auto context = std::make_shared<QtPrivate::WhenAllContext<OutputSequence>>(size);
context->futures.resize(size);
context->promise.start();
@@ -1124,18 +1165,19 @@ QFuture<QtFuture::WhenAnyResult<typename Future<ValueType>::type>> whenAnyImpl(I
const qsizetype size = std::distance(first, last);
if (size == 0) {
- return QtFuture::makeReadyFuture(
+ return QtFuture::makeReadyValueFuture(
QtFuture::WhenAnyResult { qsizetype(-1), QFuture<PackagedType>() });
}
- auto context = QSharedPointer<QtPrivate::WhenAnyContext<ResultType>>::create();
+ const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>();
context->promise.start();
qsizetype idx = 0;
for (auto it = first; it != last; ++it, ++idx) {
- it->then([context, idx](const ValueType &f) {
+ // Need context=context so that the compiler does not infer the captured variable's type as 'const'
+ it->then([context=context, idx](const ValueType &f) {
context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f });
- }).onCanceled([context, idx, f = *it] {
+ }).onCanceled([context=context, idx, f = *it] {
context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f });
});
}
@@ -1147,7 +1189,7 @@ QFuture<std::variant<std::decay_t<Futures>...>> whenAnyImpl(Futures &&... future
{
using ResultType = std::variant<std::decay_t<Futures>...>;
- auto context = QSharedPointer<QtPrivate::WhenAnyContext<ResultType>>::create();
+ const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>();
context->promise.start();
QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...));