diff options
Diffstat (limited to 'src/libs/utils/async.h')
-rw-r--r-- | src/libs/utils/async.h | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/src/libs/utils/async.h b/src/libs/utils/async.h new file mode 100644 index 0000000000..f8dc6813a7 --- /dev/null +++ b/src/libs/utils/async.h @@ -0,0 +1,215 @@ +// 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 <solutions/tasking/tasktree.h> + +#include <QFutureWatcher> +#include <QtConcurrent> + +namespace Utils { + +QTCREATOR_UTILS_EXPORT QThreadPool *asyncThreadPool(QThread::Priority priority); + +template <typename Function, typename ...Args> +auto asyncRun(QThreadPool *threadPool, QThread::Priority priority, + Function &&function, Args &&...args) +{ + QThreadPool *pool = threadPool ? threadPool : asyncThreadPool(priority); + return QtConcurrent::run(pool, std::forward<Function>(function), std::forward<Args>(args)...); +} + +template <typename Function, typename ...Args> +auto asyncRun(QThread::Priority priority, Function &&function, Args &&...args) +{ + return asyncRun<Function, Args...>(nullptr, priority, + std::forward<Function>(function), std::forward<Args>(args)...); +} + +template <typename Function, typename ...Args> +auto asyncRun(QThreadPool *threadPool, Function &&function, Args &&...args) +{ + return asyncRun<Function, Args...>(threadPool, QThread::InheritPriority, + std::forward<Function>(function), std::forward<Args>(args)...); +} + +template <typename Function, typename ...Args> +auto asyncRun(Function &&function, Args &&...args) +{ + return asyncRun<Function, Args...>(nullptr, QThread::InheritPriority, + std::forward<Function>(function), std::forward<Args>(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 <typename R, typename T> +const QFuture<T> &onResultReady(const QFuture<T> &future, R *receiver, void(R::*member)(const T &)) +{ + auto watcher = new QFutureWatcher<T>(); + 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 <typename T, typename Function> +const QFuture<T> &onResultReady(const QFuture<T> &future, QObject *guard, Function f) +{ + auto watcher = new QFutureWatcher<T>(); + 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<typename R, typename T> +const QFuture<T> &onFinished(const QFuture<T> &future, + R *receiver, void (R::*member)(const QFuture<T> &)) +{ + auto watcher = new QFutureWatcher<T>(); + 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<typename T, typename Function> +const QFuture<T> &onFinished(const QFuture<T> &future, QObject *guard, Function f) +{ + auto watcher = new QFutureWatcher<T>(); + 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); +}; + +template <typename ResultType> +class Async : public AsyncBase +{ +public: + Async() { + connect(&m_watcher, &QFutureWatcherBase::finished, this, &AsyncBase::done); + connect(&m_watcher, &QFutureWatcherBase::resultReadyAt, this, &AsyncBase::resultReadyAt); + } + ~Async() + { + if (isDone()) + return; + + m_watcher.cancel(); + if (!m_synchronizer) + m_watcher.waitForFinished(); + } + + template <typename Function, typename ...Args> + void setConcurrentCallData(Function &&function, Args &&...args) + { + return wrapConcurrent(std::forward<Function>(function), std::forward<Args>(args)...); + } + + void setFutureSynchronizer(FutureSynchronizer *synchorizer) { m_synchronizer = synchorizer; } + 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<ResultType> future() const { return m_watcher.future(); } + ResultType result() const { return m_watcher.result(); } + ResultType resultAt(int index) const { return m_watcher.resultAt(index); } + QList<ResultType> results() const { return future().results(); } + bool isResultAvailable() const { return future().resultCount(); } + +private: + template <typename Function, typename ...Args> + void wrapConcurrent(Function &&function, Args &&...args) + { + m_startHandler = [=] { + return asyncRun(m_threadPool, m_priority, function, args...); + }; + } + + template <typename Function, typename ...Args> + void wrapConcurrent(std::reference_wrapper<const Function> &&wrapper, Args &&...args) + { + m_startHandler = [=] { + return asyncRun(m_threadPool, m_priority, std::forward<const Function>(wrapper.get()), + args...); + }; + } + + using StartHandler = std::function<QFuture<ResultType>()>; + StartHandler m_startHandler; + FutureSynchronizer *m_synchronizer = nullptr; + QThreadPool *m_threadPool = nullptr; + QThread::Priority m_priority = QThread::InheritPriority; + QFutureWatcher<ResultType> m_watcher; +}; + +template <typename ResultType> +class AsyncTaskAdapter : public Tasking::TaskAdapter<Async<ResultType>> +{ +public: + AsyncTaskAdapter() { + this->connect(this->task(), &AsyncBase::done, this, [this] { + emit this->done(!this->task()->isCanceled()); + }); + } + void start() final { this->task()->start(); } +}; + +} // namespace Utils + +TASKING_DECLARE_TEMPLATE_TASK(AsyncTask, Utils::AsyncTaskAdapter); |