// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "tasking_global.h" #include "tasktree.h" #include namespace Tasking { // This class introduces the dependency to Qt::Concurrent, otherwise Tasking namespace // is independent on Qt::Concurrent. // Possibly, it could be placed inside Qt::Concurrent library, as a wrapper around // QtConcurrent::run() call. template class ConcurrentCall { Q_DISABLE_COPY_MOVE(ConcurrentCall) public: ConcurrentCall() = default; template void setConcurrentCallData(Function &&function, Args &&...args) { return wrapConcurrent(std::forward(function), std::forward(args)...); } void setThreadPool(QThreadPool *pool) { m_threadPool = pool; } ResultType result() const { return m_future.resultCount() ? m_future.result() : ResultType(); } QFuture future() const { return m_future; } private: template void wrapConcurrent(Function &&function, Args &&...args) { m_startHandler = [=] { if (m_threadPool) return QtConcurrent::run(m_threadPool, function, args...); return QtConcurrent::run(function, args...); }; } template void wrapConcurrent(std::reference_wrapper &&wrapper, Args &&...args) { m_startHandler = [=] { if (m_threadPool) { return QtConcurrent::run(m_threadPool, std::forward(wrapper.get()), args...); } return QtConcurrent::run(std::forward(wrapper.get()), args...); }; } template friend class ConcurrentCallTaskAdapter; std::function()> m_startHandler; QThreadPool *m_threadPool = nullptr; QFuture m_future; }; template class ConcurrentCallTaskAdapter : public TaskAdapter> { public: ~ConcurrentCallTaskAdapter() { if (m_watcher) { m_watcher->cancel(); m_watcher->waitForFinished(); } } void start() { if (!this->task()->m_startHandler) { emit this->done(false); // TODO: Add runtime assert return; } m_watcher.reset(new QFutureWatcher); this->connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] { emit this->done(!m_watcher->isCanceled()); m_watcher.release()->deleteLater(); }); this->task()->m_future = this->task()->m_startHandler(); m_watcher->setFuture(this->task()->m_future); } private: std::unique_ptr> m_watcher; }; } // namespace Tasking TASKING_DECLARE_TEMPLATE_TASK(ConcurrentCallTask, Tasking::ConcurrentCallTaskAdapter);