// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "utils_global.h" #include "futuresynchronizer.h" #include "qtcassert.h" #include "threadutils.h" #include #include #include namespace Utils { QTCREATOR_UTILS_EXPORT QThreadPool *asyncThreadPool(QThread::Priority priority); template auto asyncRun(QThreadPool *threadPool, QThread::Priority priority, Function &&function, Args &&...args) { QThreadPool *pool = threadPool ? threadPool : asyncThreadPool(priority); return QtConcurrent::run(pool, std::forward(function), std::forward(args)...); } template auto asyncRun(QThread::Priority priority, Function &&function, Args &&...args) { return asyncRun(nullptr, priority, std::forward(function), std::forward(args)...); } template auto asyncRun(QThreadPool *threadPool, Function &&function, Args &&...args) { return asyncRun(threadPool, QThread::InheritPriority, std::forward(function), std::forward(args)...); } template auto asyncRun(Function &&function, Args &&...args) { return asyncRun(nullptr, QThread::InheritPriority, std::forward(function), std::forward(args)...); } /*! Adds a handler for when a result is ready. This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions or create a QFutureWatcher already for other reasons. */ template const QFuture &onResultReady(const QFuture &future, R *receiver, void(R::*member)(const T &)) { auto watcher = new QFutureWatcher(receiver); QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, receiver, [=](int index) { (receiver->*member)(watcher->future().resultAt(index)); }); watcher->setFuture(future); return future; } /*! Adds a handler for when a result is ready. The guard object determines the lifetime of the connection. This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions or create a QFutureWatcher already for other reasons. */ template const QFuture &onResultReady(const QFuture &future, QObject *guard, Function f) { auto watcher = new QFutureWatcher(guard); QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, guard, [f, watcher](int index) { f(watcher->future().resultAt(index)); }); watcher->setFuture(future); return future; } /*! Adds a handler for when the future is finished. This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions or create a QFutureWatcher already for other reasons. */ template const QFuture &onFinished(const QFuture &future, R *receiver, void (R::*member)(const QFuture &)) { auto watcher = new QFutureWatcher(receiver); QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); QObject::connect(watcher, &QFutureWatcherBase::finished, receiver, [=] { (receiver->*member)(watcher->future()); }); watcher->setFuture(future); return future; } /*! Adds a handler for when the future is finished. The guard object determines the lifetime of the connection. This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions or create a QFutureWatcher already for other reasons. */ template const QFuture &onFinished(const QFuture &future, QObject *guard, Function f) { auto watcher = new QFutureWatcher(guard); QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); QObject::connect(watcher, &QFutureWatcherBase::finished, guard, [f, watcher] { f(watcher->future()); }); watcher->setFuture(future); return future; } class QTCREATOR_UTILS_EXPORT AsyncBase : public QObject { Q_OBJECT signals: void started(); void done(); void resultReadyAt(int index); void progressRangeChanged(int min, int max); void progressValueChanged(int value); void progressTextChanged(const QString &text); }; template class Async : public AsyncBase { public: Async() : m_synchronizer(isMainThread() ? futureSynchronizer() : nullptr) { connect(&m_watcher, &QFutureWatcherBase::finished, this, &AsyncBase::done); connect(&m_watcher, &QFutureWatcherBase::resultReadyAt, this, &AsyncBase::resultReadyAt); connect( &m_watcher, &QFutureWatcherBase::progressValueChanged, this, &AsyncBase::progressValueChanged); connect( &m_watcher, &QFutureWatcherBase::progressRangeChanged, this, &AsyncBase::progressRangeChanged); connect( &m_watcher, &QFutureWatcherBase::progressTextChanged, this, &AsyncBase::progressTextChanged); } ~Async() { if (isDone()) return; m_watcher.cancel(); if (!m_synchronizer) m_watcher.waitForFinished(); } template void setConcurrentCallData(Function &&function, Args &&...args) { wrapConcurrent(std::forward(function), std::forward(args)...); } void setFutureSynchronizer(FutureSynchronizer *synchronizer) { m_synchronizer = synchronizer; } void setThreadPool(QThreadPool *pool) { m_threadPool = pool; } void setPriority(QThread::Priority priority) { m_priority = priority; } void start() { QTC_ASSERT(m_startHandler, qWarning("No start handler specified."); return); m_watcher.setFuture(m_startHandler()); emit started(); if (m_synchronizer) m_synchronizer->addFuture(m_watcher.future()); } bool isDone() const { return m_watcher.isFinished(); } bool isCanceled() const { return m_watcher.isCanceled(); } QFuture future() const { return m_watcher.future(); } ResultType result() const { return m_watcher.result(); } ResultType takeResult() const { return m_watcher.future().takeResult(); } ResultType resultAt(int index) const { return m_watcher.resultAt(index); } QList results() const { return future().results(); } bool isResultAvailable() const { return future().resultCount(); } private: template void wrapConcurrent(Function &&function, Args &&...args) { m_startHandler = [this, function = std::forward(function), args...] { return asyncRun(m_threadPool, m_priority, function, args...); }; } template void wrapConcurrent(std::reference_wrapper &&wrapper, Args &&...args) { m_startHandler = [this, wrapper = std::forward>(wrapper), args...] { return asyncRun(m_threadPool, m_priority, std::forward(wrapper.get()), args...); }; } using StartHandler = std::function()>; StartHandler m_startHandler; FutureSynchronizer *m_synchronizer = nullptr; QThreadPool *m_threadPool = nullptr; QThread::Priority m_priority = QThread::InheritPriority; QFutureWatcher m_watcher; }; template class AsyncTaskAdapter final : public Tasking::TaskAdapter> { public: AsyncTaskAdapter() { this->connect(this->task(), &AsyncBase::done, this, [this] { emit this->done(Tasking::toDoneResult(!this->task()->isCanceled())); }); } void start() final { this->task()->start(); } }; template using AsyncTask = Tasking::CustomTask>; } // namespace Utils