forked from qt-creator/qt-creator
TaskTree: Introduce WorkflowPolicy::StopOnFinished
The policy is useful mainly in parallel mode. It stops executing the Group when any task finishes. It reports the task's result. Change-Id: I7aa98365cdc4c1eb869ab419d42d0cc5438d43bf Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -114,6 +114,7 @@ Workflow stopOnError(WorkflowPolicy::StopOnError);
|
||||
Workflow continueOnError(WorkflowPolicy::ContinueOnError);
|
||||
Workflow stopOnDone(WorkflowPolicy::StopOnDone);
|
||||
Workflow continueOnDone(WorkflowPolicy::ContinueOnDone);
|
||||
Workflow stopOnFinished(WorkflowPolicy::StopOnFinished);
|
||||
Workflow optional(WorkflowPolicy::Optional);
|
||||
|
||||
void TaskItem::addChildren(const QList<TaskItem> &children)
|
||||
@@ -511,6 +512,10 @@ bool TaskContainer::RuntimeData::updateSuccessBit(bool success)
|
||||
{
|
||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::Optional)
|
||||
return m_successBit;
|
||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished) {
|
||||
m_successBit = success;
|
||||
return m_successBit;
|
||||
}
|
||||
|
||||
const bool donePolicy = m_constData.m_workflowPolicy == WorkflowPolicy::StopOnDone
|
||||
|| m_constData.m_workflowPolicy == WorkflowPolicy::ContinueOnDone;
|
||||
@@ -595,8 +600,9 @@ TaskAction TaskContainer::childDone(bool success)
|
||||
{
|
||||
QTC_CHECK(isRunning());
|
||||
const int limit = m_runtimeData->currentLimit(); // Read before bumping m_doneCount and stop()
|
||||
const bool shouldStop = (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnDone && success)
|
||||
|| (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnError && !success);
|
||||
const bool shouldStop = m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished
|
||||
|| (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnDone && success)
|
||||
|| (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnError && !success);
|
||||
if (shouldStop)
|
||||
stop();
|
||||
|
||||
@@ -1146,15 +1152,15 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
\row
|
||||
\li stopOnError
|
||||
\li Default. If a task finishes with an error, the group:
|
||||
\list 1
|
||||
\li Stops the running tasks (if any - for example, in parallel
|
||||
mode).
|
||||
\li Skips executing tasks it has not started (for example, in the
|
||||
sequential mode).
|
||||
\li Immediately finishes with an error.
|
||||
\endlist
|
||||
If all child tasks finish successfully or the group is empty, the group
|
||||
finishes with success.
|
||||
\list 1
|
||||
\li Stops the running tasks (if any - for example, in parallel
|
||||
mode).
|
||||
\li Skips executing tasks it has not started (for example, in the
|
||||
sequential mode).
|
||||
\li Immediately finishes with an error.
|
||||
\endlist
|
||||
If all child tasks finish successfully or the group is empty, the group
|
||||
finishes with success.
|
||||
\row
|
||||
\li continueOnError
|
||||
\li Similar to stopOnError, but in case any child finishes with
|
||||
@@ -1162,22 +1168,22 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
and the group reports an error afterwards, even when some other
|
||||
tasks in group finished with success.
|
||||
If a task finishes with an error, the group:
|
||||
\list 1
|
||||
\li Continues executing the tasks that are running or have not
|
||||
started yet.
|
||||
\li Finishes with an error when all tasks finish.
|
||||
\endlist
|
||||
If all tasks finish successfully or the group is empty, the group
|
||||
finishes with success.
|
||||
\list 1
|
||||
\li Continues executing the tasks that are running or have not
|
||||
started yet.
|
||||
\li Finishes with an error when all tasks finish.
|
||||
\endlist
|
||||
If all tasks finish successfully or the group is empty, the group
|
||||
finishes with success.
|
||||
\row
|
||||
\li stopOnDone
|
||||
\li If a task finishes with success, the group:
|
||||
\list 1
|
||||
\li Stops running tasks and skips those that it has not started.
|
||||
\li Immediately finishes with success.
|
||||
\endlist
|
||||
If all tasks finish with an error or the group is empty, the group
|
||||
finishes with an error.
|
||||
\list 1
|
||||
\li Stops running tasks and skips those that it has not started.
|
||||
\li Immediately finishes with success.
|
||||
\endlist
|
||||
If all tasks finish with an error or the group is empty, the group
|
||||
finishes with an error.
|
||||
\row
|
||||
\li continueOnDone
|
||||
\li Similar to stopOnDone, but in case any child finishes
|
||||
@@ -1185,13 +1191,21 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
and the group reports success afterwards, even when some other
|
||||
tasks in group finished with an error.
|
||||
If a task finishes with success, the group:
|
||||
\list 1
|
||||
\li Continues executing the tasks that are running or have not
|
||||
started yet.
|
||||
\li Finishes with success when all tasks finish.
|
||||
\endlist
|
||||
If all tasks finish with an error or the group is empty, the group
|
||||
finishes with an error.
|
||||
\list 1
|
||||
\li Continues executing the tasks that are running or have not
|
||||
started yet.
|
||||
\li Finishes with success when all tasks finish.
|
||||
\endlist
|
||||
If all tasks finish with an error or the group is empty, the group
|
||||
finishes with an error.
|
||||
\row
|
||||
\li stopOnFinished
|
||||
\li The group starts as many tasks as it can. When a task finishes
|
||||
the group stops and reports the task's result.
|
||||
When the group is empty, it finishes immediately with success.
|
||||
Useful only in parallel mode.
|
||||
In sequential mode, only the first task is started, and when finished,
|
||||
the group finishes too, so the other tasks are ignored.
|
||||
\row
|
||||
\li optional
|
||||
\li The group executes all tasks and ignores their return state. If all
|
||||
|
@@ -93,19 +93,22 @@ private:
|
||||
|
||||
// WorkflowPolicy:
|
||||
// 1. When all children finished with done -> report done, otherwise:
|
||||
// a) Report error on first error and stop executing other children (including their subtree)
|
||||
// b) On first error - continue executing all children and report error afterwards
|
||||
// a) Report error on first error and stop executing other children (including their subtree).
|
||||
// b) On first error - continue executing all children and report error afterwards.
|
||||
// 2. When all children finished with error -> report error, otherwise:
|
||||
// a) Report done on first done and stop executing other children (including their subtree)
|
||||
// b) On first done - continue executing all children and report done afterwards
|
||||
// 3. Always run all children, ignore their result and report done afterwards
|
||||
// a) Report done on first done and stop executing other children (including their subtree).
|
||||
// b) On first done - continue executing all children and report done afterwards.
|
||||
// 3. Stops on first finished child. In sequential mode it will never run other children then the first one.
|
||||
// Useful only in parallel mode.
|
||||
// 4. Always run all children, ignore their result and report done afterwards.
|
||||
|
||||
enum class WorkflowPolicy {
|
||||
StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done)
|
||||
ContinueOnError, // 1b - The same, but children execution continues. When no children it reports done.
|
||||
StopOnDone, // 2a - Reports done on first child done, otherwise error (if all children were error)
|
||||
ContinueOnDone, // 2b - The same, but children execution continues. When no children it reports done. (?)
|
||||
Optional // 3 - Always reports done after all children finished
|
||||
StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done).
|
||||
ContinueOnError, // 1b - The same, but children execution continues. Reports done when no children.
|
||||
StopOnDone, // 2a - Reports done on first child done, otherwise error (if all children were error).
|
||||
ContinueOnDone, // 2b - The same, but children execution continues. Reports error when no children.
|
||||
StopOnFinished, // 3 - Stops on first finished child and report its result.
|
||||
Optional // 4 - Reports done after all children finished.
|
||||
};
|
||||
|
||||
enum class TaskAction
|
||||
@@ -287,6 +290,7 @@ TASKING_EXPORT extern Workflow stopOnError;
|
||||
TASKING_EXPORT extern Workflow continueOnError;
|
||||
TASKING_EXPORT extern Workflow stopOnDone;
|
||||
TASKING_EXPORT extern Workflow continueOnDone;
|
||||
TASKING_EXPORT extern Workflow stopOnFinished;
|
||||
TASKING_EXPORT extern Workflow optional;
|
||||
|
||||
template <typename Task>
|
||||
|
@@ -233,8 +233,9 @@ void tst_Tasking::testTree_data()
|
||||
const auto setupFailingTask = [setupTaskHelper](int taskId) {
|
||||
return [=](TestTask &task) { setupTaskHelper(task, taskId, false); };
|
||||
};
|
||||
const auto setupSleepingTask = [setupTaskHelper](int taskId, std::chrono::milliseconds sleep) {
|
||||
return [=](TestTask &task) { setupTaskHelper(task, taskId, true, sleep); };
|
||||
const auto setupSleepingTask = [setupTaskHelper](int taskId, bool success,
|
||||
std::chrono::milliseconds sleep) {
|
||||
return [=](TestTask &task) { setupTaskHelper(task, taskId, success, sleep); };
|
||||
};
|
||||
const auto setupDynamicTask = [setupTaskHelper](int taskId, TaskAction action) {
|
||||
return [=](TestTask &task) {
|
||||
@@ -680,6 +681,55 @@ void tst_Tasking::testTree_data()
|
||||
QTest::newRow("ContinueOnDone") << TestData{storage, root, log, 3, OnDone::Success};
|
||||
}
|
||||
|
||||
{
|
||||
const Group root = constructSimpleSequence(stopOnFinished);
|
||||
const Log log {
|
||||
{1, Handler::Setup},
|
||||
{1, Handler::Done},
|
||||
{0, Handler::GroupDone}
|
||||
};
|
||||
QTest::newRow("StopOnFinished") << TestData{storage, root, log, 3, OnDone::Success};
|
||||
}
|
||||
|
||||
{
|
||||
const auto setupRoot = [=](bool firstSuccess, bool secondSuccess) {
|
||||
return Group {
|
||||
parallel,
|
||||
stopOnFinished,
|
||||
Storage(storage),
|
||||
Test(setupSleepingTask(1, firstSuccess, 1000ms), logDone, logError),
|
||||
Test(setupSleepingTask(2, secondSuccess, 5ms), logDone, logError),
|
||||
OnGroupDone(groupDone(0)),
|
||||
OnGroupError(groupError(0))
|
||||
};
|
||||
};
|
||||
|
||||
const Group root1 = setupRoot(true, true);
|
||||
const Group root2 = setupRoot(true, false);
|
||||
const Group root3 = setupRoot(false, true);
|
||||
const Group root4 = setupRoot(false, false);
|
||||
|
||||
const Log success {
|
||||
{1, Handler::Setup},
|
||||
{2, Handler::Setup},
|
||||
{2, Handler::Done},
|
||||
{1, Handler::Error},
|
||||
{0, Handler::GroupDone}
|
||||
};
|
||||
const Log failure {
|
||||
{1, Handler::Setup},
|
||||
{2, Handler::Setup},
|
||||
{2, Handler::Error},
|
||||
{1, Handler::Error},
|
||||
{0, Handler::GroupError}
|
||||
};
|
||||
|
||||
QTest::newRow("StopOnFinished1") << TestData{storage, root1, success, 2, OnDone::Success};
|
||||
QTest::newRow("StopOnFinished2") << TestData{storage, root2, failure, 2, OnDone::Failure};
|
||||
QTest::newRow("StopOnFinished3") << TestData{storage, root3, success, 2, OnDone::Success};
|
||||
QTest::newRow("StopOnFinished4") << TestData{storage, root4, failure, 2, OnDone::Failure};
|
||||
}
|
||||
|
||||
{
|
||||
const Group root {
|
||||
Storage(storage),
|
||||
@@ -837,14 +887,14 @@ void tst_Tasking::testTree_data()
|
||||
|
||||
// Inside this test the task 2 should finish first, then synchonously:
|
||||
// - task 3 should exit setup with error
|
||||
// - task 1 should be stopped as a consequence of error inside the group
|
||||
// - task 1 should be stopped as a consequence of the error inside the group
|
||||
// - tasks 4 and 5 should be skipped
|
||||
const Group root2 {
|
||||
ParallelLimit(2),
|
||||
Storage(storage),
|
||||
Group {
|
||||
OnGroupSetup(groupSetup(1)),
|
||||
Test(setupSleepingTask(1, 10ms))
|
||||
Test(setupSleepingTask(1, true, 10ms))
|
||||
},
|
||||
Group {
|
||||
OnGroupSetup(groupSetup(2)),
|
||||
@@ -879,11 +929,11 @@ void tst_Tasking::testTree_data()
|
||||
ParallelLimit(2),
|
||||
Group {
|
||||
OnGroupSetup(groupSetup(1)),
|
||||
Test(setupSleepingTask(1, 20ms))
|
||||
Test(setupSleepingTask(1, true, 20ms))
|
||||
},
|
||||
Group {
|
||||
OnGroupSetup(groupSetup(2)),
|
||||
Test(setupTask(2), [](const TestTask &) { QThread::msleep(10); })
|
||||
Test(setupSleepingTask(2, true, 10ms))
|
||||
},
|
||||
Group {
|
||||
OnGroupSetup(groupSetup(3)),
|
||||
|
Reference in New Issue
Block a user