TaskTree: Make it possible to tweak the success bit in done handler

Make the arguments of done handler optional.

Task-number: QTCREATORBUG-29834
Change-Id: I9c2af431feca87351c8c9129e61ce3889d137de5
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2023-11-02 19:10:55 +01:00
parent 6e6aa7102c
commit 34cef824b5
3 changed files with 72 additions and 62 deletions

View File

@@ -956,15 +956,14 @@ void GroupItem::addChildren(const QList<GroupItem> &children)
GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout, GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout,
const GroupEndHandler &handler) const GroupEndHandler &handler)
{ {
const TimeoutTask::EndFunction taskHandler = handler const auto onSetup = [timeout](milliseconds &timeoutData) { timeoutData = timeout; };
? [handler](const milliseconds &) { handler(); } : TimeoutTask::EndFunction();
return Group { return Group {
parallel, parallel,
stopOnFinished, stopOnFinished,
Group { Group {
finishAllAndError, finishAllAndError,
TimeoutTask([timeout](milliseconds &timeoutData) { timeoutData = timeout; }, handler ? TimeoutTask(onSetup, [handler] { handler(); }, CallDoneIf::Success)
taskHandler, CallDoneIf::Success) : TimeoutTask(onSetup)
}, },
item item
}; };
@@ -1089,7 +1088,7 @@ public:
// in order to unwind properly. // in order to unwind properly.
SetupResult start(); SetupResult start();
void stop(); void stop();
void invokeDoneHandler(bool success); bool invokeDoneHandler(bool success);
bool isRunning() const { return m_task || m_container.isRunning(); } bool isRunning() const { return m_task || m_container.isRunning(); }
bool isTask() const { return bool(m_taskHandler.m_createHandler); } bool isTask() const { return bool(m_taskHandler.m_createHandler); }
int taskCount() const { return isTask() ? 1 : m_container.m_constData.m_taskCount; } int taskCount() const { return isTask() ? 1 : m_container.m_constData.m_taskCount; }
@@ -1451,14 +1450,14 @@ SetupResult TaskNode::start()
const std::shared_ptr<SetupResult> unwindAction const std::shared_ptr<SetupResult> unwindAction
= std::make_shared<SetupResult>(SetupResult::Continue); = std::make_shared<SetupResult>(SetupResult::Continue);
QObject::connect(m_task.get(), &TaskInterface::done, taskTree(), [=](bool success) { 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); QObject::disconnect(m_task.get(), &TaskInterface::done, taskTree(), nullptr);
m_task.release()->deleteLater(); m_task.release()->deleteLater();
QTC_ASSERT(parentContainer() && parentContainer()->isRunning(), return); QTC_ASSERT(parentContainer() && parentContainer()->isRunning(), return);
if (parentContainer()->isStarting()) if (parentContainer()->isStarting())
*unwindAction = toSetupResult(success); *unwindAction = toSetupResult(result);
else else
parentContainer()->childDone(success); parentContainer()->childDone(result);
}); });
m_task->start(); m_task->start();
@@ -1490,11 +1489,13 @@ static bool shouldCall(CallDoneIf callDoneIf, bool success)
return callDoneIf != CallDoneIf::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)) 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); m_container.m_constData.m_taskTreePrivate->advanceProgress(1);
return result;
} }
/*! /*!

View File

@@ -147,8 +147,8 @@ public:
using TaskCreateHandler = std::function<TaskInterface *(void)>; using TaskCreateHandler = std::function<TaskInterface *(void)>;
// Called prior to task start, just after createHandler // Called prior to task start, just after createHandler
using TaskSetupHandler = std::function<SetupResult(TaskInterface &)>; using TaskSetupHandler = std::function<SetupResult(TaskInterface &)>;
// Called on task done, just before delete later // Called on task done, just before deleteLater
using TaskDoneHandler = std::function<void(const TaskInterface &, bool)>; using TaskDoneHandler = std::function<bool(const TaskInterface &, bool)>;
// Called when group entered // Called when group entered
using GroupSetupHandler = std::function<SetupResult()>; using GroupSetupHandler = std::function<SetupResult()>;
// Called when group done / error // Called when group done / error
@@ -335,20 +335,14 @@ public:
"The Adapter type for the CustomTask<Adapter> needs to be derived from " "The Adapter type for the CustomTask<Adapter> needs to be derived from "
"TaskAdapter<Task>."); "TaskAdapter<Task>.");
using SetupFunction = std::function<void(const Task &)>; using SetupFunction = std::function<void(const Task &)>;
using DoneFunction = std::function<void(const Task &, bool)>; using DoneFunction = std::function<bool(const Task &, bool)>;
using EndFunction = std::function<void(const Task &)>;
static Adapter *createAdapter() { return new Adapter; } static Adapter *createAdapter() { return new Adapter; }
CustomTask() : GroupItem({&createAdapter}) {}
template <typename SetupHandler = SetupFunction>
CustomTask(SetupHandler &&setup, const EndFunction &done, CallDoneIf callDoneIf)
: GroupItem({&createAdapter, wrapSetup(std::forward<SetupHandler>(setup)), wrapEnd(done),
callDoneIf}) {}
template <typename SetupHandler> template <typename SetupHandler = SetupFunction, typename DoneHandler = DoneFunction>
CustomTask(SetupHandler &&setup, const DoneFunction &done = {}, CustomTask(SetupHandler &&setup = SetupFunction(), DoneHandler &&done = DoneFunction(),
CallDoneIf callDoneIf = CallDoneIf::SuccessOrError) CallDoneIf callDoneIf = CallDoneIf::SuccessOrError)
: GroupItem({&createAdapter, wrapSetup(std::forward<SetupHandler>(setup)), wrapDone(done), : GroupItem({&createAdapter, wrapSetup(std::forward<SetupHandler>(setup)),
callDoneIf}) wrapDone(std::forward<DoneHandler>(done)), callDoneIf})
{} {}
GroupItem withTimeout(std::chrono::milliseconds timeout, GroupItem withTimeout(std::chrono::milliseconds timeout,
@@ -357,17 +351,17 @@ public:
} }
private: private:
template<typename SetupHandler> template<typename Handler>
static GroupItem::TaskSetupHandler wrapSetup(SetupHandler &&handler) { static GroupItem::TaskSetupHandler wrapSetup(Handler &&handler) {
if constexpr (std::is_same_v<SetupHandler, std::function<void()>>) if constexpr (std::is_same_v<Handler, SetupFunction>)
return {}; // When user passed {} for setup handler. return {}; // When user passed {} for the setup handler.
static constexpr bool isDynamic = std::is_same_v<SetupResult, static constexpr bool isDynamic
std::invoke_result_t<std::decay_t<SetupHandler>, typename Adapter::TaskType &>>; = std::is_same_v<SetupResult, std::invoke_result_t<std::decay_t<Handler>, Task &>>;
constexpr bool isVoid = std::is_same_v<void, constexpr bool isVoid
std::invoke_result_t<std::decay_t<SetupHandler>, typename Adapter::TaskType &>>; = std::is_same_v<void, std::invoke_result_t<std::decay_t<Handler>, Task &>>;
static_assert(isDynamic || isVoid, static_assert(isDynamic || isVoid,
"Task setup handler needs to take (Task &) as an argument and has to return " "Task setup handler needs to take (Task &) as an argument (optionally) and has to "
"void or SetupResult. The passed handler doesn't fulfill these requirements."); "return void or SetupResult. The passed handler doesn't fulfill these requirements.");
return [=](TaskInterface &taskInterface) { return [=](TaskInterface &taskInterface) {
Adapter &adapter = static_cast<Adapter &>(taskInterface); Adapter &adapter = static_cast<Adapter &>(taskInterface);
if constexpr (isDynamic) if constexpr (isDynamic)
@@ -377,21 +371,42 @@ private:
}; };
}; };
static TaskDoneHandler wrapEnd(const EndFunction &handler) { template<typename Handler>
if (!handler) static GroupItem::TaskDoneHandler wrapDone(Handler &&handler) {
return {}; if constexpr (std::is_same_v<Handler, DoneFunction>)
return [handler](const TaskInterface &taskInterface, bool) { return {}; // When user passed {} for the done handler.
static constexpr bool is2ArgDynamic
= std::is_invocable_r_v<bool, std::decay_t<Handler>, const Task &, bool>;
static constexpr bool is1ArgDynamic
= std::is_invocable_r_v<bool, std::decay_t<Handler>, const Task &>;
static constexpr bool is0ArgDynamic
= std::is_invocable_r_v<bool, std::decay_t<Handler>>;
static constexpr bool is2ArgVoid
= std::is_invocable_r_v<void, std::decay_t<Handler>, const Task &, bool>;
static constexpr bool is1ArgVoid
= std::is_invocable_r_v<void, std::decay_t<Handler>, const Task &>;
static constexpr bool is0ArgVoid
= std::is_invocable_r_v<void, std::decay_t<Handler>>;
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<const Adapter &>(taskInterface); const Adapter &adapter = static_cast<const Adapter &>(taskInterface);
handler(*adapter.task()); if constexpr (is2ArgDynamic)
}; return std::invoke(handler, *adapter.task(), success);
}; if constexpr (is1ArgDynamic)
return std::invoke(handler, *adapter.task());
static TaskDoneHandler wrapDone(const DoneFunction &handler) { if constexpr (is0ArgDynamic)
if (!handler) return std::invoke(handler);
return {}; if constexpr (is2ArgVoid)
return [handler](const TaskInterface &taskInterface, bool success) { std::invoke(handler, *adapter.task(), success);
const Adapter &adapter = static_cast<const Adapter &>(taskInterface); else if constexpr (is1ArgVoid)
handler(*adapter.task(), success); std::invoke(handler, *adapter.task());
else if constexpr (is0ArgVoid)
std::invoke(handler);
return success;
}; };
}; };
}; };
@@ -429,9 +444,9 @@ public:
template <typename StorageStruct, typename StorageHandler> template <typename StorageStruct, typename StorageHandler>
void onStorageSetup(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) { void onStorageSetup(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
constexpr bool isInvokable = std::is_invocable_v<std::decay_t<StorageHandler>, constexpr bool isInvocable = std::is_invocable_v<std::decay_t<StorageHandler>,
StorageStruct &>; StorageStruct &>;
static_assert(isInvokable, static_assert(isInvocable,
"Storage setup handler needs to take (Storage &) as an argument. " "Storage setup handler needs to take (Storage &) as an argument. "
"The passed handler doesn't fulfill these requirements."); "The passed handler doesn't fulfill these requirements.");
setupStorageHandler(storage, setupStorageHandler(storage,
@@ -439,9 +454,9 @@ public:
} }
template <typename StorageStruct, typename StorageHandler> template <typename StorageStruct, typename StorageHandler>
void onStorageDone(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) { void onStorageDone(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
constexpr bool isInvokable = std::is_invocable_v<std::decay_t<StorageHandler>, constexpr bool isInvocable = std::is_invocable_v<std::decay_t<StorageHandler>,
const StorageStruct &>; const StorageStruct &>;
static_assert(isInvokable, static_assert(isInvocable,
"Storage done handler needs to take (const Storage &) as an argument. " "Storage done handler needs to take (const Storage &) as an argument. "
"The passed handler doesn't fulfill these requirements."); "The passed handler doesn't fulfill these requirements.");
setupStorageHandler(storage, setupStorageHandler(storage,

View File

@@ -223,9 +223,10 @@ void tst_Tasking::testTree_data()
}; };
}; };
const auto setupDone = [storage](int taskId) { const auto setupDone = [storage](int taskId, bool successTask = true) {
return [storage, taskId](const TaskObject &, bool success) { return [storage, taskId, successTask](const TaskObject &, bool success) {
storage->m_log.append({taskId, success ? Handler::Done : Handler::Error}); 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]( const auto createTask = [storage, setupTask, setupDone](
int taskId, bool successTask, milliseconds timeout = 0ms) -> GroupItem { int taskId, bool successTask, milliseconds timeout = 0ms) -> GroupItem {
if (successTask) return TestTask(setupTask(taskId, timeout), setupDone(taskId, 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;
}; };
const auto createSuccessTask = [createTask](int taskId, milliseconds timeout = 0ms) { const auto createSuccessTask = [createTask](int taskId, milliseconds timeout = 0ms) {