diff --git a/src/libs/solutions/tasking/CMakeLists.txt b/src/libs/solutions/tasking/CMakeLists.txt index 5beed2fe5b4..52c994de8db 100644 --- a/src/libs/solutions/tasking/CMakeLists.txt +++ b/src/libs/solutions/tasking/CMakeLists.txt @@ -1,9 +1,10 @@ add_qtc_library(Tasking OBJECT # Never add dependencies to non-Qt libraries for this library - DEPENDS Qt::Core + DEPENDS Qt::Concurrent Qt::Core PUBLIC_DEFINES TASKING_LIBRARY SOURCES barrier.cpp barrier.h + concurrentcall.h tasking_global.h tasktree.cpp tasktree.h ) diff --git a/src/libs/solutions/tasking/barrier.h b/src/libs/solutions/tasking/barrier.h index 6939da5b365..6f1afe39b09 100644 --- a/src/libs/solutions/tasking/barrier.h +++ b/src/libs/solutions/tasking/barrier.h @@ -34,7 +34,7 @@ private: int m_current = -1; }; -class TASKING_EXPORT BarrierTaskAdapter : public Tasking::TaskAdapter +class TASKING_EXPORT BarrierTaskAdapter : public TaskAdapter { public: BarrierTaskAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); } diff --git a/src/libs/solutions/tasking/concurrentcall.h b/src/libs/solutions/tasking/concurrentcall.h new file mode 100644 index 00000000000..d7799159447 --- /dev/null +++ b/src/libs/solutions/tasking/concurrentcall.h @@ -0,0 +1,100 @@ +// 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); diff --git a/src/libs/solutions/tasking/tasking.qbs b/src/libs/solutions/tasking/tasking.qbs index 8697b9c009b..0e78ed74f0a 100644 --- a/src/libs/solutions/tasking/tasking.qbs +++ b/src/libs/solutions/tasking/tasking.qbs @@ -1,11 +1,12 @@ QtcLibrary { name: "Tasking" - Depends { name: "Qt"; submodules: ["core"] } + Depends { name: "Qt"; submodules: ["concurrent", "core"] } cpp.defines: base.concat("TASKING_LIBRARY") files: [ "barrier.cpp", "barrier.h", + "concurrentcall.h", "tasking_global.h", "tasktree.cpp", "tasktree.h",