TaskTree: Introduce withTimeout()

Make it available for Group or CustomTask items.

Note, that when withTimeout() is used, the total
number of tasks grows by one.

Change-Id: Idc71737ba66b92bdc4bf17599c793b1127d22f5e
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Jarek Kobus
2023-05-29 00:13:12 +02:00
parent 1599106a22
commit 619735d99d
3 changed files with 143 additions and 1 deletions

View File

@@ -567,6 +567,23 @@ void TaskItem::setTaskErrorHandler(const TaskEndHandler &handler)
m_taskHandler.m_errorHandler = handler;
}
TaskItem TaskItem::withTimeout(const TaskItem &item, milliseconds timeout,
const GroupEndHandler &handler)
{
const TimeoutTask::EndHandler taskHandler = handler
? [handler](const milliseconds &) { handler(); } : TimeoutTask::EndHandler();
return Group {
parallel,
stopOnFinished,
Group {
finishAllAndError,
TimeoutTask([timeout](milliseconds &timeoutData) { timeoutData = timeout; },
taskHandler)
},
item
};
}
class TaskTreePrivate;
class TaskNode;

View File

@@ -187,6 +187,8 @@ protected:
static TaskItem groupHandler(const GroupHandler &handler) { return TaskItem({handler}); }
static TaskItem parallelLimit(int limit) { return TaskItem({{}, limit}); }
static TaskItem workflowPolicy(WorkflowPolicy policy) { return TaskItem({{}, {}, policy}); }
static TaskItem withTimeout(const TaskItem &item, std::chrono::milliseconds timeout,
const GroupEndHandler &handler = {});
private:
Type m_type = Type::Group;
@@ -216,6 +218,11 @@ public:
using TaskItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel).
using TaskItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError.
TaskItem withTimeout(std::chrono::milliseconds timeout,
const GroupEndHandler &handler = {}) const {
return TaskItem::withTimeout(*this, timeout, handler);
}
private:
template<typename SetupHandler>
static GroupSetupHandler wrapGroupSetup(SetupHandler &&handler)
@@ -329,6 +336,11 @@ public:
return *this;
}
TaskItem withTimeout(std::chrono::milliseconds timeout,
const GroupEndHandler &handler = {}) const {
return TaskItem::withTimeout(*this, timeout, handler);
}
private:
template<typename SetupFunction>
static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {

View File

@@ -25,7 +25,8 @@ enum class Handler {
GroupDone,
GroupError,
Sync,
BarrierAdvance
BarrierAdvance,
Timeout
};
Q_ENUM_NS(Handler);
@@ -245,6 +246,12 @@ void tst_Tasking::testTree_data()
};
};
const auto setupTimeout = [storage](int taskId) {
return [storage, taskId] {
storage->m_log.append({taskId, Handler::Timeout});
};
};
const auto createTask = [storage, setupTask, setupDone, setupError](
int taskId, bool successTask, milliseconds timeout = 0ms) -> TaskItem {
if (successTask)
@@ -2083,6 +2090,112 @@ void tst_Tasking::testTree_data()
QTest::newRow("MultiBarrierParallelMultiWaitFor")
<< TestData{storage, root4, log4, 6, OnDone::Success};
}
{
// Test CustomTask::withTimeout() combinations:
// 1. When the timeout has triggered or not.
// 2. With and without timeout handler.
const Group root1 {
Storage(storage),
TestTask(setupTask(1, 1000ms), setupDone(1), setupError(1))
.withTimeout(1ms)
};
const Log log1 {
{1, Handler::Setup},
{1, Handler::Error}
};
QTest::newRow("TaskErrorWithTimeout") << TestData{storage, root1, log1, 2,
OnDone::Failure};
const Group root2 {
Storage(storage),
TestTask(setupTask(1, 1000ms), setupDone(1), setupError(1))
.withTimeout(1ms, setupTimeout(1))
};
const Log log2 {
{1, Handler::Setup},
{1, Handler::Timeout},
{1, Handler::Error}
};
QTest::newRow("TaskErrorWithTimeoutHandler") << TestData{storage, root2, log2, 2,
OnDone::Failure};
const Group root3 {
Storage(storage),
TestTask(setupTask(1, 1ms), setupDone(1), setupError(1))
.withTimeout(1000ms)
};
const Log doneLog {
{1, Handler::Setup},
{1, Handler::Done}
};
QTest::newRow("TaskDoneWithTimeout") << TestData{storage, root3, doneLog, 2,
OnDone::Success};
const Group root4 {
Storage(storage),
TestTask(setupTask(1, 1ms), setupDone(1), setupError(1))
.withTimeout(1000ms, setupTimeout(1))
};
QTest::newRow("TaskDoneWithTimeoutHandler") << TestData{storage, root4, doneLog, 2,
OnDone::Success};
}
{
// Test Group::withTimeout() combinations:
// 1. When the timeout has triggered or not.
// 2. With and without timeout handler.
const Group root1 {
Storage(storage),
Group {
createSuccessTask(1, 1000ms)
}.withTimeout(1ms)
};
const Log log1 {
{1, Handler::Setup},
{1, Handler::Error}
};
QTest::newRow("GroupErrorWithTimeout") << TestData{storage, root1, log1, 2,
OnDone::Failure};
// Test Group::withTimeout(), passing custom handler
const Group root2 {
Storage(storage),
Group {
createSuccessTask(1, 1000ms)
}.withTimeout(1ms, setupTimeout(1))
};
const Log log2 {
{1, Handler::Setup},
{1, Handler::Timeout},
{1, Handler::Error}
};
QTest::newRow("GroupErrorWithTimeoutHandler") << TestData{storage, root2, log2, 2,
OnDone::Failure};
const Group root3 {
Storage(storage),
Group {
createSuccessTask(1, 1ms)
}.withTimeout(1000ms)
};
const Log doneLog {
{1, Handler::Setup},
{1, Handler::Done}
};
QTest::newRow("GroupDoneWithTimeout") << TestData{storage, root3, doneLog, 2,
OnDone::Success};
// Test Group::withTimeout(), passing custom handler
const Group root4 {
Storage(storage),
Group {
createSuccessTask(1, 1ms)
}.withTimeout(1000ms, setupTimeout(1))
};
QTest::newRow("GroupDoneWithTimeoutHandler") << TestData{storage, root4, doneLog, 2,
OnDone::Success};
}
}
void tst_Tasking::testTree()