From e6ca18e19401474a28fdf64384957c00b15c8e5c Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 9 Jan 2023 20:52:12 +0100 Subject: [PATCH] TaskTree: Implement dynamic setup for tasks Before it was possible to dynamically setup groups only. This functionalily is probably good enough to replace Group's DynamicSetup, as it seems much more convenient. Change-Id: I56080f0ffa844847ca955cf52ccb07f3b4e4c731 Reviewed-by: hjk --- src/libs/utils/tasktree.cpp | 38 +++++++++------- src/libs/utils/tasktree.h | 52 +++++++++++++++++----- tests/auto/utils/tasktree/tst_tasktree.cpp | 24 ++++++++++ 3 files changed, 89 insertions(+), 25 deletions(-) diff --git a/src/libs/utils/tasktree.cpp b/src/libs/utils/tasktree.cpp index 0ef5f18c624..e06abc80858 100644 --- a/src/libs/utils/tasktree.cpp +++ b/src/libs/utils/tasktree.cpp @@ -200,7 +200,7 @@ public: { } - bool start(); + void start(); void stop(); bool isRunning() const; bool isTask() const; @@ -375,7 +375,8 @@ void TaskContainer::start() const int childCount = m_selectedChildren.size(); const int startCount = m_parallelLimit ? qMin(m_parallelLimit, childCount) : childCount; for (int i = 0; i < startCount; ++i) { - if (!m_selectedChildren.at(i)->start()) // TODO: take m_groupConfig.action into account + m_selectedChildren.at(i)->start(); + if (!isRunning()) return; } } @@ -544,19 +545,32 @@ void TaskContainer::deactivateStorages() m_parentContainer->deactivateStorages(); } -bool TaskNode::start() +void TaskNode::start() { if (!isTask()) { m_container.start(); - return true; + return; } + const auto finalize = [this](bool success) { + m_container.m_taskTreePrivate->advanceProgress(1); + m_task.release()->deleteLater(); + QTC_CHECK(m_container.m_parentContainer); + m_container.m_parentContainer->childDone(success); + }; m_task.reset(m_taskHandler.m_createHandler()); { - StorageActivator activator(m_container); - GuardLocker locker(m_container.m_taskTreePrivate->m_guard); - m_taskHandler.m_setupHandler(*m_task.get()); + TaskAction action = TaskAction::Continue; + { + StorageActivator activator(m_container); + GuardLocker locker(m_container.m_taskTreePrivate->m_guard); + action = m_taskHandler.m_setupHandler(*m_task.get()); + } + if (action != TaskAction::Continue) { + finalize(action == TaskAction::StopWithDone); + return; + } } - connect(m_task.get(), &TaskInterface::done, this, [this](bool success) { + connect(m_task.get(), &TaskInterface::done, this, [=](bool success) { { StorageActivator activator(m_container); if (success && m_taskHandler.m_doneHandler) { @@ -567,16 +581,10 @@ bool TaskNode::start() m_taskHandler.m_errorHandler(*m_task.get()); } } - m_container.m_taskTreePrivate->advanceProgress(1); - - m_task.release()->deleteLater(); - - QTC_CHECK(m_container.m_parentContainer); - m_container.m_parentContainer->childDone(success); + finalize(success); }); m_task->start(); - return m_task.get(); // In case of failed to start, done handler already released process } void TaskNode::stop() diff --git a/src/libs/utils/tasktree.h b/src/libs/utils/tasktree.h index c67238dc46e..fabf7fe4286 100644 --- a/src/libs/utils/tasktree.h +++ b/src/libs/utils/tasktree.h @@ -109,6 +109,13 @@ enum class GroupAction StopWithError }; +enum class TaskAction +{ + Continue, + StopWithDone, + StopWithError +}; + class GroupConfig { public: @@ -122,7 +129,7 @@ public: // Internal, provided by QTC_DECLARE_CUSTOM_TASK using TaskCreateHandler = std::function; // Called prior to task start, just after createHandler - using TaskSetupHandler = std::function; + using TaskSetupHandler = std::function; // Called on task done / error using TaskEndHandler = std::function; // Called when group entered / after group ended with success or failure @@ -263,22 +270,47 @@ class CustomTask : public TaskItem { public: using Task = typename Adapter::Type; - using SetupHandler = std::function; using EndHandler = std::function; static Adapter *createAdapter() { return new Adapter; } - CustomTask(const SetupHandler &setup, const EndHandler &done = {}, const EndHandler &error = {}) - : TaskItem({&createAdapter, wrapSetup(setup), - wrapEnd(done), wrapEnd(error)}) {} + template + CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {}) + : TaskItem({&createAdapter, wrapSetup(function), wrapEnd(done), wrapEnd(error)}) {} private: - static TaskSetupHandler wrapSetup(const SetupHandler &handler) { - if (!handler) - return {}; - return [handler](TaskInterface &taskInterface) { + template + using IsDynamic = typename std::is_same, typename Adapter::Type &>>; + + template + using IsVoid = typename std::is_same, typename Adapter::Type &>>; + + template::value, bool> = true> + static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { + return [=](TaskInterface &taskInterface) { Adapter &adapter = static_cast(taskInterface); - handler(*adapter.task()); + return std::invoke(function, *adapter.task()); }; }; + + template::value, bool> = true> + static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { + return [=](TaskInterface &taskInterface) { + Adapter &adapter = static_cast(taskInterface); + std::invoke(function, *adapter.task()); + return TaskAction::Continue; + }; + }; + + template::value + && !IsVoid::value, bool> = true> + static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&) { + static_assert(IsDynamic::value || IsVoid::value, + "Task setup handler needs to take (Task &) as an argument and has to return " + "void or TaskAction. The passed handler doesn't fulfill these requirements."); + return {}; + }; + static TaskEndHandler wrapEnd(const EndHandler &handler) { if (!handler) return {}; diff --git a/tests/auto/utils/tasktree/tst_tasktree.cpp b/tests/auto/utils/tasktree/tst_tasktree.cpp index 3153354fd56..682eeb0e620 100644 --- a/tests/auto/utils/tasktree/tst_tasktree.cpp +++ b/tests/auto/utils/tasktree/tst_tasktree.cpp @@ -163,6 +163,11 @@ void tst_TaskTree::processTree_data() const auto rootError = [storage] { storage->m_log.append({-1, Handler::GroupError}); }; + const auto setupDynamicProcess = [storage](QtcProcess &process, TaskAction action) { + Q_UNUSED(process) + storage->m_log.append({-1, Handler::Setup}); + return action; + }; const Group emptyRoot { Storage(storage), @@ -171,6 +176,25 @@ void tst_TaskTree::processTree_data() const Log emptyLog{{-1, Handler::GroupDone}}; QTest::newRow("Empty") << emptyRoot << storage << emptyLog << false << true << 0; + const Group dynamicTaskDoneRoot { + Storage(storage), + Process(std::bind(setupDynamicProcess, _1, TaskAction::StopWithDone)), + Process(std::bind(setupDynamicProcess, _1, TaskAction::StopWithDone)) + }; + const Log dynamicTaskDoneLog{{-1, Handler::Setup}, + {-1, Handler::Setup}}; + QTest::newRow("DynamicTaskDone") << dynamicTaskDoneRoot << storage << dynamicTaskDoneLog + << false << true << 2; + + const Group dynamicTaskErrorRoot { + Storage(storage), + Process(std::bind(setupDynamicProcess, _1, TaskAction::StopWithError)), + Process(std::bind(setupDynamicProcess, _1, TaskAction::StopWithError)) + }; + const Log dynamicTaskErrorLog{{-1, Handler::Setup}}; + QTest::newRow("DynamicTaskError") << dynamicTaskErrorRoot << storage << dynamicTaskErrorLog + << false << false << 2; + const Group nestedRoot { Storage(storage), Group {