TaskTree: Prepare infrastructure for iterators

Task-number: QTCREATORBUG-30081
Change-Id: I5ad78314445508f52c3fb2ab0fd696940fe7b6dd
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2024-01-03 18:57:17 +01:00
parent ebaa13e277
commit 1610bb8ff4

View File

@@ -1345,12 +1345,14 @@ GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout,
class TaskTreePrivate; class TaskTreePrivate;
class TaskNode; class TaskNode;
class RuntimeTask;
class RuntimeContainer; class RuntimeContainer;
class RuntimeIteration;
class RuntimeTask;
class ExecutionContextActivator class ExecutionContextActivator
{ {
public: public:
ExecutionContextActivator(RuntimeIteration *iteration) { activateContext(iteration); }
ExecutionContextActivator(RuntimeContainer *container) { activateContext(container); } ExecutionContextActivator(RuntimeContainer *container) { activateContext(container); }
~ExecutionContextActivator() { ~ExecutionContextActivator() {
for (int i = m_activeStorages.size() - 1; i >= 0; --i) // iterate in reverse order for (int i = m_activeStorages.size() - 1; i >= 0; --i) // iterate in reverse order
@@ -1358,6 +1360,7 @@ public:
} }
private: private:
void activateContext(RuntimeIteration *iteration);
void activateContext(RuntimeContainer *container); void activateContext(RuntimeContainer *container);
QList<StorageBase> m_activeStorages; QList<StorageBase> m_activeStorages;
}; };
@@ -1448,13 +1451,13 @@ public:
SetupResult start(RuntimeContainer *container); SetupResult start(RuntimeContainer *container);
SetupResult continueStart(RuntimeContainer *container, SetupResult startAction); SetupResult continueStart(RuntimeContainer *container, SetupResult startAction);
SetupResult startChildren(RuntimeContainer *container); SetupResult startChildren(RuntimeContainer *container);
SetupResult childDone(RuntimeContainer *container, bool success); SetupResult childDone(RuntimeIteration *iteration, bool success);
void stop(RuntimeContainer *container); void stop(RuntimeContainer *container);
bool invokeDoneHandler(RuntimeContainer *container, DoneWith doneWith); bool invokeDoneHandler(RuntimeContainer *container, DoneWith doneWith);
template <typename Handler, typename ...Args, template <typename Container, typename Handler, typename ...Args,
typename ReturnType = std::invoke_result_t<Handler, Args...>> typename ReturnType = std::invoke_result_t<Handler, Args...>>
ReturnType invokeHandler(RuntimeContainer *container, Handler &&handler, Args &&...args) ReturnType invokeHandler(Container *container, Handler &&handler, Args &&...args)
{ {
ExecutionContextActivator activator(container); ExecutionContextActivator activator(container);
GuardLocker locker(m_guard); GuardLocker locker(m_guard);
@@ -1487,6 +1490,21 @@ static bool initialSuccessBit(WorkflowPolicy workflowPolicy)
return false; return false;
} }
class RuntimeIteration
{
Q_DISABLE_COPY(RuntimeIteration)
public:
int continueIndex() const;
int currentLimit() const;
void deleteChild(RuntimeTask *node);
const int m_iterationIndex = 0;
RuntimeContainer *m_container = nullptr;
std::vector<std::unique_ptr<RuntimeTask>> m_children = {}; // Owning.
int m_doneCount = 0;
};
class RuntimeContainer class RuntimeContainer
{ {
Q_DISABLE_COPY(RuntimeContainer) Q_DISABLE_COPY(RuntimeContainer)
@@ -1512,21 +1530,18 @@ public:
static QList<StoragePtr> createStorages(const ContainerNode &container); static QList<StoragePtr> createStorages(const ContainerNode &container);
bool isStarting() const { return m_startGuard.isLocked(); } bool isStarting() const { return m_startGuard.isLocked(); }
int continueIndex() const; RuntimeIteration *parentIteration() const;
int currentLimit() const;
RuntimeContainer *parentContainer() const;
bool updateSuccessBit(bool success); bool updateSuccessBit(bool success);
void deleteChild(RuntimeTask *node);
const ContainerNode &m_containerNode; // Not owning. const ContainerNode &m_containerNode; // Not owning.
RuntimeTask *m_parentTask = nullptr; // Not owning. RuntimeTask *m_parentTask = nullptr; // Not owning.
const QList<StoragePtr> m_storages; const QList<StoragePtr> m_storages; // Owning.
std::vector<std::unique_ptr<RuntimeTask>> m_children; // Owning.
bool m_successBit = true; bool m_successBit = true;
bool m_callStorageDoneHandlersOnDestruction = false; bool m_callStorageDoneHandlersOnDestruction = false;
int m_doneCount = 0;
Guard m_startGuard; Guard m_startGuard;
std::vector<std::unique_ptr<RuntimeIteration>> m_iterations; // Owning.
}; };
class RuntimeTask class RuntimeTask
@@ -1534,17 +1549,18 @@ class RuntimeTask
Q_DISABLE_COPY(RuntimeTask) Q_DISABLE_COPY(RuntimeTask)
public: public:
RuntimeTask(const TaskNode &taskNode, RuntimeContainer *parentContainer)
: m_taskNode(taskNode)
, m_parentContainer(parentContainer)
{}
const TaskNode &m_taskNode; // Not owning. const TaskNode &m_taskNode; // Not owning.
RuntimeContainer *m_parentContainer = nullptr; // Not owning. RuntimeIteration *m_parentIteration = nullptr; // Not owning.
std::optional<RuntimeContainer> m_container; // Owning. std::optional<RuntimeContainer> m_container = {}; // Owning.
std::unique_ptr<TaskInterface> m_task; // Owning. std::unique_ptr<TaskInterface> m_task = {}; // Owning.
}; };
void ExecutionContextActivator::activateContext(RuntimeIteration *iteration)
{
// TODO: activate iterator
activateContext(iteration->m_container);
}
void ExecutionContextActivator::activateContext(RuntimeContainer *container) void ExecutionContextActivator::activateContext(RuntimeContainer *container)
{ {
const ContainerNode &containerNode = container->m_containerNode; const ContainerNode &containerNode = container->m_containerNode;
@@ -1557,8 +1573,8 @@ void ExecutionContextActivator::activateContext(RuntimeContainer *container)
} }
// Go to the parent after activating this storages so that storage shadowing works // Go to the parent after activating this storages so that storage shadowing works
// in the direction from child to parent root. // in the direction from child to parent root.
if (container->parentContainer()) if (container->parentIteration())
activateContext(container->parentContainer()); activateContext(container->parentIteration());
} }
void TaskTreePrivate::start() void TaskTreePrivate::start()
@@ -1572,7 +1588,7 @@ void TaskTreePrivate::start()
QT_ASSERT(m_storages.contains(it.key()), qWarning("The registered storage doesn't " QT_ASSERT(m_storages.contains(it.key()), qWarning("The registered storage doesn't "
"exist in task tree. Its handlers will never be called.")); "exist in task tree. Its handlers will never be called."));
} }
m_runtimeRoot.reset(new RuntimeTask(*m_root, nullptr)); m_runtimeRoot.reset(new RuntimeTask{*m_root, nullptr});
start(m_runtimeRoot.get()); start(m_runtimeRoot.get());
} }
@@ -1616,6 +1632,29 @@ void TaskTreePrivate::emitDone(DoneWith result)
emit q->done(result); emit q->done(result);
} }
int RuntimeIteration::continueIndex() const
{
return m_doneCount ? qMin(m_doneCount + m_container->m_containerNode.m_parallelLimit - 1,
int(m_container->m_containerNode.m_children.size())) : 0;
}
int RuntimeIteration::currentLimit() const
{
// TODO: Handle children well
const int childCount = int(m_container->m_containerNode.m_children.size());
return m_container->m_containerNode.m_parallelLimit
? qMin(m_doneCount + m_container->m_containerNode.m_parallelLimit, childCount) : childCount;
}
void RuntimeIteration::deleteChild(RuntimeTask *task)
{
const auto it = std::find_if(m_children.cbegin(), m_children.cend(), [task](const auto &ptr) {
return ptr.get() == task;
});
if (it != m_children.cend())
m_children.erase(it);
}
static std::vector<TaskNode> createChildren(TaskTreePrivate *taskTreePrivate, static std::vector<TaskNode> createChildren(TaskTreePrivate *taskTreePrivate,
const QList<GroupItem> &children) const QList<GroupItem> &children)
{ {
@@ -1651,23 +1690,9 @@ QList<StoragePtr> RuntimeContainer::createStorages(const ContainerNode &containe
return storages; return storages;
} }
int RuntimeContainer::continueIndex() const RuntimeIteration *RuntimeContainer::parentIteration() const
{ {
return m_doneCount ? qMin(m_doneCount + m_containerNode.m_parallelLimit - 1, return m_parentTask->m_parentIteration;
int(m_containerNode.m_children.size())) : 0;
}
int RuntimeContainer::currentLimit() const
{
// TODO: Handle children well
const int childCount = int(m_containerNode.m_children.size());
return m_containerNode.m_parallelLimit
? qMin(m_doneCount + m_containerNode.m_parallelLimit, childCount) : childCount;
}
RuntimeContainer *RuntimeContainer::parentContainer() const
{
return m_parentTask->m_parentContainer;
} }
bool RuntimeContainer::updateSuccessBit(bool success) bool RuntimeContainer::updateSuccessBit(bool success)
@@ -1686,15 +1711,6 @@ bool RuntimeContainer::updateSuccessBit(bool success)
return m_successBit; return m_successBit;
} }
void RuntimeContainer::deleteChild(RuntimeTask *task)
{
const auto it = std::find_if(m_children.cbegin(), m_children.cend(), [task](const auto &ptr) {
return ptr.get() == task;
});
if (it != m_children.cend())
m_children.erase(it);
}
SetupResult TaskTreePrivate::start(RuntimeContainer *container) SetupResult TaskTreePrivate::start(RuntimeContainer *container)
{ {
SetupResult startAction = SetupResult::Continue; SetupResult startAction = SetupResult::Continue;
@@ -1718,14 +1734,14 @@ SetupResult TaskTreePrivate::continueStart(RuntimeContainer *container, SetupRes
: startAction; : startAction;
if (groupAction != SetupResult::Continue) { if (groupAction != SetupResult::Continue) {
const bool bit = container->updateSuccessBit(groupAction == SetupResult::StopWithSuccess); const bool bit = container->updateSuccessBit(groupAction == SetupResult::StopWithSuccess);
RuntimeContainer *parentContainer = container->parentContainer(); RuntimeIteration *parentIteration = container->parentIteration();
RuntimeTask *parentTask = container->m_parentTask; RuntimeTask *parentTask = container->m_parentTask;
QT_CHECK(parentTask); QT_CHECK(parentTask);
const bool result = invokeDoneHandler(container, bit ? DoneWith::Success : DoneWith::Error); const bool result = invokeDoneHandler(container, bit ? DoneWith::Success : DoneWith::Error);
if (parentContainer) { if (parentIteration) {
parentContainer->deleteChild(parentTask); parentIteration->deleteChild(parentTask);
if (!parentContainer->isStarting()) if (!parentIteration->m_container->isStarting())
childDone(parentContainer, result); childDone(parentIteration, result);
} else { } else {
QT_CHECK(m_runtimeRoot.get() == parentTask); QT_CHECK(m_runtimeRoot.get() == parentTask);
m_runtimeRoot.reset(); m_runtimeRoot.reset();
@@ -1737,39 +1753,49 @@ SetupResult TaskTreePrivate::continueStart(RuntimeContainer *container, SetupRes
SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container) SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container)
{ {
if (container->m_containerNode.m_parallelLimit == 0 && container->m_doneCount > 0) if (container->m_containerNode.m_parallelLimit == 0 && !container->m_iterations.empty())
return SetupResult::Continue; return SetupResult::Continue;
if (container->m_iterations.empty()) {
RuntimeIteration *iteration = new RuntimeIteration{0, container};
container->m_iterations.emplace_back(iteration);
}
GuardLocker locker(container->m_startGuard); GuardLocker locker(container->m_startGuard);
for (int i = container->continueIndex(); i < int(container->m_containerNode.m_children.size()); ++i) { for (auto &iteration : container->m_iterations) {
const int limit = container->currentLimit(); for (int i = iteration->continueIndex(); i < int(container->m_containerNode.m_children.size()); ++i) {
if (i >= limit) const int limit = iteration->currentLimit();
break; if (i >= limit)
break;
RuntimeTask *newTask = new RuntimeTask(container->m_containerNode.m_children.at(i), container); RuntimeTask *newTask = new RuntimeTask{container->m_containerNode.m_children.at(i),
container->m_children.emplace_back(newTask); iteration.get()};
iteration->m_children.emplace_back(newTask);
const SetupResult startAction = start(newTask); const SetupResult startAction = start(newTask);
if (startAction == SetupResult::Continue) if (startAction == SetupResult::Continue)
continue; continue;
const SetupResult finalizeAction = childDone(container, startAction == SetupResult::StopWithSuccess); const SetupResult finalizeAction = childDone(iteration.get(),
if (finalizeAction == SetupResult::Continue) startAction == SetupResult::StopWithSuccess);
continue; if (finalizeAction == SetupResult::Continue)
continue;
int skippedTaskCount = 0; int skippedTaskCount = 0;
// Skip scheduled but not run yet. The current (i) was already notified. // Skip scheduled but not run yet. The current (i) was already notified.
for (int j = i + 1; j < limit; ++j) for (int j = i + 1; j < limit; ++j)
skippedTaskCount += container->m_containerNode.m_children.at(j).taskCount(); skippedTaskCount += container->m_containerNode.m_children.at(j).taskCount();
// TODO: Handle progress well // TODO: Handle progress well
advanceProgress(skippedTaskCount); advanceProgress(skippedTaskCount);
return finalizeAction; return finalizeAction;
}
} }
return SetupResult::Continue; return SetupResult::Continue;
} }
SetupResult TaskTreePrivate::childDone(RuntimeContainer *container, bool success) SetupResult TaskTreePrivate::childDone(RuntimeIteration *iteration, bool success)
{ {
RuntimeContainer *container = iteration->m_container;
const WorkflowPolicy &workflowPolicy = container->m_containerNode.m_workflowPolicy; const WorkflowPolicy &workflowPolicy = container->m_containerNode.m_workflowPolicy;
const bool shouldStop = workflowPolicy == WorkflowPolicy::StopOnSuccessOrError const bool shouldStop = workflowPolicy == WorkflowPolicy::StopOnSuccessOrError
|| (workflowPolicy == WorkflowPolicy::StopOnSuccess && success) || (workflowPolicy == WorkflowPolicy::StopOnSuccess && success)
@@ -1777,10 +1803,10 @@ SetupResult TaskTreePrivate::childDone(RuntimeContainer *container, bool success
if (shouldStop) if (shouldStop)
stop(container); stop(container);
++container->m_doneCount; ++iteration->m_doneCount;
const bool updatedSuccess = container->updateSuccessBit(success); const bool updatedSuccess = container->updateSuccessBit(success);
const SetupResult startAction const SetupResult startAction
= (shouldStop || container->m_doneCount == int(container->m_containerNode.m_children.size())) = (shouldStop || iteration->m_doneCount == int(container->m_containerNode.m_children.size()))
? toSetupResult(updatedSuccess) : SetupResult::Continue; ? toSetupResult(updatedSuccess) : SetupResult::Continue;
if (container->isStarting()) if (container->isStarting())
@@ -1790,14 +1816,17 @@ SetupResult TaskTreePrivate::childDone(RuntimeContainer *container, bool success
void TaskTreePrivate::stop(RuntimeContainer *container) void TaskTreePrivate::stop(RuntimeContainer *container)
{ {
for (auto &child : container->m_children)
stop(child.get());
int skippedTaskCount = 0; int skippedTaskCount = 0;
for (int i = container->currentLimit(); i < int(container->m_containerNode.m_children.size()); ++i) for (auto &iteration : container->m_iterations) {
skippedTaskCount += container->m_containerNode.m_children.at(i).taskCount(); for (auto &child : iteration->m_children)
stop(child.get());
// TODO: Do it only for 0-index iteration (up to root).
for (int i = iteration->currentLimit(); i < int(container->m_containerNode.m_children.size()); ++i)
skippedTaskCount += container->m_containerNode.m_children.at(i).taskCount();
}
// TODO: Handle progress well // TODO: Handle progress well
// How to advance skipped tasks inside nested iterations?
advanceProgress(skippedTaskCount); advanceProgress(skippedTaskCount);
} }
@@ -1829,7 +1858,7 @@ SetupResult TaskTreePrivate::start(RuntimeTask *node)
const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler; const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler;
node->m_task.reset(handler.m_createHandler()); node->m_task.reset(handler.m_createHandler());
const SetupResult startAction = handler.m_setupHandler const SetupResult startAction = handler.m_setupHandler
? invokeHandler(node->m_parentContainer, handler.m_setupHandler, *node->m_task.get()) ? invokeHandler(node->m_parentIteration, handler.m_setupHandler, *node->m_task.get())
: SetupResult::Continue; : SetupResult::Continue;
if (startAction != SetupResult::Continue) { if (startAction != SetupResult::Continue) {
// TODO: Handle progress well // TODO: Handle progress well
@@ -1844,12 +1873,12 @@ SetupResult TaskTreePrivate::start(RuntimeTask *node)
const bool result = invokeDoneHandler(node, toDoneWith(doneResult)); const bool result = invokeDoneHandler(node, toDoneWith(doneResult));
QObject::disconnect(node->m_task.get(), &TaskInterface::done, q, nullptr); QObject::disconnect(node->m_task.get(), &TaskInterface::done, q, nullptr);
node->m_task.release()->deleteLater(); node->m_task.release()->deleteLater();
RuntimeContainer *parentContainer = node->m_parentContainer; RuntimeIteration *parentIteration = node->m_parentIteration;
parentContainer->deleteChild(node); parentIteration->deleteChild(node);
if (parentContainer->isStarting()) if (parentIteration->m_container->isStarting())
*unwindAction = toSetupResult(result); *unwindAction = toSetupResult(result);
else else
childDone(parentContainer, result); childDone(parentIteration, result);
}); });
node->m_task->start(); node->m_task->start();
@@ -1876,7 +1905,7 @@ bool TaskTreePrivate::invokeDoneHandler(RuntimeTask *node, DoneWith doneWith)
DoneResult result = toDoneResult(doneWith); DoneResult result = toDoneResult(doneWith);
const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler; const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler;
if (handler.m_doneHandler && shouldCall(handler.m_callDoneIf, doneWith)) { if (handler.m_doneHandler && shouldCall(handler.m_callDoneIf, doneWith)) {
result = invokeHandler(node->m_parentContainer, result = invokeHandler(node->m_parentIteration,
handler.m_doneHandler, *node->m_task.get(), doneWith); handler.m_doneHandler, *node->m_task.get(), doneWith);
} }
// TODO: Handle progress well // TODO: Handle progress well