diff options
Diffstat (limited to 'src/corelib/thread/qfuture_impl.h')
-rw-r--r-- | src/corelib/thread/qfuture_impl.h | 208 |
1 files changed, 125 insertions, 83 deletions
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index 3435ea382b..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) @@ -423,7 +431,7 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction() fulfillPromiseWithResult(); } else { // This assert normally should never fail, this is to make sure - // that nothing unexpected happend. + // that nothing unexpected happened. static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>, "The continuation is not invocable with the provided arguments"); fulfillPromise(parentFuture); @@ -438,7 +446,7 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction() fulfillVoidPromise(); } else { // This assert normally should never fail, this is to make sure - // that nothing unexpected happend. + // that nothing unexpected happened. static_assert(std::is_invocable_v<Function, QFuture<ParentResultType>>, "The continuation is not invocable with the provided arguments"); function(parentFuture); @@ -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)...)); |