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 continueOnError(WorkflowPolicy::ContinueOnError);
|
||||||
Workflow stopOnDone(WorkflowPolicy::StopOnDone);
|
Workflow stopOnDone(WorkflowPolicy::StopOnDone);
|
||||||
Workflow continueOnDone(WorkflowPolicy::ContinueOnDone);
|
Workflow continueOnDone(WorkflowPolicy::ContinueOnDone);
|
||||||
|
Workflow stopOnFinished(WorkflowPolicy::StopOnFinished);
|
||||||
Workflow optional(WorkflowPolicy::Optional);
|
Workflow optional(WorkflowPolicy::Optional);
|
||||||
|
|
||||||
void TaskItem::addChildren(const QList<TaskItem> &children)
|
void TaskItem::addChildren(const QList<TaskItem> &children)
|
||||||
@@ -511,6 +512,10 @@ bool TaskContainer::RuntimeData::updateSuccessBit(bool success)
|
|||||||
{
|
{
|
||||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::Optional)
|
if (m_constData.m_workflowPolicy == WorkflowPolicy::Optional)
|
||||||
return m_successBit;
|
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
|
const bool donePolicy = m_constData.m_workflowPolicy == WorkflowPolicy::StopOnDone
|
||||||
|| m_constData.m_workflowPolicy == WorkflowPolicy::ContinueOnDone;
|
|| m_constData.m_workflowPolicy == WorkflowPolicy::ContinueOnDone;
|
||||||
@@ -595,7 +600,8 @@ TaskAction TaskContainer::childDone(bool success)
|
|||||||
{
|
{
|
||||||
QTC_CHECK(isRunning());
|
QTC_CHECK(isRunning());
|
||||||
const int limit = m_runtimeData->currentLimit(); // Read before bumping m_doneCount and stop()
|
const int limit = m_runtimeData->currentLimit(); // Read before bumping m_doneCount and stop()
|
||||||
const bool shouldStop = (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnDone && success)
|
const bool shouldStop = m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished
|
||||||
|
|| (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnDone && success)
|
||||||
|| (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnError && !success);
|
|| (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnError && !success);
|
||||||
if (shouldStop)
|
if (shouldStop)
|
||||||
stop();
|
stop();
|
||||||
@@ -1192,6 +1198,14 @@ void TaskNode::invokeEndHandler(bool success)
|
|||||||
\endlist
|
\endlist
|
||||||
If all tasks finish with an error or the group is empty, the group
|
If all tasks finish with an error or the group is empty, the group
|
||||||
finishes with an error.
|
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
|
\row
|
||||||
\li optional
|
\li optional
|
||||||
\li The group executes all tasks and ignores their return state. If all
|
\li The group executes all tasks and ignores their return state. If all
|
||||||
|
@@ -93,19 +93,22 @@ private:
|
|||||||
|
|
||||||
// WorkflowPolicy:
|
// WorkflowPolicy:
|
||||||
// 1. When all children finished with done -> report done, otherwise:
|
// 1. When all children finished with done -> report done, otherwise:
|
||||||
// a) Report error on first error and stop executing other children (including their subtree)
|
// 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
|
// b) On first error - continue executing all children and report error afterwards.
|
||||||
// 2. When all children finished with error -> report error, otherwise:
|
// 2. When all children finished with error -> report error, otherwise:
|
||||||
// a) Report done on first done and stop executing other children (including their subtree)
|
// 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
|
// b) On first done - continue executing all children and report done afterwards.
|
||||||
// 3. Always run all children, ignore their result 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 {
|
enum class WorkflowPolicy {
|
||||||
StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done)
|
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.
|
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)
|
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. (?)
|
ContinueOnDone, // 2b - The same, but children execution continues. Reports error when no children.
|
||||||
Optional // 3 - Always reports done after all children finished
|
StopOnFinished, // 3 - Stops on first finished child and report its result.
|
||||||
|
Optional // 4 - Reports done after all children finished.
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TaskAction
|
enum class TaskAction
|
||||||
@@ -287,6 +290,7 @@ TASKING_EXPORT extern Workflow stopOnError;
|
|||||||
TASKING_EXPORT extern Workflow continueOnError;
|
TASKING_EXPORT extern Workflow continueOnError;
|
||||||
TASKING_EXPORT extern Workflow stopOnDone;
|
TASKING_EXPORT extern Workflow stopOnDone;
|
||||||
TASKING_EXPORT extern Workflow continueOnDone;
|
TASKING_EXPORT extern Workflow continueOnDone;
|
||||||
|
TASKING_EXPORT extern Workflow stopOnFinished;
|
||||||
TASKING_EXPORT extern Workflow optional;
|
TASKING_EXPORT extern Workflow optional;
|
||||||
|
|
||||||
template <typename Task>
|
template <typename Task>
|
||||||
|
@@ -233,8 +233,9 @@ void tst_Tasking::testTree_data()
|
|||||||
const auto setupFailingTask = [setupTaskHelper](int taskId) {
|
const auto setupFailingTask = [setupTaskHelper](int taskId) {
|
||||||
return [=](TestTask &task) { setupTaskHelper(task, taskId, false); };
|
return [=](TestTask &task) { setupTaskHelper(task, taskId, false); };
|
||||||
};
|
};
|
||||||
const auto setupSleepingTask = [setupTaskHelper](int taskId, std::chrono::milliseconds sleep) {
|
const auto setupSleepingTask = [setupTaskHelper](int taskId, bool success,
|
||||||
return [=](TestTask &task) { setupTaskHelper(task, taskId, true, sleep); };
|
std::chrono::milliseconds sleep) {
|
||||||
|
return [=](TestTask &task) { setupTaskHelper(task, taskId, success, sleep); };
|
||||||
};
|
};
|
||||||
const auto setupDynamicTask = [setupTaskHelper](int taskId, TaskAction action) {
|
const auto setupDynamicTask = [setupTaskHelper](int taskId, TaskAction action) {
|
||||||
return [=](TestTask &task) {
|
return [=](TestTask &task) {
|
||||||
@@ -680,6 +681,55 @@ void tst_Tasking::testTree_data()
|
|||||||
QTest::newRow("ContinueOnDone") << TestData{storage, root, log, 3, OnDone::Success};
|
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 {
|
const Group root {
|
||||||
Storage(storage),
|
Storage(storage),
|
||||||
@@ -837,14 +887,14 @@ void tst_Tasking::testTree_data()
|
|||||||
|
|
||||||
// Inside this test the task 2 should finish first, then synchonously:
|
// Inside this test the task 2 should finish first, then synchonously:
|
||||||
// - task 3 should exit setup with error
|
// - 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
|
// - tasks 4 and 5 should be skipped
|
||||||
const Group root2 {
|
const Group root2 {
|
||||||
ParallelLimit(2),
|
ParallelLimit(2),
|
||||||
Storage(storage),
|
Storage(storage),
|
||||||
Group {
|
Group {
|
||||||
OnGroupSetup(groupSetup(1)),
|
OnGroupSetup(groupSetup(1)),
|
||||||
Test(setupSleepingTask(1, 10ms))
|
Test(setupSleepingTask(1, true, 10ms))
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
OnGroupSetup(groupSetup(2)),
|
OnGroupSetup(groupSetup(2)),
|
||||||
@@ -879,11 +929,11 @@ void tst_Tasking::testTree_data()
|
|||||||
ParallelLimit(2),
|
ParallelLimit(2),
|
||||||
Group {
|
Group {
|
||||||
OnGroupSetup(groupSetup(1)),
|
OnGroupSetup(groupSetup(1)),
|
||||||
Test(setupSleepingTask(1, 20ms))
|
Test(setupSleepingTask(1, true, 20ms))
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
OnGroupSetup(groupSetup(2)),
|
OnGroupSetup(groupSetup(2)),
|
||||||
Test(setupTask(2), [](const TestTask &) { QThread::msleep(10); })
|
Test(setupSleepingTask(2, true, 10ms))
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
OnGroupSetup(groupSetup(3)),
|
OnGroupSetup(groupSetup(3)),
|
||||||
|
Reference in New Issue
Block a user