// Copyright (C) 2024 Jarek Kobus // Copyright (C) 2024 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 TASKING_CONCURRENTCALL_H #define TASKING_CONCURRENTCALL_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include "tasktree.h" #include QT_BEGIN_NAMESPACE 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) { 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(); } QList results() const { return m_future.results(); } QFuture future() const { return m_future; } private: template void wrapConcurrent(Function &&function, Args &&...args) { m_startHandler = [this, function = std::forward(function), args...] { QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance(); return QtConcurrent::run(threadPool, function, args...); }; } template void wrapConcurrent(std::reference_wrapper &&wrapper, Args &&...args) { m_startHandler = [this, wrapper = std::forward>(wrapper), args...] { QThreadPool *threadPool = m_threadPool ? m_threadPool : QThreadPool::globalInstance(); return QtConcurrent::run(threadPool, 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() final { if (!this->task()->m_startHandler) { emit this->done(DoneResult::Error); // TODO: Add runtime assert return; } m_watcher.reset(new QFutureWatcher); this->connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] { emit this->done(toDoneResult(!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; }; template using ConcurrentCallTask = CustomTask>; } // namespace Tasking QT_END_NAMESPACE #endif // TASKING_CONCURRENTCALL_H