// 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 #endif #if 0 #pragma qt_sync_skip_header_check #pragma qt_sync_stop_processing #endif #include #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 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 std::forward(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 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 happened. 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 happened. 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(); } }; struct UnwrapHandler { template static auto unwrapImpl(T *outer) { Q_ASSERT(outer); using ResultType = typename QtPrivate::Future>::type; using NestedType = typename QtPrivate::Future::type; QFutureInterface promise(QFutureInterfaceBase::State::Pending); outer->then([promise](const QFuture &outerFuture) mutable { // We use the .then([](QFuture 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 &nested) mutable { #ifndef QT_NO_EXCEPTIONS if (nested.d.hasException()) { promise.reportException(nested.d.exceptionStore().exception()); } else #endif { if constexpr (!std::is_void_v) 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(); } }; } // 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(); if (!sender) { promise.reportCanceled(); promise.reportFinished(); return promise.future(); } 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::ArgResolver::HasExtraArgs) { 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(); }); } 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); 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_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; explicit WhenAllContext(qsizetype size) : remaining(size) {} template void checkForCompletion(qsizetype index, T &&future) { futures[index] = std::forward(future); const auto oldRemaining = remaining.fetchAndSubRelaxed(1); Q_ASSERT(oldRemaining > 0); if (oldRemaining <= 1) { // that was the last one promise.addResult(futures); promise.finish(); } } QAtomicInteger remaining; 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