/**************************************************************************** ** ** 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 QT_BEGIN_NAMESPACE // // forward declarations // template class QFuture; template class QFutureInterface; namespace QtFuture { enum class Launch { Sync, Async, Inherit }; } namespace QtPrivate { 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>; }; template class Continuation { public: Continuation(Function &&func, const QFuture &f, const QFutureInterface &p) : promise(p), parentFuture(f), function(std::forward(func)) { } virtual ~Continuation() = default; bool execute(); static void create(Function &&func, QFuture *f, QFutureInterface &p, QtFuture::Launch policy); static void create(Function &&func, QFuture *f, QFutureInterface &p, QThreadPool *pool); protected: virtual void runImpl() = 0; void runFunction(); protected: QFutureInterface promise; const QFuture parentFuture; Function function; }; template class SyncContinuation final : public Continuation { public: SyncContinuation(Function &&func, const QFuture &f, const QFutureInterface &p) : Continuation(std::forward(func), f, p) { } ~SyncContinuation() override = default; private: void runImpl() override { this->runFunction(); } }; template class AsyncContinuation final : public QRunnable, public Continuation { public: AsyncContinuation(Function &&func, const QFuture &f, const QFutureInterface &p, QThreadPool *pool = nullptr) : Continuation(std::forward(func), f, p), threadPool(pool) { this->promise.setRunnable(this); } ~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; }; template void Continuation::runFunction() { promise.reportStarted(); Q_ASSERT(parentFuture.isFinished()); #ifndef QT_NO_EXCEPTIONS try { #endif if constexpr (!std::is_void_v) { if constexpr (std::is_invocable_v, QFuture>) { promise.reportResult(function(parentFuture)); } else if constexpr (std::is_void_v) { promise.reportResult(function()); } else { // This assert normally should never fail, this is to make sure // that nothing unexpected happend. static_assert( std::is_invocable_v, std::decay_t>, "The continuation is not invocable with the provided arguments"); promise.reportResult(function(parentFuture.result())); } } else { if constexpr (std::is_invocable_v, QFuture>) { function(parentFuture); } else if constexpr (std::is_void_v) { function(); } else { // This assert normally should never fail, this is to make sure // that nothing unexpected happend. static_assert( std::is_invocable_v, std::decay_t>, "The continuation is not invocable with the provided arguments"); function(parentFuture.result()); } } #ifndef QT_NO_EXCEPTIONS } catch (QException &e) { promise.reportException(e); } catch (...) { promise.reportException(QUnhandledException()); } #endif promise.reportFinished(); } template bool Continuation::execute() { Q_ASSERT(parentFuture.isFinished()); if (parentFuture.isCanceled()) { #ifndef QT_NO_EXCEPTIONS if (parentFuture.d.exceptionStore().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.reportStarted(); const QException *e = parentFuture.d.exceptionStore().exception().exception(); promise.reportException(*e); promise.reportFinished(); return false; } } else #endif { promise.reportStarted(); promise.reportCanceled(); promise.reportFinished(); return false; } } runImpl(); return true; } template void Continuation::create(Function &&func, QFuture *f, QFutureInterface &p, 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(); p.setThreadPool(pool); } } Continuation *continuationJob = nullptr; if (launchAsync) { continuationJob = new AsyncContinuation( std::forward(func), *f, p, pool); } else { continuationJob = new SyncContinuation( std::forward(func), *f, p); } p.setLaunchAsync(launchAsync); auto continuation = [continuationJob, policy, launchAsync]() mutable { 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(std::move(continuation)); } template void Continuation::create(Function &&func, QFuture *f, QFutureInterface &p, QThreadPool *pool) { Q_ASSERT(f); auto continuationJob = new AsyncContinuation( std::forward(func), *f, p, pool); p.setLaunchAsync(true); p.setThreadPool(pool); auto continuation = [continuationJob]() mutable { 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(continuation); } } // namespace QtPrivate QT_END_NAMESPACE