2022-10-12 14:30:24 +02:00
|
|
|
// Copyright (C) 2022 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
#include "tasktree.h"
|
|
|
|
|
|
|
|
|
|
#include "guard.h"
|
|
|
|
|
#include "qtcassert.h"
|
|
|
|
|
|
2023-01-20 10:44:21 +01:00
|
|
|
#include <QSet>
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
namespace Utils {
|
|
|
|
|
namespace Tasking {
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
static TaskAction toTaskAction(bool success)
|
|
|
|
|
{
|
|
|
|
|
return success ? TaskAction::StopWithDone : TaskAction::StopWithError;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-21 12:20:21 +01:00
|
|
|
bool TreeStorageBase::isValid() const
|
|
|
|
|
{
|
|
|
|
|
return m_storageData && m_storageData->m_constructor && m_storageData->m_destructor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TreeStorageBase::TreeStorageBase(StorageConstructor ctor, StorageDestructor dtor)
|
|
|
|
|
: m_storageData(new StorageData{ctor, dtor}) { }
|
|
|
|
|
|
2022-11-22 15:21:45 +01:00
|
|
|
TreeStorageBase::StorageData::~StorageData()
|
|
|
|
|
{
|
|
|
|
|
QTC_CHECK(m_storageHash.isEmpty());
|
2022-11-29 19:58:54 +01:00
|
|
|
for (void *ptr : std::as_const(m_storageHash))
|
2022-11-22 15:21:45 +01:00
|
|
|
m_destructor(ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-21 12:20:21 +01:00
|
|
|
void *TreeStorageBase::activeStorageVoid() const
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_storageData->m_activeStorage, return nullptr);
|
|
|
|
|
const auto it = m_storageData->m_storageHash.constFind(m_storageData->m_activeStorage);
|
|
|
|
|
QTC_ASSERT(it != m_storageData->m_storageHash.constEnd(), return nullptr);
|
|
|
|
|
return it.value();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TreeStorageBase::createStorage()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_storageData->m_constructor, return 0); // TODO: add isValid()?
|
|
|
|
|
QTC_ASSERT(m_storageData->m_destructor, return 0);
|
|
|
|
|
QTC_ASSERT(m_storageData->m_activeStorage == 0, return 0); // TODO: should be allowed?
|
|
|
|
|
const int newId = ++m_storageData->m_storageCounter;
|
|
|
|
|
m_storageData->m_storageHash.insert(newId, m_storageData->m_constructor());
|
|
|
|
|
return newId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TreeStorageBase::deleteStorage(int id)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_storageData->m_constructor, return); // TODO: add isValid()?
|
|
|
|
|
QTC_ASSERT(m_storageData->m_destructor, return);
|
|
|
|
|
QTC_ASSERT(m_storageData->m_activeStorage == 0, return); // TODO: should be allowed?
|
2022-11-29 19:58:54 +01:00
|
|
|
const auto it = m_storageData->m_storageHash.constFind(id);
|
|
|
|
|
QTC_ASSERT(it != m_storageData->m_storageHash.constEnd(), return);
|
2022-11-21 12:20:21 +01:00
|
|
|
m_storageData->m_destructor(it.value());
|
|
|
|
|
m_storageData->m_storageHash.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// passing 0 deactivates currently active storage
|
|
|
|
|
void TreeStorageBase::activateStorage(int id)
|
|
|
|
|
{
|
|
|
|
|
if (id == 0) {
|
|
|
|
|
QTC_ASSERT(m_storageData->m_activeStorage, return);
|
|
|
|
|
m_storageData->m_activeStorage = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QTC_ASSERT(m_storageData->m_activeStorage == 0, return);
|
|
|
|
|
const auto it = m_storageData->m_storageHash.find(id);
|
|
|
|
|
QTC_ASSERT(it != m_storageData->m_storageHash.end(), return);
|
|
|
|
|
m_storageData->m_activeStorage = id;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-06 16:07:16 +01:00
|
|
|
ParallelLimit sequential(1);
|
|
|
|
|
ParallelLimit parallel(0);
|
2022-11-11 10:37:02 +01:00
|
|
|
Workflow stopOnError(WorkflowPolicy::StopOnError);
|
|
|
|
|
Workflow continueOnError(WorkflowPolicy::ContinueOnError);
|
|
|
|
|
Workflow stopOnDone(WorkflowPolicy::StopOnDone);
|
|
|
|
|
Workflow continueOnDone(WorkflowPolicy::ContinueOnDone);
|
|
|
|
|
Workflow optional(WorkflowPolicy::Optional);
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
void TaskItem::addChildren(const QList<TaskItem> &children)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_type == Type::Group, qWarning("Only Task may have children, skipping..."); return);
|
|
|
|
|
for (const TaskItem &child : children) {
|
|
|
|
|
switch (child.m_type) {
|
|
|
|
|
case Type::Group:
|
|
|
|
|
m_children.append(child);
|
|
|
|
|
break;
|
2023-01-06 16:07:16 +01:00
|
|
|
case Type::Limit:
|
2022-10-12 14:30:24 +02:00
|
|
|
QTC_ASSERT(m_type == Type::Group,
|
|
|
|
|
qWarning("Mode may only be a child of Group, skipping..."); return);
|
2023-01-06 16:07:16 +01:00
|
|
|
m_parallelLimit = child.m_parallelLimit; // TODO: Assert on redefinition?
|
2022-10-12 14:30:24 +02:00
|
|
|
break;
|
|
|
|
|
case Type::Policy:
|
|
|
|
|
QTC_ASSERT(m_type == Type::Group,
|
|
|
|
|
qWarning("Workflow Policy may only be a child of Group, skipping..."); return);
|
|
|
|
|
m_workflowPolicy = child.m_workflowPolicy; // TODO: Assert on redefinition?
|
|
|
|
|
break;
|
|
|
|
|
case Type::TaskHandler:
|
|
|
|
|
QTC_ASSERT(child.m_taskHandler.m_createHandler,
|
|
|
|
|
qWarning("Task Create Handler can't be null, skipping..."); return);
|
|
|
|
|
QTC_ASSERT(child.m_taskHandler.m_setupHandler,
|
|
|
|
|
qWarning("Task Setup Handler can't be null, skipping..."); return);
|
|
|
|
|
m_children.append(child);
|
|
|
|
|
break;
|
|
|
|
|
case Type::GroupHandler:
|
|
|
|
|
QTC_ASSERT(m_type == Type::Group, qWarning("Group Handler may only be a "
|
|
|
|
|
"child of Group, skipping..."); break);
|
2022-11-04 14:08:32 +01:00
|
|
|
QTC_ASSERT(!child.m_groupHandler.m_setupHandler
|
|
|
|
|
|| !m_groupHandler.m_setupHandler,
|
2022-10-12 14:30:24 +02:00
|
|
|
qWarning("Group Setup Handler redefinition, overriding..."));
|
2022-11-04 14:08:32 +01:00
|
|
|
QTC_ASSERT(!child.m_groupHandler.m_doneHandler
|
|
|
|
|
|| !m_groupHandler.m_doneHandler,
|
2022-10-12 14:30:24 +02:00
|
|
|
qWarning("Group Done Handler redefinition, overriding..."));
|
2022-11-04 14:08:32 +01:00
|
|
|
QTC_ASSERT(!child.m_groupHandler.m_errorHandler
|
|
|
|
|
|| !m_groupHandler.m_errorHandler,
|
2022-10-12 14:30:24 +02:00
|
|
|
qWarning("Group Error Handler redefinition, overriding..."));
|
2022-11-04 14:08:32 +01:00
|
|
|
QTC_ASSERT(!child.m_groupHandler.m_dynamicSetupHandler
|
|
|
|
|
|| !m_groupHandler.m_dynamicSetupHandler,
|
|
|
|
|
qWarning("Dynamic Setup Handler redefinition, overriding..."));
|
|
|
|
|
if (child.m_groupHandler.m_setupHandler)
|
|
|
|
|
m_groupHandler.m_setupHandler = child.m_groupHandler.m_setupHandler;
|
|
|
|
|
if (child.m_groupHandler.m_doneHandler)
|
|
|
|
|
m_groupHandler.m_doneHandler = child.m_groupHandler.m_doneHandler;
|
|
|
|
|
if (child.m_groupHandler.m_errorHandler)
|
|
|
|
|
m_groupHandler.m_errorHandler = child.m_groupHandler.m_errorHandler;
|
|
|
|
|
if (child.m_groupHandler.m_dynamicSetupHandler)
|
|
|
|
|
m_groupHandler.m_dynamicSetupHandler = child.m_groupHandler.m_dynamicSetupHandler;
|
2022-10-12 14:30:24 +02:00
|
|
|
break;
|
2022-11-21 15:05:39 +01:00
|
|
|
case Type::Storage:
|
|
|
|
|
m_storageList.append(child.m_storageList);
|
|
|
|
|
break;
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Tasking
|
|
|
|
|
|
|
|
|
|
using namespace Tasking;
|
|
|
|
|
|
|
|
|
|
class TaskTreePrivate;
|
|
|
|
|
class TaskNode;
|
|
|
|
|
|
|
|
|
|
class TaskContainer
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
TaskContainer(TaskTreePrivate *taskTreePrivate, TaskContainer *parentContainer,
|
|
|
|
|
const TaskItem &task);
|
|
|
|
|
~TaskContainer();
|
2023-01-19 23:28:56 +01:00
|
|
|
TaskAction start();
|
|
|
|
|
TaskAction startChildren();
|
2022-10-12 14:30:24 +02:00
|
|
|
void stop();
|
|
|
|
|
bool isRunning() const;
|
2022-11-10 14:31:25 +01:00
|
|
|
int taskCount() const;
|
2023-01-19 23:28:56 +01:00
|
|
|
int currentLimit() const;
|
|
|
|
|
TaskAction childDone(bool success);
|
|
|
|
|
void groupDone(bool success);
|
|
|
|
|
void invokeEndHandler(bool success);
|
2022-10-12 14:30:24 +02:00
|
|
|
void resetSuccessBit();
|
|
|
|
|
void updateSuccessBit(bool success);
|
|
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
void createStorages();
|
|
|
|
|
void deleteStorages();
|
|
|
|
|
void activateStorages();
|
|
|
|
|
void deactivateStorages();
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
TaskTreePrivate *m_taskTreePrivate = nullptr;
|
|
|
|
|
TaskContainer *m_parentContainer = nullptr;
|
2023-01-06 16:07:16 +01:00
|
|
|
const int m_parallelLimit = 1;
|
2023-01-20 10:44:21 +01:00
|
|
|
const WorkflowPolicy m_workflowPolicy = WorkflowPolicy::StopOnError;
|
2022-10-12 14:30:24 +02:00
|
|
|
const TaskItem::GroupHandler m_groupHandler;
|
2022-11-21 15:05:39 +01:00
|
|
|
QList<TreeStorageBase> m_storageList;
|
|
|
|
|
QList<int> m_storageIdList;
|
2022-11-10 14:31:25 +01:00
|
|
|
int m_taskCount = 0;
|
2022-10-12 14:30:24 +02:00
|
|
|
QList<TaskNode *> m_children;
|
2023-01-19 23:28:56 +01:00
|
|
|
int m_doneCount = -1;
|
2022-10-12 14:30:24 +02:00
|
|
|
bool m_successBit = true;
|
2023-01-19 23:28:56 +01:00
|
|
|
Guard m_startGuard;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
class StorageActivator
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
StorageActivator(TaskContainer &container)
|
|
|
|
|
: m_container(container)
|
|
|
|
|
{
|
|
|
|
|
m_container.activateStorages();
|
|
|
|
|
}
|
|
|
|
|
~StorageActivator()
|
|
|
|
|
{
|
|
|
|
|
m_container.deactivateStorages();
|
|
|
|
|
}
|
|
|
|
|
private:
|
|
|
|
|
TaskContainer &m_container;
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
class TaskNode : public QObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
TaskNode(TaskTreePrivate *taskTreePrivate, TaskContainer *parentContainer,
|
|
|
|
|
const TaskItem &task)
|
|
|
|
|
: m_taskHandler(task.taskHandler())
|
|
|
|
|
, m_container(taskTreePrivate, parentContainer, task)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
// If returned value != Start, childDone() needs to be called in parent container (in caller)
|
|
|
|
|
TaskAction start();
|
2022-10-12 14:30:24 +02:00
|
|
|
void stop();
|
2022-11-10 14:31:25 +01:00
|
|
|
bool isRunning() const;
|
|
|
|
|
bool isTask() const;
|
|
|
|
|
int taskCount() const;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const TaskItem::TaskHandler m_taskHandler;
|
|
|
|
|
TaskContainer m_container;
|
|
|
|
|
std::unique_ptr<TaskInterface> m_task;
|
2023-01-19 23:28:56 +01:00
|
|
|
Guard m_startGuard;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TaskTreePrivate
|
|
|
|
|
{
|
|
|
|
|
public:
|
2022-11-16 08:07:29 +01:00
|
|
|
TaskTreePrivate(TaskTree *taskTree)
|
|
|
|
|
: q(taskTree) {}
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2022-11-10 15:59:54 +01:00
|
|
|
void start() {
|
2022-11-16 08:07:29 +01:00
|
|
|
QTC_ASSERT(m_root, return);
|
2022-11-10 15:59:54 +01:00
|
|
|
m_progressValue = 0;
|
|
|
|
|
emitStartedAndProgress();
|
2022-12-05 11:20:57 +01:00
|
|
|
// TODO: check storage handlers for not existing storages in tree
|
|
|
|
|
for (auto it = m_storageHandlers.cbegin(); it != m_storageHandlers.cend(); ++it) {
|
|
|
|
|
QTC_ASSERT(m_storages.contains(it.key()), qWarning("The registered storage doesn't "
|
|
|
|
|
"exist in task tree. Its handlers will never be called."));
|
|
|
|
|
}
|
2023-01-19 23:28:56 +01:00
|
|
|
const TaskAction action = m_root->start();
|
|
|
|
|
if (action == TaskAction::StopWithDone)
|
|
|
|
|
emitDone();
|
|
|
|
|
else if (action == TaskAction::StopWithError)
|
|
|
|
|
emitError();
|
2022-11-10 15:59:54 +01:00
|
|
|
}
|
|
|
|
|
void stop() {
|
2022-11-16 08:07:29 +01:00
|
|
|
QTC_ASSERT(m_root, return);
|
|
|
|
|
if (!m_root->isRunning())
|
2022-11-10 15:59:54 +01:00
|
|
|
return;
|
|
|
|
|
// TODO: should we have canceled flag (passed to handler)?
|
|
|
|
|
// Just one done handler with result flag:
|
|
|
|
|
// FinishedWithSuccess, FinishedWithError, Canceled, TimedOut.
|
|
|
|
|
// Canceled either directly by user, or by workflow policy - doesn't matter, in both
|
|
|
|
|
// cases canceled from outside.
|
2022-11-16 08:07:29 +01:00
|
|
|
m_root->stop();
|
2022-11-10 15:59:54 +01:00
|
|
|
emitError();
|
|
|
|
|
}
|
|
|
|
|
void advanceProgress(int byValue) {
|
|
|
|
|
if (byValue == 0)
|
|
|
|
|
return;
|
|
|
|
|
QTC_CHECK(byValue > 0);
|
2022-11-16 08:07:29 +01:00
|
|
|
QTC_CHECK(m_progressValue + byValue <= m_root->taskCount());
|
2022-11-10 15:59:54 +01:00
|
|
|
m_progressValue += byValue;
|
|
|
|
|
emitProgress();
|
|
|
|
|
}
|
|
|
|
|
void emitStartedAndProgress() {
|
|
|
|
|
GuardLocker locker(m_guard);
|
|
|
|
|
emit q->started();
|
|
|
|
|
emit q->progressValueChanged(m_progressValue);
|
|
|
|
|
}
|
|
|
|
|
void emitProgress() {
|
|
|
|
|
GuardLocker locker(m_guard);
|
|
|
|
|
emit q->progressValueChanged(m_progressValue);
|
|
|
|
|
}
|
2022-10-12 14:30:24 +02:00
|
|
|
void emitDone() {
|
2022-11-16 08:07:29 +01:00
|
|
|
QTC_CHECK(m_progressValue == m_root->taskCount());
|
2022-10-12 14:30:24 +02:00
|
|
|
GuardLocker locker(m_guard);
|
|
|
|
|
emit q->done();
|
|
|
|
|
}
|
|
|
|
|
void emitError() {
|
2022-11-16 08:07:29 +01:00
|
|
|
QTC_CHECK(m_progressValue == m_root->taskCount());
|
2022-10-12 14:30:24 +02:00
|
|
|
GuardLocker locker(m_guard);
|
|
|
|
|
emit q->errorOccurred();
|
|
|
|
|
}
|
2022-12-05 10:25:59 +01:00
|
|
|
QList<TreeStorageBase> addStorages(const QList<TreeStorageBase> &storages) {
|
|
|
|
|
QList<TreeStorageBase> addedStorages;
|
|
|
|
|
for (const TreeStorageBase &storage : storages) {
|
|
|
|
|
QTC_ASSERT(!m_storages.contains(storage), qWarning("Can't add the same storage into "
|
|
|
|
|
"one TaskTree twice, skipping..."); continue);
|
|
|
|
|
addedStorages << storage;
|
|
|
|
|
m_storages << storage;
|
|
|
|
|
}
|
|
|
|
|
return addedStorages;
|
|
|
|
|
}
|
2022-12-05 11:20:57 +01:00
|
|
|
void callSetupHandler(TreeStorageBase storage, int storageId) {
|
|
|
|
|
callStorageHandler(storage, storageId, &StorageHandler::m_setupHandler);
|
|
|
|
|
}
|
|
|
|
|
void callDoneHandler(TreeStorageBase storage, int storageId) {
|
|
|
|
|
callStorageHandler(storage, storageId, &StorageHandler::m_doneHandler);
|
|
|
|
|
}
|
|
|
|
|
struct StorageHandler {
|
|
|
|
|
TaskTree::StorageVoidHandler m_setupHandler = {};
|
|
|
|
|
TaskTree::StorageVoidHandler m_doneHandler = {};
|
|
|
|
|
};
|
|
|
|
|
typedef TaskTree::StorageVoidHandler StorageHandler::*HandlerPtr; // ptr to class member
|
|
|
|
|
void callStorageHandler(TreeStorageBase storage, int storageId, HandlerPtr ptr)
|
|
|
|
|
{
|
|
|
|
|
const auto it = m_storageHandlers.constFind(storage);
|
|
|
|
|
if (it == m_storageHandlers.constEnd())
|
|
|
|
|
return;
|
|
|
|
|
GuardLocker locker(m_guard);
|
|
|
|
|
const StorageHandler storageHandler = *it;
|
|
|
|
|
storage.activateStorage(storageId);
|
|
|
|
|
if (storageHandler.*ptr)
|
|
|
|
|
(storageHandler.*ptr)(storage.activeStorageVoid());
|
|
|
|
|
storage.activateStorage(0);
|
|
|
|
|
}
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
TaskTree *q = nullptr;
|
|
|
|
|
Guard m_guard;
|
2022-11-10 15:59:54 +01:00
|
|
|
int m_progressValue = 0;
|
2022-12-05 10:25:59 +01:00
|
|
|
QSet<TreeStorageBase> m_storages;
|
2022-12-05 11:20:57 +01:00
|
|
|
QHash<TreeStorageBase, StorageHandler> m_storageHandlers;
|
|
|
|
|
std::unique_ptr<TaskNode> m_root = nullptr; // Keep me last in order to destruct first
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TaskContainer::TaskContainer(TaskTreePrivate *taskTreePrivate, TaskContainer *parentContainer,
|
|
|
|
|
const TaskItem &task)
|
|
|
|
|
: m_taskTreePrivate(taskTreePrivate)
|
|
|
|
|
, m_parentContainer(parentContainer)
|
2023-01-06 16:07:16 +01:00
|
|
|
, m_parallelLimit(task.parallelLimit())
|
2022-10-12 14:30:24 +02:00
|
|
|
, m_workflowPolicy(task.workflowPolicy())
|
|
|
|
|
, m_groupHandler(task.groupHandler())
|
2022-12-05 10:25:59 +01:00
|
|
|
, m_storageList(taskTreePrivate->addStorages(task.storageList()))
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
const QList<TaskItem> &children = task.children();
|
2022-11-10 14:31:25 +01:00
|
|
|
for (const TaskItem &child : children) {
|
|
|
|
|
TaskNode *node = new TaskNode(m_taskTreePrivate, this, child);
|
|
|
|
|
m_children.append(node);
|
|
|
|
|
m_taskCount += node->taskCount();
|
|
|
|
|
}
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TaskContainer::~TaskContainer()
|
|
|
|
|
{
|
|
|
|
|
qDeleteAll(m_children);
|
2022-11-22 15:21:45 +01:00
|
|
|
deleteStorages();
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
TaskAction TaskContainer::start()
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
2023-01-19 23:28:56 +01:00
|
|
|
m_doneCount = 0;
|
2022-11-04 14:08:32 +01:00
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
createStorages();
|
2023-01-20 10:44:21 +01:00
|
|
|
TaskAction groupAction = TaskAction::Continue;
|
2023-01-19 23:28:56 +01:00
|
|
|
if (m_groupHandler.m_setupHandler || m_groupHandler.m_dynamicSetupHandler) {
|
2022-11-21 15:05:39 +01:00
|
|
|
StorageActivator activator(*this);
|
2023-01-19 23:28:56 +01:00
|
|
|
GuardLocker locker(m_taskTreePrivate->m_guard);
|
|
|
|
|
if (m_groupHandler.m_setupHandler)
|
2022-11-21 15:05:39 +01:00
|
|
|
m_groupHandler.m_setupHandler();
|
2023-01-19 23:28:56 +01:00
|
|
|
if (m_groupHandler.m_dynamicSetupHandler)
|
2023-01-20 10:44:21 +01:00
|
|
|
groupAction = m_groupHandler.m_dynamicSetupHandler();
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 10:44:21 +01:00
|
|
|
if (groupAction == TaskAction::StopWithDone || groupAction == TaskAction::StopWithError) {
|
|
|
|
|
const bool success = groupAction == TaskAction::StopWithDone;
|
2022-11-10 15:59:54 +01:00
|
|
|
const int skippedTaskCount = taskCount();
|
|
|
|
|
m_taskTreePrivate->advanceProgress(skippedTaskCount);
|
2022-11-04 14:08:32 +01:00
|
|
|
invokeEndHandler(success);
|
2023-01-19 23:28:56 +01:00
|
|
|
return toTaskAction(success);
|
2022-11-04 14:08:32 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-20 10:44:21 +01:00
|
|
|
if (m_children.isEmpty()) {
|
2022-11-04 14:08:32 +01:00
|
|
|
invokeEndHandler(true);
|
2023-01-19 23:28:56 +01:00
|
|
|
return TaskAction::StopWithDone; // TODO: take workflow policy into account?
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resetSuccessBit();
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
GuardLocker locker(m_startGuard);
|
|
|
|
|
return startChildren();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TaskAction TaskContainer::startChildren()
|
|
|
|
|
{
|
2023-01-20 10:44:21 +01:00
|
|
|
const int childCount = m_children.size();
|
2023-01-19 23:28:56 +01:00
|
|
|
for (int i = m_doneCount; i < childCount; ++i) {
|
|
|
|
|
const int limit = currentLimit();
|
|
|
|
|
if (i >= limit)
|
|
|
|
|
break;
|
|
|
|
|
|
2023-01-20 10:44:21 +01:00
|
|
|
const TaskAction action = m_children.at(i)->start();
|
2023-01-19 23:28:56 +01:00
|
|
|
if (action == TaskAction::Continue)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const TaskAction finalizeAction = childDone(action == TaskAction::StopWithDone);
|
|
|
|
|
if (finalizeAction == TaskAction::Continue)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
int skippedTaskCount = 0;
|
|
|
|
|
// Skip scheduled but not run yet. The current (i) was already notified.
|
|
|
|
|
for (int j = i + 1; j < limit; ++j)
|
2023-01-20 10:44:21 +01:00
|
|
|
skippedTaskCount += m_children.at(j)->taskCount();
|
2023-01-19 23:28:56 +01:00
|
|
|
m_taskTreePrivate->advanceProgress(skippedTaskCount);
|
|
|
|
|
|
|
|
|
|
return finalizeAction;
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
2023-01-19 23:28:56 +01:00
|
|
|
return TaskAction::Continue;
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskContainer::stop()
|
|
|
|
|
{
|
2022-11-10 15:59:54 +01:00
|
|
|
if (!isRunning())
|
|
|
|
|
return;
|
|
|
|
|
|
2023-01-20 10:44:21 +01:00
|
|
|
const int childCount = m_children.size();
|
2023-01-19 23:28:56 +01:00
|
|
|
const int limit = currentLimit();
|
2022-11-10 15:59:54 +01:00
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
for (int i = m_doneCount; i < limit; ++i)
|
2023-01-20 10:44:21 +01:00
|
|
|
m_children.at(i)->stop();
|
2023-01-19 23:28:56 +01:00
|
|
|
|
|
|
|
|
int skippedTaskCount = 0;
|
|
|
|
|
for (int i = limit; i < childCount; ++i)
|
2023-01-20 10:44:21 +01:00
|
|
|
skippedTaskCount += m_children.at(i)->taskCount();
|
2023-01-19 23:28:56 +01:00
|
|
|
|
|
|
|
|
m_taskTreePrivate->advanceProgress(skippedTaskCount);
|
|
|
|
|
m_doneCount = -1;
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TaskContainer::isRunning() const
|
|
|
|
|
{
|
2023-01-19 23:28:56 +01:00
|
|
|
return m_doneCount >= 0;
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 14:31:25 +01:00
|
|
|
int TaskContainer::taskCount() const
|
|
|
|
|
{
|
|
|
|
|
return m_taskCount;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
int TaskContainer::currentLimit() const
|
|
|
|
|
{
|
2023-01-20 10:44:21 +01:00
|
|
|
const int childCount = m_children.size();
|
2023-01-19 23:28:56 +01:00
|
|
|
return m_parallelLimit ? qMin(m_doneCount + m_parallelLimit, childCount) : childCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TaskAction TaskContainer::childDone(bool success)
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
2022-11-11 10:37:02 +01:00
|
|
|
if ((m_workflowPolicy == WorkflowPolicy::StopOnDone && success)
|
|
|
|
|
|| (m_workflowPolicy == WorkflowPolicy::StopOnError && !success)) {
|
2022-10-12 14:30:24 +02:00
|
|
|
stop();
|
2022-11-04 14:08:32 +01:00
|
|
|
invokeEndHandler(success);
|
2023-01-19 23:28:56 +01:00
|
|
|
groupDone(success);
|
|
|
|
|
return toTaskAction(success);
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
++m_doneCount;
|
2022-10-12 14:30:24 +02:00
|
|
|
updateSuccessBit(success);
|
|
|
|
|
|
2023-01-20 10:44:21 +01:00
|
|
|
if (m_doneCount == m_children.size()) {
|
2022-11-04 14:08:32 +01:00
|
|
|
invokeEndHandler(m_successBit);
|
2023-01-19 23:28:56 +01:00
|
|
|
groupDone(m_successBit);
|
|
|
|
|
return toTaskAction(m_successBit);
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-06 16:07:16 +01:00
|
|
|
if (m_parallelLimit == 0)
|
2023-01-19 23:28:56 +01:00
|
|
|
return TaskAction::Continue;
|
2023-01-06 16:07:16 +01:00
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
if (m_startGuard.isLocked())
|
|
|
|
|
return TaskAction::Continue;
|
|
|
|
|
|
|
|
|
|
return startChildren();
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
void TaskContainer::groupDone(bool success)
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
2023-01-19 23:28:56 +01:00
|
|
|
if (m_startGuard.isLocked())
|
2022-11-10 15:59:54 +01:00
|
|
|
return;
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
if (m_parentContainer) {
|
|
|
|
|
m_parentContainer->childDone(success);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (success)
|
|
|
|
|
m_taskTreePrivate->emitDone();
|
|
|
|
|
else
|
|
|
|
|
m_taskTreePrivate->emitError();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
void TaskContainer::invokeEndHandler(bool success)
|
|
|
|
|
{
|
|
|
|
|
m_doneCount = -1;
|
|
|
|
|
m_successBit = success;
|
|
|
|
|
const bool callDoneHandler = success && m_groupHandler.m_doneHandler;
|
|
|
|
|
const bool callErrorHandler = !success && m_groupHandler.m_errorHandler;
|
|
|
|
|
if (callDoneHandler || callErrorHandler) {
|
|
|
|
|
StorageActivator activator(*this);
|
|
|
|
|
GuardLocker locker(m_taskTreePrivate->m_guard);
|
|
|
|
|
if (callDoneHandler)
|
|
|
|
|
m_groupHandler.m_doneHandler();
|
|
|
|
|
else if (callErrorHandler)
|
|
|
|
|
m_groupHandler.m_errorHandler();
|
|
|
|
|
}
|
|
|
|
|
deleteStorages();
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
void TaskContainer::resetSuccessBit()
|
|
|
|
|
{
|
2023-01-20 10:44:21 +01:00
|
|
|
if (m_children.isEmpty())
|
2022-10-12 14:30:24 +02:00
|
|
|
m_successBit = true;
|
|
|
|
|
|
2022-11-11 10:37:02 +01:00
|
|
|
if (m_workflowPolicy == WorkflowPolicy::StopOnDone
|
|
|
|
|
|| m_workflowPolicy == WorkflowPolicy::ContinueOnDone) {
|
2022-10-12 14:30:24 +02:00
|
|
|
m_successBit = false;
|
|
|
|
|
} else {
|
|
|
|
|
m_successBit = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskContainer::updateSuccessBit(bool success)
|
|
|
|
|
{
|
2022-11-11 10:37:02 +01:00
|
|
|
if (m_workflowPolicy == WorkflowPolicy::Optional)
|
2022-10-12 14:30:24 +02:00
|
|
|
return;
|
2022-11-11 10:37:02 +01:00
|
|
|
if (m_workflowPolicy == WorkflowPolicy::StopOnDone
|
|
|
|
|
|| m_workflowPolicy == WorkflowPolicy::ContinueOnDone) {
|
2022-10-12 14:30:24 +02:00
|
|
|
m_successBit = m_successBit || success;
|
|
|
|
|
} else {
|
|
|
|
|
m_successBit = m_successBit && success;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-21 15:05:39 +01:00
|
|
|
void TaskContainer::createStorages()
|
|
|
|
|
{
|
2022-11-22 15:21:45 +01:00
|
|
|
QTC_CHECK(m_storageIdList.isEmpty());
|
2022-11-21 15:05:39 +01:00
|
|
|
|
2022-12-05 11:20:57 +01:00
|
|
|
for (int i = 0; i < m_storageList.size(); ++i) {
|
|
|
|
|
TreeStorageBase storage = m_storageList[i];
|
|
|
|
|
const int storageId = storage.createStorage();
|
|
|
|
|
m_storageIdList << storageId;
|
|
|
|
|
m_taskTreePrivate->callSetupHandler(storage, storageId);
|
|
|
|
|
}
|
2022-11-21 15:05:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskContainer::deleteStorages()
|
|
|
|
|
{
|
2022-12-05 11:20:57 +01:00
|
|
|
for (int i = 0; i < m_storageIdList.size(); ++i) { // iterate in reverse order?
|
|
|
|
|
TreeStorageBase storage = m_storageList[i];
|
|
|
|
|
const int storageId = m_storageIdList.value(i);
|
|
|
|
|
m_taskTreePrivate->callDoneHandler(storage, storageId);
|
|
|
|
|
storage.deleteStorage(storageId);
|
|
|
|
|
}
|
2022-11-21 15:05:39 +01:00
|
|
|
m_storageIdList.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskContainer::activateStorages()
|
|
|
|
|
{
|
|
|
|
|
if (m_parentContainer)
|
|
|
|
|
m_parentContainer->activateStorages();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_storageList.size(); ++i)
|
|
|
|
|
m_storageList[i].activateStorage(m_storageIdList.value(i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskContainer::deactivateStorages()
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < m_storageList.size(); ++i) // iterate in reverse order?
|
|
|
|
|
m_storageList[i].activateStorage(0);
|
|
|
|
|
|
|
|
|
|
if (m_parentContainer)
|
|
|
|
|
m_parentContainer->deactivateStorages();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
TaskAction TaskNode::start()
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
2023-01-19 23:28:56 +01:00
|
|
|
if (!isTask())
|
|
|
|
|
return m_container.start();
|
|
|
|
|
|
|
|
|
|
const auto finalize = [this] {
|
2023-01-09 20:52:12 +01:00
|
|
|
m_container.m_taskTreePrivate->advanceProgress(1);
|
|
|
|
|
m_task.release()->deleteLater();
|
|
|
|
|
};
|
2022-10-12 14:30:24 +02:00
|
|
|
m_task.reset(m_taskHandler.m_createHandler());
|
|
|
|
|
{
|
2023-01-09 20:52:12 +01:00
|
|
|
TaskAction action = TaskAction::Continue;
|
|
|
|
|
{
|
|
|
|
|
StorageActivator activator(m_container);
|
|
|
|
|
GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
|
|
|
|
|
action = m_taskHandler.m_setupHandler(*m_task.get());
|
|
|
|
|
}
|
|
|
|
|
if (action != TaskAction::Continue) {
|
2023-01-19 23:28:56 +01:00
|
|
|
finalize();
|
|
|
|
|
return action;
|
2023-01-09 20:52:12 +01:00
|
|
|
}
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
2023-01-19 23:28:56 +01:00
|
|
|
const std::shared_ptr<TaskAction> unwindAction = std::make_shared<TaskAction>(TaskAction::Continue);
|
2023-01-09 20:52:12 +01:00
|
|
|
connect(m_task.get(), &TaskInterface::done, this, [=](bool success) {
|
2023-01-19 23:28:56 +01:00
|
|
|
const bool callDoneHandler = success && m_taskHandler.m_doneHandler;
|
|
|
|
|
const bool callErrorHandler = !success && m_taskHandler.m_errorHandler;
|
|
|
|
|
if (callDoneHandler || callErrorHandler) {
|
2022-11-21 15:05:39 +01:00
|
|
|
StorageActivator activator(m_container);
|
2023-01-19 23:28:56 +01:00
|
|
|
GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
|
|
|
|
|
if (callDoneHandler)
|
2022-11-21 15:05:39 +01:00
|
|
|
m_taskHandler.m_doneHandler(*m_task.get());
|
2023-01-19 23:28:56 +01:00
|
|
|
else if (callErrorHandler)
|
2022-11-21 15:05:39 +01:00
|
|
|
m_taskHandler.m_errorHandler(*m_task.get());
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
2023-01-19 23:28:56 +01:00
|
|
|
finalize();
|
|
|
|
|
if (m_startGuard.isLocked())
|
|
|
|
|
*unwindAction = toTaskAction(success);
|
|
|
|
|
else
|
|
|
|
|
m_container.m_parentContainer->childDone(success);
|
2022-10-12 14:30:24 +02:00
|
|
|
});
|
|
|
|
|
|
2023-01-19 23:28:56 +01:00
|
|
|
GuardLocker locker(m_startGuard);
|
2022-10-12 14:30:24 +02:00
|
|
|
m_task->start();
|
2023-01-19 23:28:56 +01:00
|
|
|
return *unwindAction;
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskNode::stop()
|
|
|
|
|
{
|
2022-11-10 15:59:54 +01:00
|
|
|
if (!isRunning())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!m_task) {
|
|
|
|
|
m_container.stop();
|
2023-01-19 23:28:56 +01:00
|
|
|
m_container.invokeEndHandler(false);
|
2022-11-10 15:59:54 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: cancelHandler?
|
|
|
|
|
// TODO: call TaskInterface::stop() ?
|
|
|
|
|
if (m_taskHandler.m_errorHandler) {
|
2022-11-21 15:05:39 +01:00
|
|
|
StorageActivator activator(m_container);
|
2022-11-10 15:59:54 +01:00
|
|
|
GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
|
|
|
|
|
m_taskHandler.m_errorHandler(*m_task.get());
|
|
|
|
|
}
|
|
|
|
|
m_container.m_taskTreePrivate->advanceProgress(1);
|
2022-10-12 14:30:24 +02:00
|
|
|
m_task.reset();
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-10 14:31:25 +01:00
|
|
|
bool TaskNode::isRunning() const
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
return m_task || m_container.isRunning();
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-10 14:31:25 +01:00
|
|
|
bool TaskNode::isTask() const
|
|
|
|
|
{
|
|
|
|
|
return m_taskHandler.m_createHandler && m_taskHandler.m_setupHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TaskNode::taskCount() const
|
|
|
|
|
{
|
|
|
|
|
return isTask() ? 1 : m_container.taskCount();
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
/*!
|
|
|
|
|
\class Utils::TaskTree
|
|
|
|
|
|
|
|
|
|
\brief The TaskTree class is responsible for running async task tree structure defined in a
|
|
|
|
|
declarative way.
|
|
|
|
|
|
|
|
|
|
The Tasking namespace (similar to Layouting) is designer for building declarative task
|
|
|
|
|
tree structure. The examples of tasks that can be used inside TaskTree are e.g. QtcProcess,
|
|
|
|
|
FileTransfer, AsyncTask<>. It's extensible, so any possible asynchronous task may be
|
|
|
|
|
integrated and used inside TaskTree. TaskTree enables to form sophisticated mixtures of
|
|
|
|
|
parallel or sequential flow of tasks in tree form.
|
|
|
|
|
|
|
|
|
|
The TaskTree consist of Group root element. The Group can have nested Group elements.
|
|
|
|
|
The Group may also contain any number of tasks, e.g. Process, FileTransfer,
|
|
|
|
|
AsyncTask<ReturnType>.
|
|
|
|
|
|
|
|
|
|
Each Group can contain various other elements describing the processing flow.
|
|
|
|
|
|
|
|
|
|
The execute mode elements of a Group specify how direct children of a Group will be executed.
|
|
|
|
|
The "sequential" element of a Group means all tasks in a group will be executed in chain,
|
|
|
|
|
so after the previous task finished, the next will be started. This is the default Group
|
|
|
|
|
behavior. The "parallel" element of a Group means that all tasks in a Group will be started
|
|
|
|
|
simultaneously. When having nested Groups hierarchy, we may mix execute modes freely
|
|
|
|
|
and each Group will be executed according to its own execute mode.
|
|
|
|
|
The "sequential" mode may be very useful in cases when result data from one task is need to
|
|
|
|
|
be passed as an input data to the other task - sequential mode guarantees that the next
|
|
|
|
|
task will be started only after the previous task has already finished.
|
|
|
|
|
|
|
|
|
|
There are many possible "workflow" behaviors for the Group. E.g. "stopOnError",
|
|
|
|
|
the default Group workflow behavior, means that whenever any direct child of a Group
|
|
|
|
|
finished with error, we immediately stop processing other tasks in this group
|
|
|
|
|
(in parallel case) by canceling them and immediately finish the Group with error.
|
|
|
|
|
|
|
|
|
|
The user of TaskTree specifies how to setup his tasks (by providing TaskSetupHandlers)
|
|
|
|
|
and how to collect output data from the finished tasks (by providing TaskEndHandlers).
|
|
|
|
|
The user don't need to create tasks manually - TaskTree will create them when it's needed
|
|
|
|
|
and destroy when they are not used anymore.
|
|
|
|
|
|
|
|
|
|
Whenever a Group elemenent is being started, the Group's OnGroupSetup handler is being called.
|
|
|
|
|
Just after the handler finishes, all Group's children are executed (either in parallel or
|
|
|
|
|
in sequence). When all Group's children finished, one of Group's OnGroupDone or OnGroupError
|
|
|
|
|
is being executed, depending on results of children execution and Group's workflow policy.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-11-16 08:07:29 +01:00
|
|
|
TaskTree::TaskTree()
|
|
|
|
|
: d(new TaskTreePrivate(this))
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-16 08:07:29 +01:00
|
|
|
TaskTree::TaskTree(const Group &root) : TaskTree()
|
|
|
|
|
{
|
|
|
|
|
setupRoot(root);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
TaskTree::~TaskTree()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!d->m_guard.isLocked(), qWarning("Deleting TaskTree instance directly from "
|
|
|
|
|
"one of its handlers will lead to crash!"));
|
|
|
|
|
delete d;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-16 08:07:29 +01:00
|
|
|
void TaskTree::setupRoot(const Tasking::Group &root)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return);
|
|
|
|
|
QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The setupRoot() is called from one of the"
|
|
|
|
|
"TaskTree handlers, ingoring..."); return);
|
|
|
|
|
d->m_root.reset(new TaskNode(d, nullptr, root));
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
void TaskTree::start()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return);
|
|
|
|
|
QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The start() is called from one of the"
|
|
|
|
|
"TaskTree handlers, ingoring..."); return);
|
2022-11-10 15:59:54 +01:00
|
|
|
d->start();
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskTree::stop()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The stop() is called from one of the"
|
|
|
|
|
"TaskTree handlers, ingoring..."); return);
|
2022-11-10 15:59:54 +01:00
|
|
|
d->stop();
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TaskTree::isRunning() const
|
|
|
|
|
{
|
2022-11-16 08:07:29 +01:00
|
|
|
return d->m_root && d->m_root->isRunning();
|
2022-10-12 14:30:24 +02:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 14:31:25 +01:00
|
|
|
int TaskTree::taskCount() const
|
|
|
|
|
{
|
2022-11-16 08:07:29 +01:00
|
|
|
return d->m_root ? d->m_root->taskCount() : 0;
|
2022-11-10 14:31:25 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-10 15:59:54 +01:00
|
|
|
int TaskTree::progressValue() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_progressValue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-05 11:20:57 +01:00
|
|
|
void TaskTree::setupStorageHandler(const Tasking::TreeStorageBase &storage,
|
|
|
|
|
StorageVoidHandler setupHandler,
|
|
|
|
|
StorageVoidHandler doneHandler)
|
|
|
|
|
{
|
|
|
|
|
auto it = d->m_storageHandlers.find(storage);
|
|
|
|
|
if (it == d->m_storageHandlers.end()) {
|
|
|
|
|
d->m_storageHandlers.insert(storage, {setupHandler, doneHandler});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (setupHandler) {
|
|
|
|
|
QTC_ASSERT(!it->m_setupHandler,
|
|
|
|
|
qWarning("The storage has its setup handler defined, overriding..."));
|
|
|
|
|
it->m_setupHandler = setupHandler;
|
|
|
|
|
}
|
|
|
|
|
if (doneHandler) {
|
|
|
|
|
QTC_ASSERT(!it->m_doneHandler,
|
|
|
|
|
qWarning("The storage has its done handler defined, overriding..."));
|
|
|
|
|
it->m_doneHandler = doneHandler;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-16 09:06:32 +01:00
|
|
|
TaskTreeAdapter::TaskTreeAdapter()
|
|
|
|
|
{
|
|
|
|
|
connect(task(), &TaskTree::done, this, [this] { emit done(true); });
|
|
|
|
|
connect(task(), &TaskTree::errorOccurred, this, [this] { emit done(false); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskTreeAdapter::start()
|
|
|
|
|
{
|
|
|
|
|
task()->start();
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
} // namespace Utils
|