diff options
Diffstat (limited to 'src/corelib/thread/qfuture_impl.h')
-rw-r--r-- | src/corelib/thread/qfuture_impl.h | 593 |
1 files changed, 331 insertions, 262 deletions
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index 136892ddaf..351093adc7 100644 --- a/src/corelib/thread/qfuture_impl.h +++ b/src/corelib/thread/qfuture_impl.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QFUTURE_H #error Do not include qfuture_impl.h directly @@ -50,7 +14,9 @@ #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 @@ -82,10 +48,6 @@ WhenAnyResult(qsizetype, const QFuture<T> &) -> WhenAnyResult<T>; namespace QtPrivate { -template<class T, class U> -using EnableIfSameOrConvertible = std::enable_if_t<std::is_same_v<T, U> - || std::is_convertible_v<T, U>>; - template<class T> using EnableForVoid = std::enable_if_t<std::is_same_v<T, void>>; @@ -129,7 +91,48 @@ struct ResultTypeHelper< using ResultType = std::invoke_result_t<std::decay_t<F>>; }; +// Helpers to remove QPrivateSignal argument from the list of arguments + +template<class T, class Enable = void> +inline constexpr bool IsPrivateSignalArg = false; + +template<class T> +inline constexpr bool IsPrivateSignalArg<T, typename std::enable_if_t< + // finds injected-class-name, the 'class' avoids falling into the rules of [class.qual]/2: + std::is_class_v<class T::QPrivateSignal> + >> = true; + +template<class Tuple, std::size_t... I> +auto cutTuple(Tuple &&t, std::index_sequence<I...>) +{ + return std::make_tuple(std::get<I>(t)...); +} + +template<class Arg, class... Args> +auto createTuple(Arg &&arg, Args &&... args) +{ + using TupleType = std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>; + constexpr auto Size = sizeof...(Args); // One less than the size of all arguments + if constexpr (QtPrivate::IsPrivateSignalArg<std::tuple_element_t<Size, TupleType>>) { + if constexpr (Size == 1) { + return std::forward<Arg>(arg); + } else { + return cutTuple(std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...), + std::make_index_sequence<Size>()); + } + } else { + return std::make_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...); + } +} + // Helpers to resolve argument types of callables. + +template<class Arg, class... Args> +using FilterLastPrivateSignalArg = + std::conditional_t<(sizeof...(Args) > 0), + std::invoke_result_t<decltype(createTuple<Arg, Args...>), Arg, Args...>, + std::conditional_t<IsPrivateSignalArg<Arg>, void, Arg>>; + template<typename...> struct ArgsType; @@ -140,9 +143,7 @@ struct ArgsType<Arg, Args...> using PromiseType = void; using IsPromise = std::false_type; static const bool HasExtraArgs = (sizeof...(Args) > 0); - using AllArgs = - std::conditional_t<HasExtraArgs, std::tuple<std::decay_t<Arg>, std::decay_t<Args>...>, - std::decay_t<Arg>>; + using AllArgs = FilterLastPrivateSignalArg<std::decay_t<Arg>, std::decay_t<Args>...>; template<class Class, class Callable> static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, Arg, Args...>; @@ -155,9 +156,7 @@ struct ArgsType<QPromise<Arg> &, Args...> using PromiseType = Arg; using IsPromise = std::true_type; static const bool HasExtraArgs = (sizeof...(Args) > 0); - using AllArgs = - std::conditional_t<HasExtraArgs, std::tuple<std::decay_t<QPromise<Arg> &>, std::decay_t<Args>...>, - std::decay_t<QPromise<Arg> &>>; + using AllArgs = FilterLastPrivateSignalArg<QPromise<Arg>, std::decay_t<Args>...>; template<class Class, class Callable> static const bool CanInvokeWithArgs = std::is_invocable_v<Callable, Class, QPromise<Arg> &, Args...>; @@ -245,17 +244,6 @@ template<class Class, class Callable> using EnableIfInvocable = std::enable_if_t< QtPrivate::ArgResolver<Callable>::template CanInvokeWithArgs<Class, Callable>>; -template<class> -struct isTuple : std::false_type -{ -}; -template<class... T> -struct isTuple<std::tuple<T...>> : std::true_type -{ -}; -template<class T> -inline constexpr bool isTupleV = isTuple<T>::value; - template<class T> inline constexpr bool isQFutureV = false; @@ -285,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, @@ -293,11 +287,11 @@ 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, - const QFutureInterface<ResultType> &p) - : promise(p), parentFuture(f), function(std::forward<F>(func)) + Continuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p) + : promise(std::move(p)), parentFuture(f), function(std::forward<F>(func)) { } virtual ~Continuation() = default; @@ -305,15 +299,15 @@ public: bool execute(); template<typename F = Function> - static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p, + static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi, QtFuture::Launch policy); template<typename F = Function> - static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p, + static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi, QThreadPool *pool); template<typename F = Function> - static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &p, + static void create(F &&func, QFuture<ParentResultType> *f, QFutureInterface<ResultType> &fi, QObject *context); private: @@ -330,7 +324,7 @@ protected: void runFunction(); protected: - QFutureInterface<ResultType> promise; + QPromise<ResultType> promise; QFuture<ParentResultType> parentFuture; Function function; }; @@ -340,9 +334,9 @@ class SyncContinuation final : public Continuation<Function, ResultType, ParentR { public: template<typename F = Function> - SyncContinuation(F &&func, const QFuture<ParentResultType> &f, - const QFutureInterface<ResultType> &p) - : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, p) + SyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p) + : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, + std::move(p)) { } @@ -358,12 +352,12 @@ class AsyncContinuation final : public QRunnable, { public: template<typename F = Function> - AsyncContinuation(F &&func, const QFuture<ParentResultType> &f, - const QFutureInterface<ResultType> &p, QThreadPool *pool = nullptr) - : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, p), + AsyncContinuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p, + QThreadPool *pool = nullptr) + : Continuation<Function, ResultType, ParentResultType>(std::forward<F>(func), f, + std::move(p)), threadPool(pool) { - this->promise.setRunnable(this); } ~AsyncContinuation() override = default; @@ -392,15 +386,15 @@ class FailureHandler public: template<typename F = Function> static void create(F &&function, QFuture<ResultType> *future, - const QFutureInterface<ResultType> &promise); + const QFutureInterface<ResultType> &fi); template<typename F = Function> - static void create(F &&function, QFuture<ResultType> *future, - QFutureInterface<ResultType> &promise, QObject *context); + static void create(F &&function, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi, + QObject *context); template<typename F = Function> - FailureHandler(F &&func, const QFuture<ResultType> &f, const QFutureInterface<ResultType> &p) - : promise(p), parentFuture(f), handler(std::forward<F>(func)) + FailureHandler(F &&func, const QFuture<ResultType> &f, QPromise<ResultType> &&p) + : promise(std::move(p)), parentFuture(f), handler(std::forward<F>(func)) { } @@ -413,7 +407,7 @@ private: void handleAllExceptions(); private: - QFutureInterface<ResultType> promise; + QPromise<ResultType> promise; QFuture<ResultType> parentFuture; Function handler; }; @@ -423,7 +417,7 @@ private: template<typename Function, typename ResultType, typename ParentResultType> void Continuation<Function, ResultType, ParentResultType>::runFunction() { - promise.reportStarted(); + promise.start(); Q_ASSERT(parentFuture.isFinished()); @@ -437,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); @@ -452,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); @@ -460,10 +454,10 @@ void Continuation<Function, ResultType, ParentResultType>::runFunction() } #ifndef QT_NO_EXCEPTIONS } catch (...) { - promise.reportException(std::current_exception()); + promise.setException(std::current_exception()); } #endif - promise.reportFinished(); + promise.finish(); } template<typename Function, typename ResultType, typename ParentResultType> @@ -479,17 +473,17 @@ bool Continuation<Function, ResultType, ParentResultType>::execute() // 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(); - promise.reportException(parentFuture.d.exceptionStore().exception()); - promise.reportFinished(); + promise.start(); + promise.setException(parentFuture.d.exceptionStore().exception()); + promise.finish(); return false; } } else #endif { - promise.reportStarted(); - promise.reportCanceled(); - promise.reportFinished(); + promise.start(); + promise.future().cancel(); + promise.finish(); return false; } } @@ -502,18 +496,26 @@ bool Continuation<Function, ResultType, ParentResultType>::execute() template<class Function> struct ContinuationWrapper { - ContinuationWrapper(Function &&f) : function(QSharedPointer<Function>::create(std::move(f))) { } - void operator()(const QFutureInterfaceBase &parentData) { (*function)(parentData); } + ContinuationWrapper(Function &&f) : function(std::move(f)) { } + ContinuationWrapper(const ContinuationWrapper &other) + : function(std::move(const_cast<ContinuationWrapper &>(other).function)) + { + Q_ASSERT_X(false, "QFuture", "Continuation shouldn't be copied"); + } + ContinuationWrapper(ContinuationWrapper &&other) = default; + ContinuationWrapper &operator=(ContinuationWrapper &&) = default; + + void operator()(const QFutureInterfaceBase &parentData) { function(parentData); } private: - QSharedPointer<Function> function; + Function function; }; template<typename Function, typename ResultType, typename ParentResultType> template<typename F> void Continuation<Function, ResultType, ParentResultType>::create(F &&func, QFuture<ParentResultType> *f, - QFutureInterface<ResultType> &p, + QFutureInterface<ResultType> &fi, QtFuture::Launch policy) { Q_ASSERT(f); @@ -527,22 +529,24 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func, // 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); + fi.setThreadPool(pool); } } - p.setLaunchAsync(launchAsync); + fi.setLaunchAsync(launchAsync); - auto continuation = [func = std::forward<F>(func), p, 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) { - continuationJob = new AsyncContinuation<Function, ResultType, ParentResultType>( - std::forward<Function>(func), parent, p, pool); + auto asyncJob = new AsyncContinuation<Function, ResultType, ParentResultType>( + 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, p); + std::forward<Function>(func), parent, std::move(promise_)); } bool isLaunched = continuationJob->execute(); @@ -554,29 +558,26 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func, continuationJob = nullptr; } }; - if constexpr (!std::is_copy_constructible_v<Function>) - f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d); - else - f->d.setContinuation(std::move(continuation), p.d); + f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d); } template<typename Function, typename ResultType, typename ParentResultType> template<typename F> void Continuation<Function, ResultType, ParentResultType>::create(F &&func, QFuture<ParentResultType> *f, - QFutureInterface<ResultType> &p, + QFutureInterface<ResultType> &fi, QThreadPool *pool) { Q_ASSERT(f); - p.setLaunchAsync(true); - p.setThreadPool(pool); + fi.setLaunchAsync(true); + fi.setThreadPool(pool); - auto continuation = [func = std::forward<F>(func), p, + 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, p, 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. @@ -585,37 +586,39 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func, continuationJob = nullptr; } }; + f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d); +} - if constexpr (!std::is_copy_constructible_v<Function>) - f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d); - else - f->d.setContinuation(std::move(continuation), p.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, QFuture<ParentResultType> *f, - QFutureInterface<ResultType> &p, + QFutureInterface<ResultType> &fi, QObject *context) { Q_ASSERT(f); - - auto continuation = [func = std::forward<F>(func), p, 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), p, parent]() mutable { - SyncContinuation<Function, ResultType, ParentResultType> continuationJob( - std::forward<Function>(func), parent, p); - 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(); }; - if constexpr (!std::is_copy_constructible_v<Function>) - f->d.setContinuation(ContinuationWrapper(std::move(continuation)), p.d); - else - f->d.setContinuation(std::move(continuation), p.d); + QtPrivate::watchContinuation(context, std::move(continuation), f->d); } template<typename Function, typename ResultType, typename ParentResultType> @@ -649,32 +652,27 @@ template<typename Function, typename ResultType, typename ParentResultType> template<class... Args> void Continuation<Function, ResultType, ParentResultType>::fulfillPromise(Args &&... args) { - if constexpr (std::is_copy_constructible_v<ResultType>) - promise.reportResult(std::invoke(function, std::forward<Args>(args)...)); - else - promise.reportAndMoveResult(std::invoke(function, std::forward<Args>(args)...)); + promise.addResult(std::invoke(function, std::forward<Args>(args)...)); } template<class T> -void fulfillPromise(QFutureInterface<T> &promise, QFuture<T> &future) +void fulfillPromise(QPromise<T> &promise, QFuture<T> &future) { if constexpr (!std::is_void_v<T>) { if constexpr (std::is_copy_constructible_v<T>) - promise.reportResult(future.result()); + promise.addResult(future.result()); else - promise.reportAndMoveResult(future.takeResult()); + promise.addResult(future.takeResult()); } } template<class T, class Function> -void fulfillPromise(QFutureInterface<T> &promise, Function &&handler) +void fulfillPromise(QPromise<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()); + promise.addResult(handler()); } #ifndef QT_NO_EXCEPTIONS @@ -682,49 +680,37 @@ void fulfillPromise(QFutureInterface<T> &promise, Function &&handler) template<class Function, class ResultType> template<class F> void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future, - const QFutureInterface<ResultType> &promise) + const QFutureInterface<ResultType> &fi) { Q_ASSERT(future); - auto failureContinuation = [function = std::forward<F>(function), - promise](const QFutureInterfaceBase &parentData) mutable { + 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, promise); + parent, std::move(promise_)); failureHandler.run(); }; - if constexpr (!std::is_copy_constructible_v<Function>) - future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation))); - else - future->d.setContinuation(std::move(failureContinuation)); + future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation))); } template<class Function, class ResultType> template<class F> void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultType> *future, - QFutureInterface<ResultType> &promise, + QFutureInterface<ResultType> &fi, 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, - 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, parent]() mutable { - FailureHandler<Function, ResultType> failureHandler( - std::forward<Function>(function), parent, promise); - failureHandler.run(); - }); - }; - - if constexpr (!std::is_copy_constructible_v<Function>) - future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation))); - else - future->d.setContinuation(std::move(failureContinuation)); + QtPrivate::watchContinuation(context, std::move(failureContinuation), future->d); } template<class Function, class ResultType> @@ -732,7 +718,7 @@ void FailureHandler<Function, ResultType>::run() { Q_ASSERT(parentFuture.isFinished()); - promise.reportStarted(); + promise.start(); if (parentFuture.d.hasException()) { using ArgType = typename QtPrivate::ArgResolver<Function>::First; @@ -741,10 +727,12 @@ void FailureHandler<Function, ResultType>::run() } else { handleException<ArgType>(); } + } else if (parentFuture.d.isChainCanceled()) { + promise.future().cancel(); } else { QtPrivate::fulfillPromise(promise, parentFuture); } - promise.reportFinished(); + promise.finish(); } template<class Function, class ResultType> @@ -757,21 +745,17 @@ void FailureHandler<Function, ResultType>::handleException() } catch (const ArgType &e) { try { // Handle exceptions matching with the handler's argument type - if constexpr (std::is_void_v<ResultType>) { + if constexpr (std::is_void_v<ResultType>) handler(e); - } else { - if constexpr (std::is_copy_constructible_v<ResultType>) - promise.reportResult(handler(e)); - else - promise.reportAndMoveResult(handler(e)); - } + else + promise.addResult(handler(e)); } catch (...) { - promise.reportException(std::current_exception()); + promise.setException(std::current_exception()); } } catch (...) { // Exception doesn't match with handler's argument type, propagate // the exception to be handled later. - promise.reportException(std::current_exception()); + promise.setException(std::current_exception()); } } @@ -785,7 +769,7 @@ void FailureHandler<Function, ResultType>::handleAllExceptions() try { QtPrivate::fulfillPromise(promise, std::forward<Function>(handler)); } catch (...) { - promise.reportException(std::current_exception()); + promise.setException(std::current_exception()); } } } @@ -797,68 +781,49 @@ class CanceledHandler { public: template<class F = Function> - static QFuture<ResultType> create(F &&handler, QFuture<ResultType> *future, - QFutureInterface<ResultType> &promise) + static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi) { Q_ASSERT(future); - auto canceledContinuation = [promise, handler = std::forward<F>(handler)]( + auto canceledContinuation = [promise = QPromise(fi), handler = std::forward<F>(handler)]( const QFutureInterfaceBase &parentData) mutable { auto parentFuture = QFutureInterface<ResultType>(parentData).future(); - run(std::forward<F>(handler), parentFuture, promise); + run(std::forward<F>(handler), parentFuture, std::move(promise)); }; - - if constexpr (!std::is_copy_constructible_v<Function>) - future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation))); - else - future->d.setContinuation(std::move(canceledContinuation)); - - return promise.future(); + future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation))); } template<class F = Function> - static QFuture<ResultType> create(F &&handler, QFuture<ResultType> *future, - QFutureInterface<ResultType> &promise, QObject *context) + static void create(F &&handler, QFuture<ResultType> *future, QFutureInterface<ResultType> &fi, + QObject *context) { Q_ASSERT(future); - - auto canceledContinuation = [promise, 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, parentFuture, handler = std::forward<F>(handler)]() mutable { - run(std::forward<F>(handler), parentFuture, 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)); }; - if constexpr (!std::is_copy_constructible_v<Function>) - future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation))); - else - future->d.setContinuation(std::move(canceledContinuation)); - - return promise.future(); + QtPrivate::watchContinuation(context, std::move(canceledContinuation), future->d); } template<class F = Function> - static void run(F &&handler, QFuture<ResultType> &parentFuture, - QFutureInterface<ResultType> &promise) + static void run(F &&handler, QFuture<ResultType> &parentFuture, QPromise<ResultType> &&promise) { - promise.reportStarted(); + promise.start(); if (parentFuture.isCanceled()) { #ifndef QT_NO_EXCEPTIONS if (parentFuture.d.hasException()) { // Propagate the exception to the result future - promise.reportException(parentFuture.d.exceptionStore().exception()); + promise.setException(parentFuture.d.exceptionStore().exception()); } else { try { #endif QtPrivate::fulfillPromise(promise, std::forward<F>(handler)); #ifndef QT_NO_EXCEPTIONS } catch (...) { - promise.reportException(std::current_exception()); + promise.setException(std::current_exception()); } } #endif @@ -866,10 +831,73 @@ public: QtPrivate::fulfillPromise(promise, parentFuture); } - promise.reportFinished(); + promise.finish(); } }; +struct UnwrapHandler +{ + template<class T> + static auto unwrapImpl(T *outer) + { + Q_ASSERT(outer); + + using ResultType = typename QtPrivate::Future<std::decay_t<T>>::type; + using NestedType = typename QtPrivate::Future<ResultType>::type; + QFutureInterface<NestedType> promise(QFutureInterfaceBase::State::Pending); + + outer->then([promise](const QFuture<ResultType> &outerFuture) mutable { + // We use the .then([](QFuture<ResultType> outerFuture) {...}) version + // (where outerFuture == *outer), to propagate the exception if the + // outer future has failed. + Q_ASSERT(outerFuture.isFinished()); +#ifndef QT_NO_EXCEPTIONS + if (outerFuture.d.hasException()) { + promise.reportStarted(); + promise.reportException(outerFuture.d.exceptionStore().exception()); + promise.reportFinished(); + return; + } +#endif + + promise.reportStarted(); + ResultType nestedFuture = outerFuture.result(); + + nestedFuture.then([promise] (const QFuture<NestedType> &nested) mutable { +#ifndef QT_NO_EXCEPTIONS + if (nested.d.hasException()) { + promise.reportException(nested.d.exceptionStore().exception()); + } else +#endif + { + if constexpr (!std::is_void_v<NestedType>) + promise.reportResults(nested.results()); + } + promise.reportFinished(); + }).onCanceled([promise] () mutable { + promise.reportCanceled(); + promise.reportFinished(); + }); + }).onCanceled([promise]() mutable { + // propagate the cancellation of the outer future + promise.reportStarted(); + promise.reportCanceled(); + promise.reportFinished(); + }); + return promise.future(); + } +}; + +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 { @@ -883,6 +911,11 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal) using ArgsType = ArgsType<Signal>; QFutureInterface<ArgsType> promise; promise.reportStarted(); + if (!sender) { + promise.reportCanceled(); + promise.reportFinished(); + return promise.future(); + } using Connections = std::pair<QMetaObject::Connection, QMetaObject::Connection>; auto connections = std::make_shared<Connections>(); @@ -894,12 +927,13 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal) QObject::disconnect(connections->second); promise.reportFinished(); }); - } else if constexpr (QtPrivate::isTupleV<ArgsType>) { + } else if constexpr (QtPrivate::ArgResolver<Signal>::HasExtraArgs) { connections->first = QObject::connect(sender, signal, sender, [promise, connections](auto... values) mutable { QObject::disconnect(connections->first); QObject::disconnect(connections->second); - promise.reportResult(std::make_tuple(values...)); + promise.reportResult(QtPrivate::createTuple( + std::move(values)...)); promise.reportFinished(); }); } else { @@ -912,6 +946,12 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal) }); } + if (!connections->first) { + promise.reportCanceled(); + promise.reportFinished(); + return promise.future(); + } + connections->second = QObject::connect(sender, &QObject::destroyed, sender, [promise, connections]() mutable { QObject::disconnect(connections->first); @@ -923,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(); @@ -934,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 @@ -994,21 +1059,22 @@ struct WhenAllContext { using ValueType = typename ResultFutures::value_type; - WhenAllContext(qsizetype size) : count(size) {} + explicit WhenAllContext(qsizetype size) : remaining(size) {} template<typename T = ValueType> void checkForCompletion(qsizetype index, T &&future) { futures[index] = std::forward<T>(future); - Q_ASSERT(count > 0); - if (--count <= 0) { - promise.reportResult(futures); - promise.reportFinished(); + const auto oldRemaining = remaining.fetchAndSubRelaxed(1); + Q_ASSERT(oldRemaining > 0); + if (oldRemaining <= 1) { // that was the last one + promise.addResult(futures); + promise.finish(); } } - QAtomicInteger<qsizetype> count; - QFutureInterface<ResultFutures> promise; + QAtomicInteger<qsizetype> remaining; + QPromise<ResultFutures> promise; ResultFutures futures; }; @@ -1021,24 +1087,25 @@ struct WhenAnyContext void checkForCompletion(qsizetype, T &&result) { if (!ready.fetchAndStoreRelaxed(true)) { - promise.reportResult(std::forward<T>(result)); - promise.reportFinished(); + promise.addResult(std::forward<T>(result)); + promise.finish(); } } QAtomicInt ready = false; - QFutureInterface<ResultType> promise; + QPromise<ResultType> promise; }; 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 }); }); @@ -1047,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); @@ -1058,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.reportStarted(); + 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); }); } @@ -1079,9 +1147,9 @@ 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.reportStarted(); + context->promise.start(); QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...)); @@ -1097,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(); - context->promise.reportStarted(); + 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 }); }); } @@ -1120,8 +1189,8 @@ 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(); - context->promise.reportStarted(); + const auto context = std::make_shared<QtPrivate::WhenAnyContext<ResultType>>(); + context->promise.start(); QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward<Futures>(futures)...)); |