diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index f7c44d8aaf0..fbc02a35f2f 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -956,15 +956,14 @@ void GroupItem::addChildren(const QList &children) GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout, const GroupEndHandler &handler) { - const TimeoutTask::EndFunction taskHandler = handler - ? [handler](const milliseconds &) { handler(); } : TimeoutTask::EndFunction(); + const auto onSetup = [timeout](milliseconds &timeoutData) { timeoutData = timeout; }; return Group { parallel, stopOnFinished, Group { finishAllAndError, - TimeoutTask([timeout](milliseconds &timeoutData) { timeoutData = timeout; }, - taskHandler, CallDoneIf::Success) + handler ? TimeoutTask(onSetup, [handler] { handler(); }, CallDoneIf::Success) + : TimeoutTask(onSetup) }, item }; @@ -1089,7 +1088,7 @@ public: // in order to unwind properly. SetupResult start(); void stop(); - void invokeDoneHandler(bool success); + bool invokeDoneHandler(bool success); bool isRunning() const { return m_task || m_container.isRunning(); } bool isTask() const { return bool(m_taskHandler.m_createHandler); } int taskCount() const { return isTask() ? 1 : m_container.m_constData.m_taskCount; } @@ -1451,14 +1450,14 @@ SetupResult TaskNode::start() const std::shared_ptr unwindAction = std::make_shared(SetupResult::Continue); QObject::connect(m_task.get(), &TaskInterface::done, taskTree(), [=](bool success) { - invokeDoneHandler(success); + const bool result = invokeDoneHandler(success); QObject::disconnect(m_task.get(), &TaskInterface::done, taskTree(), nullptr); m_task.release()->deleteLater(); QTC_ASSERT(parentContainer() && parentContainer()->isRunning(), return); if (parentContainer()->isStarting()) - *unwindAction = toSetupResult(success); + *unwindAction = toSetupResult(result); else - parentContainer()->childDone(success); + parentContainer()->childDone(result); }); m_task->start(); @@ -1490,11 +1489,13 @@ static bool shouldCall(CallDoneIf callDoneIf, bool success) return callDoneIf != CallDoneIf::Success; } -void TaskNode::invokeDoneHandler(bool success) +bool TaskNode::invokeDoneHandler(bool success) { + bool result = success; if (m_taskHandler.m_doneHandler && shouldCall(m_taskHandler.m_callDoneIf, success)) - invokeHandler(parentContainer(), m_taskHandler.m_doneHandler, *m_task.get(), success); + result = invokeHandler(parentContainer(), m_taskHandler.m_doneHandler, *m_task.get(), success); m_container.m_constData.m_taskTreePrivate->advanceProgress(1); + return result; } /*! diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index f09ec80ed7e..fa3b47ffaab 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -147,8 +147,8 @@ public: using TaskCreateHandler = std::function; // Called prior to task start, just after createHandler using TaskSetupHandler = std::function; - // Called on task done, just before delete later - using TaskDoneHandler = std::function; + // Called on task done, just before deleteLater + using TaskDoneHandler = std::function; // Called when group entered using GroupSetupHandler = std::function; // Called when group done / error @@ -335,20 +335,14 @@ public: "The Adapter type for the CustomTask needs to be derived from " "TaskAdapter."); using SetupFunction = std::function; - using DoneFunction = std::function; - using EndFunction = std::function; + using DoneFunction = std::function; static Adapter *createAdapter() { return new Adapter; } - CustomTask() : GroupItem({&createAdapter}) {} - template - CustomTask(SetupHandler &&setup, const EndFunction &done, CallDoneIf callDoneIf) - : GroupItem({&createAdapter, wrapSetup(std::forward(setup)), wrapEnd(done), - callDoneIf}) {} - template - CustomTask(SetupHandler &&setup, const DoneFunction &done = {}, + template + CustomTask(SetupHandler &&setup = SetupFunction(), DoneHandler &&done = DoneFunction(), CallDoneIf callDoneIf = CallDoneIf::SuccessOrError) - : GroupItem({&createAdapter, wrapSetup(std::forward(setup)), wrapDone(done), - callDoneIf}) + : GroupItem({&createAdapter, wrapSetup(std::forward(setup)), + wrapDone(std::forward(done)), callDoneIf}) {} GroupItem withTimeout(std::chrono::milliseconds timeout, @@ -357,17 +351,17 @@ public: } private: - template - static GroupItem::TaskSetupHandler wrapSetup(SetupHandler &&handler) { - if constexpr (std::is_same_v>) - return {}; // When user passed {} for setup handler. - static constexpr bool isDynamic = std::is_same_v, typename Adapter::TaskType &>>; - constexpr bool isVoid = std::is_same_v, typename Adapter::TaskType &>>; + template + static GroupItem::TaskSetupHandler wrapSetup(Handler &&handler) { + if constexpr (std::is_same_v) + return {}; // When user passed {} for the setup handler. + static constexpr bool isDynamic + = std::is_same_v, Task &>>; + constexpr bool isVoid + = std::is_same_v, Task &>>; static_assert(isDynamic || isVoid, - "Task setup handler needs to take (Task &) as an argument and has to return " - "void or SetupResult. The passed handler doesn't fulfill these requirements."); + "Task setup handler needs to take (Task &) as an argument (optionally) and has to " + "return void or SetupResult. The passed handler doesn't fulfill these requirements."); return [=](TaskInterface &taskInterface) { Adapter &adapter = static_cast(taskInterface); if constexpr (isDynamic) @@ -377,21 +371,42 @@ private: }; }; - static TaskDoneHandler wrapEnd(const EndFunction &handler) { - if (!handler) - return {}; - return [handler](const TaskInterface &taskInterface, bool) { + template + static GroupItem::TaskDoneHandler wrapDone(Handler &&handler) { + if constexpr (std::is_same_v) + return {}; // When user passed {} for the done handler. + static constexpr bool is2ArgDynamic + = std::is_invocable_r_v, const Task &, bool>; + static constexpr bool is1ArgDynamic + = std::is_invocable_r_v, const Task &>; + static constexpr bool is0ArgDynamic + = std::is_invocable_r_v>; + static constexpr bool is2ArgVoid + = std::is_invocable_r_v, const Task &, bool>; + static constexpr bool is1ArgVoid + = std::is_invocable_r_v, const Task &>; + static constexpr bool is0ArgVoid + = std::is_invocable_r_v>; + static constexpr bool isInvocable = is2ArgDynamic || is1ArgDynamic || is0ArgDynamic + || is2ArgVoid || is1ArgVoid || is0ArgVoid; + static_assert(isInvocable, + "Task done handler needs to take (const Task &, bool) as arguments (optionally) and " + "has to return void or bool. The passed handler doesn't fulfill these requirements."); + return [=](const TaskInterface &taskInterface, bool success) { const Adapter &adapter = static_cast(taskInterface); - handler(*adapter.task()); - }; - }; - - static TaskDoneHandler wrapDone(const DoneFunction &handler) { - if (!handler) - return {}; - return [handler](const TaskInterface &taskInterface, bool success) { - const Adapter &adapter = static_cast(taskInterface); - handler(*adapter.task(), success); + if constexpr (is2ArgDynamic) + return std::invoke(handler, *adapter.task(), success); + if constexpr (is1ArgDynamic) + return std::invoke(handler, *adapter.task()); + if constexpr (is0ArgDynamic) + return std::invoke(handler); + if constexpr (is2ArgVoid) + std::invoke(handler, *adapter.task(), success); + else if constexpr (is1ArgVoid) + std::invoke(handler, *adapter.task()); + else if constexpr (is0ArgVoid) + std::invoke(handler); + return success; }; }; }; @@ -429,9 +444,9 @@ public: template void onStorageSetup(const TreeStorage &storage, StorageHandler &&handler) { - constexpr bool isInvokable = std::is_invocable_v, + constexpr bool isInvocable = std::is_invocable_v, StorageStruct &>; - static_assert(isInvokable, + static_assert(isInvocable, "Storage setup handler needs to take (Storage &) as an argument. " "The passed handler doesn't fulfill these requirements."); setupStorageHandler(storage, @@ -439,9 +454,9 @@ public: } template void onStorageDone(const TreeStorage &storage, StorageHandler &&handler) { - constexpr bool isInvokable = std::is_invocable_v, + constexpr bool isInvocable = std::is_invocable_v, const StorageStruct &>; - static_assert(isInvokable, + static_assert(isInvocable, "Storage done handler needs to take (const Storage &) as an argument. " "The passed handler doesn't fulfill these requirements."); setupStorageHandler(storage, diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index 58ab4513df5..6908817617e 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -223,9 +223,10 @@ void tst_Tasking::testTree_data() }; }; - const auto setupDone = [storage](int taskId) { - return [storage, taskId](const TaskObject &, bool success) { - storage->m_log.append({taskId, success ? Handler::Done : Handler::Error}); + const auto setupDone = [storage](int taskId, bool successTask = true) { + return [storage, taskId, successTask](const TaskObject &, bool success) { + storage->m_log.append({taskId, successTask && success ? Handler::Done : Handler::Error}); + return successTask && success; }; }; @@ -237,14 +238,7 @@ void tst_Tasking::testTree_data() const auto createTask = [storage, setupTask, setupDone]( int taskId, bool successTask, milliseconds timeout = 0ms) -> GroupItem { - if (successTask) - return TestTask(setupTask(taskId, timeout), setupDone(taskId)); - const Group root { - finishAllAndError, - TestTask(setupTask(taskId, timeout)), - onGroupError([storage, taskId] { storage->m_log.append({taskId, Handler::Error}); }) - }; - return root; + return TestTask(setupTask(taskId, timeout), setupDone(taskId, successTask)); }; const auto createSuccessTask = [createTask](int taskId, milliseconds timeout = 0ms) {