/**************************************************************************** ** ** 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 #include #include #include #include QT_BEGIN_NAMESPACE // // forward declarations // template class QFuture; template class QFutureInterface; template class QPromise; namespace QtFuture { enum class Launch { Sync, Async, Inherit }; template struct WhenAnyResult { qsizetype index = -1; QFuture future; }; // Deduction guide template WhenAnyResult(qsizetype, const QFuture &) -> WhenAnyResult; } namespace QtPrivate { template using EnableIfSameOrConvertible = std::enable_if_t || std::is_convertible_v>; template using EnableForVoid = std::enable_if_t>; template using EnableForNonVoid = std::enable_if_t>; template struct ResultTypeHelper { }; // The callable takes an argument of type Arg template struct ResultTypeHelper< F, Arg, typename std::enable_if_t, QFuture>>> { using ResultType = std::invoke_result_t, std::decay_t>; }; // The callable takes an argument of type QFuture template struct ResultTypeHelper< F, Arg, typename std::enable_if_t, QFuture>>> { using ResultType = std::invoke_result_t, QFuture>; }; // The callable takes an argument of type QFuture template struct ResultTypeHelper< F, void, typename std::enable_if_t, QFuture>>> { using ResultType = std::invoke_result_t, QFuture>; }; // The callable doesn't take argument template struct ResultTypeHelper< F, void, typename std::enable_if_t, QFuture>>> { using ResultType = std::invoke_result_t>; }; // Helpers to remove QPrivateSignal argument from the list of arguments template inline constexpr bool IsPrivateSignalArg = false; template inline constexpr bool IsPrivateSignalArg >> = true; template auto cutTuple(Tuple &&t, std::index_sequence) { return std::make_tuple(std::get(t)...); } template auto createTuple(Arg &&arg, Args &&... args) { using TupleType = std::tuple, std::decay_t...>; constexpr auto Size = sizeof...(Args); // One less than the size of all arguments if constexpr (QtPrivate::IsPrivateSignalArg>) { if constexpr (Size == 1) { return arg; } else { return cutTuple(std::make_tuple(std::forward(arg), std::forward(args)...), std::make_index_sequence()); } } else { return std::make_tuple(std::forward(arg), std::forward(args)...); } } // Helpers to resolve argument types of callables. template using FilterLastPrivateSignalArg = std::conditional_t<(sizeof...(Args) > 0), std::invoke_result_t), Arg, Args...>, std::conditional_t, void, Arg>>; template struct ArgsType; template struct ArgsType { using First = Arg; using PromiseType = void; using IsPromise = std::false_type; static const bool HasExtraArgs = (sizeof...(Args) > 0); using AllArgs = FilterLastPrivateSignalArg, std::decay_t...>; template static const bool CanInvokeWithArgs = std::is_invocable_v; }; template struct ArgsType &, Args...> { using First = QPromise &; using PromiseType = Arg; using IsPromise = std::true_type; static const bool HasExtraArgs = (sizeof...(Args) > 0); using AllArgs = FilterLastPrivateSignalArg, std::decay_t...>; template static const bool CanInvokeWithArgs = std::is_invocable_v &, Args...>; }; template<> struct ArgsType<> { using First = void; using PromiseType = void; using IsPromise = std::false_type; static const bool HasExtraArgs = false; using AllArgs = void; template static const bool CanInvokeWithArgs = std::is_invocable_v; }; template struct ArgResolver : ArgResolver::operator())> { }; template struct ArgResolver> : ArgResolver::operator())> { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template struct ArgResolver : public ArgsType { }; template using EnableIfInvocable = std::enable_if_t< QtPrivate::ArgResolver::template CanInvokeWithArgs>; template struct isTuple : std::false_type { }; template struct isTuple> : std::true_type { }; template inline constexpr bool isTupleV = isTuple::value; template inline constexpr bool isQFutureV = false; template inline constexpr bool isQFutureV> = true; template using isQFuture = std::bool_constant>; template struct Future { }; template struct Future> { using type = T; }; template using NotEmpty = std::bool_constant<(sizeof...(Args) > 0)>; template using IsRandomAccessible = std::is_convertible()))>>::iterator_category, std::random_access_iterator_tag>; template using IsForwardIterable = std::is_convertible::iterator_category, std::forward_iterator_tag>; template class Continuation { public: template Continuation(F &&func, const QFuture &f, QPromise &&p) : promise(std::move(p)), parentFuture(f), function(std::forward(func)) { } virtual ~Continuation() = default; bool execute(); template static void create(F &&func, QFuture *f, QFutureInterface &fi, QtFuture::Launch policy); template static void create(F &&func, QFuture *f, QFutureInterface &fi, QThreadPool *pool); template static void create(F &&func, QFuture *f, QFutureInterface &fi, QObject *context); private: void fulfillPromiseWithResult(); void fulfillVoidPromise(); void fulfillPromiseWithVoidResult(); template void fulfillPromise(Args &&... args); protected: virtual void runImpl() = 0; void runFunction(); protected: QPromise promise; QFuture parentFuture; Function function; }; template class SyncContinuation final : public Continuation { public: template SyncContinuation(F &&func, const QFuture &f, QPromise &&p) : Continuation(std::forward(func), f, std::move(p)) { } ~SyncContinuation() override = default; private: void runImpl() override { this->runFunction(); } }; template class AsyncContinuation final : public QRunnable, public Continuation { public: template AsyncContinuation(F &&func, const QFuture &f, QPromise &&p, QThreadPool *pool = nullptr) : Continuation(std::forward(func), f, std::move(p)), threadPool(pool) { } ~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; }; #ifndef QT_NO_EXCEPTIONS template class FailureHandler { public: template static void create(F &&function, QFuture *future, const QFutureInterface &fi); template static void create(F &&function, QFuture *future, QFutureInterface &fi, QObject *context); template FailureHandler(F &&func, const QFuture &f, QPromise &&p) : promise(std::move(p)), parentFuture(f), handler(std::forward(func)) { } public: void run(); private: template void handleException(); void handleAllExceptions(); private: QPromise promise; QFuture parentFuture; Function handler; }; #endif template void Continuation::runFunction() { promise.start(); Q_ASSERT(parentFuture.isFinished()); #ifndef QT_NO_EXCEPTIONS try { #endif if constexpr (!std::is_void_v) { if constexpr (std::is_void_v) { fulfillPromiseWithVoidResult(); } else if constexpr (std::is_invocable_v) { fulfillPromiseWithResult(); } else { // This assert normally should never fail, this is to make sure // that nothing unexpected happend. static_assert(std::is_invocable_v>, "The continuation is not invocable with the provided arguments"); fulfillPromise(parentFuture); } } else { if constexpr (std::is_void_v) { if constexpr (std::is_invocable_v>) function(parentFuture); else function(); } else if constexpr (std::is_invocable_v) { fulfillVoidPromise(); } else { // This assert normally should never fail, this is to make sure // that nothing unexpected happend. static_assert(std::is_invocable_v>, "The continuation is not invocable with the provided arguments"); function(parentFuture); } } #ifndef QT_NO_EXCEPTIONS } catch (...) { promise.setException(std::current_exception()); } #endif promise.finish(); } template bool Continuation::execute() { Q_ASSERT(parentFuture.isFinished()); if (parentFuture.d.isChainCanceled()) { #ifndef QT_NO_EXCEPTIONS if (parentFuture.d.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, QFuture>) { promise.start(); promise.setException(parentFuture.d.exceptionStore().exception()); promise.finish(); return false; } } else #endif { promise.start(); promise.future().cancel(); promise.finish(); return false; } } runImpl(); return true; } // Workaround for keeping move-only lambdas inside std::function template struct ContinuationWrapper { ContinuationWrapper(Function &&f) : function(std::move(f)) { } ContinuationWrapper(const ContinuationWrapper &other) : function(std::move(const_cast(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: Function function; }; template template void Continuation::create(F &&func, QFuture *f, QFutureInterface &fi, 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(); fi.setThreadPool(pool); } } fi.setLaunchAsync(launchAsync); auto continuation = [func = std::forward(func), fi, promise = QPromise(fi), pool, launchAsync](const QFutureInterfaceBase &parentData) mutable { const auto parent = QFutureInterface(parentData).future(); Continuation *continuationJob = nullptr; if (launchAsync) { auto asyncJob = new AsyncContinuation( std::forward(func), parent, std::move(promise), pool); fi.setRunnable(asyncJob); continuationJob = asyncJob; } else { continuationJob = new SyncContinuation( std::forward(func), parent, std::move(promise)); } 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(ContinuationWrapper(std::move(continuation)), fi.d); } template template void Continuation::create(F &&func, QFuture *f, QFutureInterface &fi, QThreadPool *pool) { Q_ASSERT(f); fi.setLaunchAsync(true); fi.setThreadPool(pool); auto continuation = [func = std::forward(func), promise = QPromise(fi), pool](const QFutureInterfaceBase &parentData) mutable { const auto parent = QFutureInterface(parentData).future(); auto continuationJob = new AsyncContinuation( std::forward(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. if (!isLaunched) { delete continuationJob; continuationJob = nullptr; } }; f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d); } template template void Continuation::create(F &&func, QFuture *f, QFutureInterface &fi, QObject *context) { Q_ASSERT(f); auto continuation = [func = std::forward(func), promise = QPromise(fi), context = QPointer(context)]( const QFutureInterfaceBase &parentData) mutable { Q_ASSERT(context); const auto parent = QFutureInterface(parentData).future(); QMetaObject::invokeMethod( context, [func = std::forward(func), promise = std::move(promise), parent]() mutable { SyncContinuation continuationJob( std::forward(func), parent, std::move(promise)); continuationJob.execute(); }); }; f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d); } template void Continuation::fulfillPromiseWithResult() { if constexpr (std::is_copy_constructible_v) fulfillPromise(parentFuture.result()); else fulfillPromise(parentFuture.takeResult()); } template void Continuation::fulfillVoidPromise() { if constexpr (std::is_copy_constructible_v) function(parentFuture.result()); else function(parentFuture.takeResult()); } template void Continuation::fulfillPromiseWithVoidResult() { if constexpr (std::is_invocable_v>) fulfillPromise(parentFuture); else fulfillPromise(); } template template void Continuation::fulfillPromise(Args &&... args) { promise.addResult(std::invoke(function, std::forward(args)...)); } template void fulfillPromise(QPromise &promise, QFuture &future) { if constexpr (!std::is_void_v) { if constexpr (std::is_copy_constructible_v) promise.addResult(future.result()); else promise.addResult(future.takeResult()); } } template void fulfillPromise(QPromise &promise, Function &&handler) { if constexpr (std::is_void_v) handler(); else promise.addResult(handler()); } #ifndef QT_NO_EXCEPTIONS template template void FailureHandler::create(F &&function, QFuture *future, const QFutureInterface &fi) { Q_ASSERT(future); auto failureContinuation = [function = std::forward(function), promise = QPromise(fi)]( const QFutureInterfaceBase &parentData) mutable { const auto parent = QFutureInterface(parentData).future(); FailureHandler failureHandler(std::forward(function), parent, std::move(promise)); failureHandler.run(); }; future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation))); } template template void FailureHandler::create(F &&function, QFuture *future, QFutureInterface &fi, QObject *context) { Q_ASSERT(future); auto failureContinuation = [function = std::forward(function), promise = QPromise(fi), context = QPointer(context)](const QFutureInterfaceBase &parentData) mutable { Q_ASSERT(context); const auto parent = QFutureInterface(parentData).future(); QMetaObject::invokeMethod(context, [function = std::forward(function), promise = std::move(promise), parent]() mutable { FailureHandler failureHandler( std::forward(function), parent, std::move(promise)); failureHandler.run(); }); }; future->d.setContinuation(ContinuationWrapper(std::move(failureContinuation))); } template void FailureHandler::run() { Q_ASSERT(parentFuture.isFinished()); promise.start(); if (parentFuture.d.hasException()) { using ArgType = typename QtPrivate::ArgResolver::First; if constexpr (std::is_void_v) { handleAllExceptions(); } else { handleException(); } } else { QtPrivate::fulfillPromise(promise, parentFuture); } promise.finish(); } template template void FailureHandler::handleException() { try { Q_ASSERT(parentFuture.d.hasException()); parentFuture.d.exceptionStore().rethrowException(); } catch (const ArgType &e) { try { // Handle exceptions matching with the handler's argument type if constexpr (std::is_void_v) handler(e); else promise.addResult(handler(e)); } catch (...) { promise.setException(std::current_exception()); } } catch (...) { // Exception doesn't match with handler's argument type, propagate // the exception to be handled later. promise.setException(std::current_exception()); } } template void FailureHandler::handleAllExceptions() { try { Q_ASSERT(parentFuture.d.hasException()); parentFuture.d.exceptionStore().rethrowException(); } catch (...) { try { QtPrivate::fulfillPromise(promise, std::forward(handler)); } catch (...) { promise.setException(std::current_exception()); } } } #endif // QT_NO_EXCEPTIONS template class CanceledHandler { public: template static void create(F &&handler, QFuture *future, QFutureInterface &fi) { Q_ASSERT(future); auto canceledContinuation = [promise = QPromise(fi), handler = std::forward(handler)]( const QFutureInterfaceBase &parentData) mutable { auto parentFuture = QFutureInterface(parentData).future(); run(std::forward(handler), parentFuture, std::move(promise)); }; future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation))); } template static void create(F &&handler, QFuture *future, QFutureInterface &fi, QObject *context) { Q_ASSERT(future); auto canceledContinuation = [promise = QPromise(fi), handler = std::forward(handler), context = QPointer(context)]( const QFutureInterfaceBase &parentData) mutable { Q_ASSERT(context); auto parentFuture = QFutureInterface(parentData).future(); QMetaObject::invokeMethod(context, [promise = std::move(promise), parentFuture, handler = std::forward(handler)]() mutable { run(std::forward(handler), parentFuture, std::move(promise)); }); }; future->d.setContinuation(ContinuationWrapper(std::move(canceledContinuation))); } template static void run(F &&handler, QFuture &parentFuture, QPromise &&promise) { promise.start(); if (parentFuture.isCanceled()) { #ifndef QT_NO_EXCEPTIONS if (parentFuture.d.hasException()) { // Propagate the exception to the result future promise.setException(parentFuture.d.exceptionStore().exception()); } else { try { #endif QtPrivate::fulfillPromise(promise, std::forward(handler)); #ifndef QT_NO_EXCEPTIONS } catch (...) { promise.setException(std::current_exception()); } } #endif } else { QtPrivate::fulfillPromise(promise, parentFuture); } promise.finish(); } }; } // namespace QtPrivate namespace QtFuture { template using ArgsType = typename QtPrivate::ArgResolver::AllArgs; template> static QFuture> connect(Sender *sender, Signal signal) { using ArgsType = ArgsType; QFutureInterface promise; promise.reportStarted(); using Connections = std::pair; auto connections = std::make_shared(); if constexpr (std::is_void_v) { connections->first = QObject::connect(sender, signal, sender, [promise, connections]() mutable { QObject::disconnect(connections->first); QObject::disconnect(connections->second); promise.reportFinished(); }); } else if constexpr (QtPrivate::isTupleV) { connections->first = QObject::connect(sender, signal, sender, [promise, connections](auto... values) mutable { QObject::disconnect(connections->first); QObject::disconnect(connections->second); promise.reportResult(QtPrivate::createTuple( std::move(values)...)); promise.reportFinished(); }); } else { connections->first = QObject::connect(sender, signal, sender, [promise, connections](ArgsType value) mutable { QObject::disconnect(connections->first); QObject::disconnect(connections->second); promise.reportResult(value); promise.reportFinished(); }); } connections->second = QObject::connect(sender, &QObject::destroyed, sender, [promise, connections]() mutable { QObject::disconnect(connections->first); QObject::disconnect(connections->second); promise.reportCanceled(); promise.reportFinished(); }); return promise.future(); } template> static QFuture> makeReadyFuture(T &&value) { QFutureInterface> promise; promise.reportStarted(); promise.reportResult(std::forward(value)); promise.reportFinished(); return promise.future(); } #if defined(Q_CLANG_QDOC) static QFuture makeReadyFuture() #else template static QFuture makeReadyFuture() #endif { QFutureInterface promise; promise.reportStarted(); promise.reportFinished(); return promise.future(); } template static QFuture makeReadyFuture(const QList &values) { QFutureInterface promise; promise.reportStarted(); promise.reportResults(values); promise.reportFinished(); return promise.future(); } #ifndef QT_NO_EXCEPTIONS template static QFuture makeExceptionalFuture(std::exception_ptr exception) { QFutureInterface promise; promise.reportStarted(); promise.reportException(exception); promise.reportFinished(); return promise.future(); } template static QFuture makeExceptionalFuture(const QException &exception) { try { exception.raise(); } catch (...) { return makeExceptionalFuture(std::current_exception()); } Q_UNREACHABLE(); } #endif // QT_NO_EXCEPTIONS } // namespace QtFuture namespace QtPrivate { template struct WhenAllContext { using ValueType = typename ResultFutures::value_type; WhenAllContext(qsizetype size) : count(size) {} template void checkForCompletion(qsizetype index, T &&future) { futures[index] = std::forward(future); Q_ASSERT(count > 0); if (--count <= 0) { promise.addResult(futures); promise.finish(); } } QAtomicInteger count; QPromise promise; ResultFutures futures; }; template struct WhenAnyContext { using ValueType = ResultType; template> void checkForCompletion(qsizetype, T &&result) { if (!ready.fetchAndStoreRelaxed(true)) { promise.addResult(std::forward(result)); promise.finish(); } } QAtomicInt ready = false; QPromise promise; }; template void addCompletionHandlersImpl(const QSharedPointer &context, const std::tuple &t) { auto future = std::get(t); using ResultType = typename ContextType::ValueType; future.then([context](const std::tuple_element_t> &f) { context->checkForCompletion(Index, ResultType { std::in_place_index, f }); }).onCanceled([context, future]() { context->checkForCompletion(Index, ResultType { std::in_place_index, future }); }); if constexpr (Index != 0) addCompletionHandlersImpl(context, t); } template void addCompletionHandlers(const QSharedPointer &context, const std::tuple &t) { constexpr qsizetype size = std::tuple_size>::value; addCompletionHandlersImpl(context, t); } template QFuture whenAllImpl(InputIt first, InputIt last) { const qsizetype size = std::distance(first, last); if (size == 0) return QtFuture::makeReadyFuture(OutputSequence()); auto context = QSharedPointer>::create(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) { context->checkForCompletion(idx, f); }).onCanceled([context, idx, f = *it] { context->checkForCompletion(idx, f); }); } return context->promise.future(); } template QFuture whenAllImpl(Futures &&... futures) { constexpr qsizetype size = sizeof...(Futures); auto context = QSharedPointer>::create(size); context->futures.resize(size); context->promise.start(); QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward(futures)...)); return context->promise.future(); } template QFuture::type>> whenAnyImpl(InputIt first, InputIt last) { using PackagedType = typename Future::type; using ResultType = QtFuture::WhenAnyResult; const qsizetype size = std::distance(first, last); if (size == 0) { return QtFuture::makeReadyFuture( QtFuture::WhenAnyResult { qsizetype(-1), QFuture() }); } auto context = QSharedPointer>::create(); context->promise.start(); qsizetype idx = 0; for (auto it = first; it != last; ++it, ++idx) { it->then([context, idx](const ValueType &f) { context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f }); }).onCanceled([context, idx, f = *it] { context->checkForCompletion(idx, QtFuture::WhenAnyResult { idx, f }); }); } return context->promise.future(); } template QFuture...>> whenAnyImpl(Futures &&... futures) { using ResultType = std::variant...>; auto context = QSharedPointer>::create(); context->promise.start(); QtPrivate::addCompletionHandlers(context, std::make_tuple(std::forward(futures)...)); return context->promise.future(); } } // namespace QtPrivate QT_END_NAMESPACE