forked from qt-creator/qt-creator
TaskTree: Split completely const and runtime trees
Separate completely const tree (data taken from recipe) from runtime tree. Build runtime tree incrementally, on demand. The const tree is created on TaskTree::setRecipe() and the runtime tree is created on TaskTree::start(). The const tree serves as a kind of template for creating the runtime task tree later, dynamically. Prepare for the implementation of the loop functionality (3rd point on the master task below). Task-number: QTCREATORBUG-28741 Change-Id: I80382e5ef43d53b36ca3c70472b193c5949f5ab9 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "tasktree.h"
|
#include "tasktree.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
@@ -1053,6 +1054,22 @@ GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout,
|
|||||||
|
|
||||||
class TaskTreePrivate;
|
class TaskTreePrivate;
|
||||||
class TaskNode;
|
class TaskNode;
|
||||||
|
class TaskRuntimeNode;
|
||||||
|
class TaskRuntimeContainer;
|
||||||
|
|
||||||
|
class ExecutionContextActivator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExecutionContextActivator(TaskRuntimeContainer *container) { activateContext(container); }
|
||||||
|
~ExecutionContextActivator() {
|
||||||
|
for (int i = m_activeStorages.size() - 1; i >= 0; --i) // iterate in reverse order
|
||||||
|
m_activeStorages[i].m_storageData->threadData().activateStorage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void activateContext(TaskRuntimeContainer *container);
|
||||||
|
QList<TreeStorageBase> m_activeStorages;
|
||||||
|
};
|
||||||
|
|
||||||
class TaskTreePrivate
|
class TaskTreePrivate
|
||||||
{
|
{
|
||||||
@@ -1075,10 +1092,10 @@ public:
|
|||||||
callStorageHandler(storage, storageId, &StorageHandler::m_doneHandler);
|
callStorageHandler(storage, storageId, &StorageHandler::m_doneHandler);
|
||||||
}
|
}
|
||||||
struct StorageHandler {
|
struct StorageHandler {
|
||||||
TaskTree::StorageVoidHandler m_setupHandler = {};
|
TreeStorageBase::StorageVoidHandler m_setupHandler = {};
|
||||||
TaskTree::StorageVoidHandler m_doneHandler = {};
|
TreeStorageBase::StorageVoidHandler m_doneHandler = {};
|
||||||
};
|
};
|
||||||
typedef TaskTree::StorageVoidHandler StorageHandler::*HandlerPtr; // ptr to class member
|
typedef TreeStorageBase::StorageVoidHandler StorageHandler::*HandlerPtr; // ptr to class member
|
||||||
void callStorageHandler(TreeStorageBase storage, int storageId, HandlerPtr ptr)
|
void callStorageHandler(TreeStorageBase storage, int storageId, HandlerPtr ptr)
|
||||||
{
|
{
|
||||||
const auto it = m_storageHandlers.constFind(storage);
|
const auto it = m_storageHandlers.constFind(storage);
|
||||||
@@ -1095,98 +1112,176 @@ public:
|
|||||||
threadData.activateStorage(0);
|
threadData.activateStorage(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Node related methods
|
||||||
|
|
||||||
|
// If returned value != Continue, childDone() needs to be called in parent container (in caller)
|
||||||
|
// in order to unwind properly.
|
||||||
|
SetupResult start(TaskRuntimeNode *node);
|
||||||
|
void stop(TaskRuntimeNode *node);
|
||||||
|
bool invokeDoneHandler(TaskRuntimeNode *node, DoneWith doneWith);
|
||||||
|
|
||||||
|
// Container related methods
|
||||||
|
|
||||||
|
SetupResult start(TaskRuntimeContainer *container);
|
||||||
|
SetupResult continueStart(TaskRuntimeContainer *container, SetupResult startAction, int nextChild);
|
||||||
|
SetupResult startChildren(TaskRuntimeContainer *container, int nextChild);
|
||||||
|
SetupResult childDone(TaskRuntimeContainer *container, bool success);
|
||||||
|
void stop(TaskRuntimeContainer *container);
|
||||||
|
bool invokeDoneHandler(TaskRuntimeContainer *container, DoneWith doneWith);
|
||||||
|
|
||||||
|
template <typename Handler, typename ...Args,
|
||||||
|
typename ReturnType = typename std::invoke_result_t<Handler, Args...>>
|
||||||
|
ReturnType invokeHandler(TaskRuntimeContainer *container, Handler &&handler, Args &&...args)
|
||||||
|
{
|
||||||
|
ExecutionContextActivator activator(container);
|
||||||
|
GuardLocker locker(m_guard);
|
||||||
|
return std::invoke(std::forward<Handler>(handler), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
TaskTree *q = nullptr;
|
TaskTree *q = nullptr;
|
||||||
Guard m_guard;
|
Guard m_guard;
|
||||||
int m_progressValue = 0;
|
int m_progressValue = 0;
|
||||||
QSet<TreeStorageBase> m_storages;
|
QSet<TreeStorageBase> m_storages;
|
||||||
QHash<TreeStorageBase, StorageHandler> m_storageHandlers;
|
QHash<TreeStorageBase, StorageHandler> m_storageHandlers;
|
||||||
std::unique_ptr<TaskNode> m_root = nullptr; // Keep me last in order to destruct first
|
std::unique_ptr<TaskNode> m_root = nullptr; // Keep me last in order to destruct first
|
||||||
|
std::unique_ptr<TaskRuntimeNode> m_runtimeRoot = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TaskContainer
|
class TaskContainer
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY_MOVE(TaskContainer)
|
Q_DISABLE_COPY(TaskContainer)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
TaskContainer(TaskContainer &&other) = default;
|
||||||
TaskContainer(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
TaskContainer(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
||||||
TaskNode *parentNode, TaskContainer *parentContainer)
|
TaskNode *parentNode, TaskContainer *parentContainer);
|
||||||
: m_constData(taskTreePrivate, task, parentNode, parentContainer, this) {}
|
|
||||||
SetupResult start();
|
|
||||||
SetupResult continueStart(SetupResult startAction, int nextChild);
|
|
||||||
SetupResult startChildren(int nextChild);
|
|
||||||
SetupResult childDone(bool success);
|
|
||||||
void stop();
|
|
||||||
bool invokeDoneHandler(DoneWith doneWith);
|
|
||||||
bool isRunning() const { return m_runtimeData.has_value(); }
|
|
||||||
bool isStarting() const { return isRunning() && m_runtimeData->m_startGuard.isLocked(); }
|
|
||||||
|
|
||||||
struct ConstData {
|
TaskTreePrivate *const m_taskTreePrivate = nullptr;
|
||||||
ConstData(TaskTreePrivate *taskTreePrivate, const GroupItem &task, TaskNode *parentNode,
|
TaskNode *const m_parentNode = nullptr;
|
||||||
TaskContainer *parentContainer, TaskContainer *thisContainer);
|
TaskContainer *const m_parentContainer = nullptr;
|
||||||
~ConstData() { qDeleteAll(m_children); }
|
|
||||||
TaskTreePrivate * const m_taskTreePrivate = nullptr;
|
|
||||||
TaskNode * const m_parentNode = nullptr;
|
|
||||||
TaskContainer * const m_parentContainer = nullptr;
|
|
||||||
|
|
||||||
const int m_parallelLimit = 1;
|
const int m_parallelLimit = 1;
|
||||||
const WorkflowPolicy m_workflowPolicy = WorkflowPolicy::StopOnError;
|
const WorkflowPolicy m_workflowPolicy = WorkflowPolicy::StopOnError;
|
||||||
const GroupItem::GroupHandler m_groupHandler;
|
const GroupItem::GroupHandler m_groupHandler;
|
||||||
const QList<TreeStorageBase> m_storageList;
|
const QList<TreeStorageBase> m_storageList;
|
||||||
const QList<TaskNode *> m_children;
|
std::vector<TaskNode> m_children;
|
||||||
const int m_taskCount = 0;
|
const int m_taskCount = 0;
|
||||||
};
|
|
||||||
|
|
||||||
struct RuntimeData {
|
|
||||||
RuntimeData(const ConstData &constData);
|
|
||||||
~RuntimeData();
|
|
||||||
|
|
||||||
static QList<int> createStorages(const TaskContainer::ConstData &constData);
|
|
||||||
bool updateSuccessBit(bool success);
|
|
||||||
int currentLimit() const;
|
|
||||||
|
|
||||||
const ConstData &m_constData;
|
|
||||||
const QList<int> m_storageIdList;
|
|
||||||
bool m_successBit = true;
|
|
||||||
bool m_callStorageDoneHandlersOnDestruction = false;
|
|
||||||
int m_doneCount = 0;
|
|
||||||
Guard m_startGuard;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ConstData m_constData;
|
|
||||||
std::optional<RuntimeData> m_runtimeData;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TaskNode
|
class TaskNode
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY_MOVE(TaskNode)
|
Q_DISABLE_COPY(TaskNode)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
TaskNode(TaskNode &&other) = default;
|
||||||
TaskNode(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
TaskNode(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
||||||
TaskContainer *parentContainer)
|
TaskContainer *parentContainer)
|
||||||
: m_taskHandler(task.m_taskHandler)
|
: m_taskHandler(task.m_taskHandler)
|
||||||
, m_container(taskTreePrivate, task, this, parentContainer)
|
, m_container(taskTreePrivate, task, this, parentContainer)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// If returned value != Continue, childDone() needs to be called in parent container (in caller)
|
|
||||||
// in order to unwind properly.
|
|
||||||
SetupResult start();
|
|
||||||
void stop();
|
|
||||||
bool invokeDoneHandler(DoneWith doneWith);
|
|
||||||
bool isRunning() const { return m_task || m_container.isRunning(); }
|
|
||||||
bool isTask() const { return bool(m_taskHandler.m_createHandler); }
|
bool isTask() const { return bool(m_taskHandler.m_createHandler); }
|
||||||
int taskCount() const { return isTask() ? 1 : m_container.m_constData.m_taskCount; }
|
int taskCount() const { return isTask() ? 1 : m_container.m_taskCount; }
|
||||||
TaskContainer *parentContainer() const { return m_container.m_constData.m_parentContainer; }
|
TaskContainer *parentContainer() const { return m_container.m_parentContainer; }
|
||||||
TaskTree *taskTree() const { return m_container.m_constData.m_taskTreePrivate->q; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const GroupItem::TaskHandler m_taskHandler;
|
const GroupItem::TaskHandler m_taskHandler;
|
||||||
TaskContainer m_container;
|
TaskContainer m_container;
|
||||||
std::unique_ptr<TaskInterface> m_task;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool initialSuccessBit(WorkflowPolicy workflowPolicy)
|
||||||
|
{
|
||||||
|
switch (workflowPolicy) {
|
||||||
|
case WorkflowPolicy::StopOnError:
|
||||||
|
case WorkflowPolicy::ContinueOnError:
|
||||||
|
case WorkflowPolicy::FinishAllAndSuccess:
|
||||||
|
return true;
|
||||||
|
case WorkflowPolicy::StopOnSuccess:
|
||||||
|
case WorkflowPolicy::ContinueOnSuccess:
|
||||||
|
case WorkflowPolicy::StopOnSuccessOrError:
|
||||||
|
case WorkflowPolicy::FinishAllAndError:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QT_CHECK(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaskRuntimeContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TaskRuntimeContainer(const TaskContainer &taskContainer, TaskRuntimeNode *parentNode,
|
||||||
|
TaskRuntimeContainer *parentContainer)
|
||||||
|
: m_taskContainer(taskContainer)
|
||||||
|
, m_parentNode(parentNode)
|
||||||
|
, m_parentContainer(parentContainer)
|
||||||
|
, m_storageIdList(createStorages(taskContainer))
|
||||||
|
, m_successBit(initialSuccessBit(taskContainer.m_workflowPolicy))
|
||||||
|
{}
|
||||||
|
|
||||||
|
~TaskRuntimeContainer()
|
||||||
|
{
|
||||||
|
for (int i = m_taskContainer.m_storageList.size() - 1; i >= 0; --i) { // iterate in reverse order
|
||||||
|
const TreeStorageBase storage = m_taskContainer.m_storageList[i];
|
||||||
|
const int storageId = m_storageIdList.value(i);
|
||||||
|
if (m_callStorageDoneHandlersOnDestruction)
|
||||||
|
m_taskContainer.m_taskTreePrivate->callDoneHandler(storage, storageId);
|
||||||
|
storage.m_storageData->deleteStorage(storageId);
|
||||||
|
}
|
||||||
|
qDeleteAll(m_children);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static QList<int> createStorages(const TaskContainer &container);
|
||||||
|
bool isStarting() const { return m_startGuard.isLocked(); }
|
||||||
|
bool updateSuccessBit(bool success);
|
||||||
|
int currentLimit() const;
|
||||||
|
|
||||||
|
const TaskContainer &m_taskContainer; // Not owning.
|
||||||
|
TaskRuntimeNode *m_parentNode = nullptr; // Not owning.
|
||||||
|
TaskRuntimeContainer *m_parentContainer = nullptr; // Not owning.
|
||||||
|
const QList<int> m_storageIdList;
|
||||||
|
|
||||||
|
QList<TaskRuntimeNode *> m_children; // Owning.
|
||||||
|
bool m_successBit = true;
|
||||||
|
bool m_callStorageDoneHandlersOnDestruction = false;
|
||||||
|
int m_doneCount = 0;
|
||||||
|
Guard m_startGuard;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TaskRuntimeNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TaskRuntimeNode(const TaskNode &taskNode, TaskRuntimeContainer *parentContainer)
|
||||||
|
: m_taskNode(taskNode)
|
||||||
|
, m_parentContainer(parentContainer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const TaskNode &m_taskNode; // Not owning.
|
||||||
|
TaskRuntimeContainer *m_parentContainer = nullptr; // Not owning.
|
||||||
|
std::unique_ptr<TaskRuntimeContainer> m_container; // Owning.
|
||||||
|
std::unique_ptr<TaskInterface> m_task; // Owning.
|
||||||
|
};
|
||||||
|
|
||||||
|
void ExecutionContextActivator::activateContext(TaskRuntimeContainer *container)
|
||||||
|
{
|
||||||
|
const TaskContainer &taskContainer = container->m_taskContainer;
|
||||||
|
for (int i = 0; i < taskContainer.m_storageList.size(); ++i) {
|
||||||
|
const TreeStorageBase &storage = taskContainer.m_storageList[i];
|
||||||
|
auto &threadData = storage.m_storageData->threadData();
|
||||||
|
if (threadData.activeStorageId())
|
||||||
|
continue; // Storage shadowing: The storage is already active, skipping it...
|
||||||
|
m_activeStorages.append(storage);
|
||||||
|
threadData.activateStorage(container->m_storageIdList.value(i));
|
||||||
|
}
|
||||||
|
// Go to the parent after activating this storages so that storage shadowing works
|
||||||
|
// in the direction from child to parent root.
|
||||||
|
if (container->m_parentContainer)
|
||||||
|
activateContext(container->m_parentContainer);
|
||||||
|
}
|
||||||
|
|
||||||
void TaskTreePrivate::start()
|
void TaskTreePrivate::start()
|
||||||
{
|
{
|
||||||
QT_ASSERT(m_root, return);
|
QT_ASSERT(m_root, return);
|
||||||
|
QT_ASSERT(!m_runtimeRoot, return);
|
||||||
m_progressValue = 0;
|
m_progressValue = 0;
|
||||||
emitStartedAndProgress();
|
emitStartedAndProgress();
|
||||||
// TODO: check storage handlers for not existing storages in tree
|
// TODO: check storage handlers for not existing storages in tree
|
||||||
@@ -1194,15 +1289,16 @@ 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_root->start();
|
m_runtimeRoot.reset(new TaskRuntimeNode(*m_root.get(), nullptr));
|
||||||
|
start(m_runtimeRoot.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskTreePrivate::stop()
|
void TaskTreePrivate::stop()
|
||||||
{
|
{
|
||||||
QT_ASSERT(m_root, return);
|
QT_ASSERT(m_root, return);
|
||||||
if (!m_root->isRunning())
|
if (!m_runtimeRoot)
|
||||||
return;
|
return;
|
||||||
m_root->stop();
|
stop(m_runtimeRoot.get());
|
||||||
emitDone(DoneWith::Cancel);
|
emitDone(DoneWith::Cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1236,57 +1332,18 @@ void TaskTreePrivate::emitDone(DoneWith result)
|
|||||||
emit q->done(result);
|
emit q->done(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExecutionContextActivator
|
static std::vector<TaskNode> createChildren(TaskTreePrivate *taskTreePrivate, TaskContainer *container,
|
||||||
|
const QList<GroupItem> &children)
|
||||||
{
|
{
|
||||||
public:
|
std::vector<TaskNode> result;
|
||||||
ExecutionContextActivator(TaskContainer *container) { activateContext(container); }
|
result.reserve(children.size());
|
||||||
~ExecutionContextActivator() {
|
|
||||||
for (int i = m_activeStorages.size() - 1; i >= 0; --i) // iterate in reverse order
|
|
||||||
m_activeStorages[i].m_storageData->threadData().activateStorage(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void activateContext(TaskContainer *container)
|
|
||||||
{
|
|
||||||
QT_ASSERT(container && container->isRunning(), return);
|
|
||||||
const TaskContainer::ConstData &constData = container->m_constData;
|
|
||||||
for (int i = 0; i < constData.m_storageList.size(); ++i) {
|
|
||||||
const TreeStorageBase &storage = constData.m_storageList[i];
|
|
||||||
auto &threadData = storage.m_storageData->threadData();
|
|
||||||
if (threadData.activeStorageId())
|
|
||||||
continue; // Storage shadowing: The storage is already active, skipping it...
|
|
||||||
m_activeStorages.append(storage);
|
|
||||||
threadData.activateStorage(container->m_runtimeData->m_storageIdList.value(i));
|
|
||||||
}
|
|
||||||
// Go to the parent after activating this storages so that storage shadowing works
|
|
||||||
// in the direction from child to parent root.
|
|
||||||
if (constData.m_parentContainer)
|
|
||||||
activateContext(constData.m_parentContainer);
|
|
||||||
}
|
|
||||||
QList<TreeStorageBase> m_activeStorages;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Handler, typename ...Args,
|
|
||||||
typename ReturnType = typename std::invoke_result_t<Handler, Args...>>
|
|
||||||
ReturnType invokeHandler(TaskContainer *container, Handler &&handler, Args &&...args)
|
|
||||||
{
|
|
||||||
ExecutionContextActivator activator(container);
|
|
||||||
GuardLocker locker(container->m_constData.m_taskTreePrivate->m_guard);
|
|
||||||
return std::invoke(std::forward<Handler>(handler), std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
static QList<TaskNode *> createChildren(TaskTreePrivate *taskTreePrivate, TaskContainer *container,
|
|
||||||
const QList<GroupItem> &children)
|
|
||||||
{
|
|
||||||
QList<TaskNode *> result;
|
|
||||||
for (const GroupItem &child : children)
|
for (const GroupItem &child : children)
|
||||||
result.append(new TaskNode(taskTreePrivate, child, container));
|
result.emplace_back(taskTreePrivate, child, container);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
TaskContainer::TaskContainer(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
||||||
TaskNode *parentNode, TaskContainer *parentContainer,
|
TaskNode *parentNode, TaskContainer *parentContainer)
|
||||||
TaskContainer *thisContainer)
|
|
||||||
: m_taskTreePrivate(taskTreePrivate)
|
: m_taskTreePrivate(taskTreePrivate)
|
||||||
, m_parentNode(parentNode)
|
, m_parentNode(parentNode)
|
||||||
, m_parentContainer(parentContainer)
|
, m_parentContainer(parentContainer)
|
||||||
@@ -1294,184 +1351,157 @@ TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const Grou
|
|||||||
, m_workflowPolicy(task.m_groupData.m_workflowPolicy.value_or(WorkflowPolicy::StopOnError))
|
, m_workflowPolicy(task.m_groupData.m_workflowPolicy.value_or(WorkflowPolicy::StopOnError))
|
||||||
, m_groupHandler(task.m_groupData.m_groupHandler)
|
, m_groupHandler(task.m_groupData.m_groupHandler)
|
||||||
, m_storageList(task.m_storageList)
|
, m_storageList(task.m_storageList)
|
||||||
, m_children(createChildren(taskTreePrivate, thisContainer, task.m_children))
|
, m_children(createChildren(taskTreePrivate, this, task.m_children))
|
||||||
, m_taskCount(std::accumulate(m_children.cbegin(), m_children.cend(), 0,
|
, m_taskCount(std::accumulate(m_children.cbegin(), m_children.cend(), 0,
|
||||||
[](int r, TaskNode *n) { return r + n->taskCount(); }))
|
[](int r, const TaskNode &n) { return r + n.taskCount(); }))
|
||||||
{
|
{
|
||||||
for (const TreeStorageBase &storage : m_storageList)
|
for (const TreeStorageBase &storage : m_storageList)
|
||||||
m_taskTreePrivate->m_storages << storage;
|
m_taskTreePrivate->m_storages << storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<int> TaskContainer::RuntimeData::createStorages(const TaskContainer::ConstData &constData)
|
QList<int> TaskRuntimeContainer::createStorages(const TaskContainer &container)
|
||||||
{
|
{
|
||||||
QList<int> storageIdList;
|
QList<int> storageIdList;
|
||||||
for (const TreeStorageBase &storage : constData.m_storageList) {
|
for (const TreeStorageBase &storage : container.m_storageList) {
|
||||||
const int storageId = storage.m_storageData->threadData().createStorage();
|
const int storageId = storage.m_storageData->threadData().createStorage();
|
||||||
storageIdList.append(storageId);
|
storageIdList.append(storageId);
|
||||||
constData.m_taskTreePrivate->callSetupHandler(storage, storageId);
|
container.m_taskTreePrivate->callSetupHandler(storage, storageId);
|
||||||
}
|
}
|
||||||
return storageIdList;
|
return storageIdList;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool initialSuccessBit(WorkflowPolicy workflowPolicy)
|
bool TaskRuntimeContainer::updateSuccessBit(bool success)
|
||||||
{
|
{
|
||||||
switch (workflowPolicy) {
|
if (m_taskContainer.m_workflowPolicy == WorkflowPolicy::FinishAllAndSuccess
|
||||||
case WorkflowPolicy::StopOnError:
|
|| m_taskContainer.m_workflowPolicy == WorkflowPolicy::FinishAllAndError
|
||||||
case WorkflowPolicy::ContinueOnError:
|
|| m_taskContainer.m_workflowPolicy == WorkflowPolicy::StopOnSuccessOrError) {
|
||||||
case WorkflowPolicy::FinishAllAndSuccess:
|
if (m_taskContainer.m_workflowPolicy == WorkflowPolicy::StopOnSuccessOrError)
|
||||||
return true;
|
|
||||||
case WorkflowPolicy::StopOnSuccess:
|
|
||||||
case WorkflowPolicy::ContinueOnSuccess:
|
|
||||||
case WorkflowPolicy::StopOnSuccessOrError:
|
|
||||||
case WorkflowPolicy::FinishAllAndError:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
QT_CHECK(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskContainer::RuntimeData::RuntimeData(const ConstData &constData)
|
|
||||||
: m_constData(constData)
|
|
||||||
, m_storageIdList(createStorages(constData))
|
|
||||||
, m_successBit(initialSuccessBit(m_constData.m_workflowPolicy))
|
|
||||||
{}
|
|
||||||
|
|
||||||
TaskContainer::RuntimeData::~RuntimeData()
|
|
||||||
{
|
|
||||||
for (int i = m_constData.m_storageList.size() - 1; i >= 0; --i) { // iterate in reverse order
|
|
||||||
const TreeStorageBase storage = m_constData.m_storageList[i];
|
|
||||||
const int storageId = m_storageIdList.value(i);
|
|
||||||
if (m_callStorageDoneHandlersOnDestruction)
|
|
||||||
m_constData.m_taskTreePrivate->callDoneHandler(storage, storageId);
|
|
||||||
storage.m_storageData->deleteStorage(storageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TaskContainer::RuntimeData::updateSuccessBit(bool success)
|
|
||||||
{
|
|
||||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::FinishAllAndSuccess
|
|
||||||
|| m_constData.m_workflowPolicy == WorkflowPolicy::FinishAllAndError
|
|
||||||
|| m_constData.m_workflowPolicy == WorkflowPolicy::StopOnSuccessOrError) {
|
|
||||||
if (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnSuccessOrError)
|
|
||||||
m_successBit = success;
|
m_successBit = success;
|
||||||
return m_successBit;
|
return m_successBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool donePolicy = m_constData.m_workflowPolicy == WorkflowPolicy::StopOnSuccess
|
const bool donePolicy = m_taskContainer.m_workflowPolicy == WorkflowPolicy::StopOnSuccess
|
||||||
|| m_constData.m_workflowPolicy == WorkflowPolicy::ContinueOnSuccess;
|
|| m_taskContainer.m_workflowPolicy == WorkflowPolicy::ContinueOnSuccess;
|
||||||
m_successBit = donePolicy ? (m_successBit || success) : (m_successBit && success);
|
m_successBit = donePolicy ? (m_successBit || success) : (m_successBit && success);
|
||||||
return m_successBit;
|
return m_successBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TaskContainer::RuntimeData::currentLimit() const
|
int TaskRuntimeContainer::currentLimit() const
|
||||||
{
|
{
|
||||||
const int childCount = m_constData.m_children.size();
|
// TODO: Handle children well
|
||||||
return m_constData.m_parallelLimit
|
const int childCount = m_taskContainer.m_children.size();
|
||||||
? qMin(m_doneCount + m_constData.m_parallelLimit, childCount) : childCount;
|
return m_taskContainer.m_parallelLimit
|
||||||
|
? qMin(m_doneCount + m_taskContainer.m_parallelLimit, childCount) : childCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupResult TaskContainer::start()
|
SetupResult TaskTreePrivate::start(TaskRuntimeContainer *container)
|
||||||
{
|
{
|
||||||
QT_CHECK(!isRunning());
|
|
||||||
m_runtimeData.emplace(m_constData);
|
|
||||||
|
|
||||||
SetupResult startAction = SetupResult::Continue;
|
SetupResult startAction = SetupResult::Continue;
|
||||||
if (m_constData.m_groupHandler.m_setupHandler) {
|
if (container->m_taskContainer.m_groupHandler.m_setupHandler) {
|
||||||
startAction = invokeHandler(this, m_constData.m_groupHandler.m_setupHandler);
|
startAction = invokeHandler(container, container->m_taskContainer.m_groupHandler.m_setupHandler);
|
||||||
if (startAction != SetupResult::Continue) {
|
if (startAction != SetupResult::Continue) {
|
||||||
m_constData.m_taskTreePrivate->advanceProgress(m_constData.m_taskCount);
|
// TODO: Handle progress well.
|
||||||
|
advanceProgress(container->m_taskContainer.m_taskCount);
|
||||||
// Non-Continue SetupResult takes precedence over the workflow policy.
|
// Non-Continue SetupResult takes precedence over the workflow policy.
|
||||||
m_runtimeData->m_successBit = startAction == SetupResult::StopWithSuccess;
|
container->m_successBit = startAction == SetupResult::StopWithSuccess;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (startAction == SetupResult::Continue) {
|
if (startAction == SetupResult::Continue) {
|
||||||
if (m_constData.m_children.isEmpty())
|
if (container->m_taskContainer.m_children.empty())
|
||||||
startAction = toSetupResult(m_runtimeData->m_successBit);
|
startAction = toSetupResult(container->m_successBit);
|
||||||
|
} else { // TODO: Check if repeater exists, call its handler.
|
||||||
|
|
||||||
}
|
}
|
||||||
return continueStart(startAction, 0);
|
return continueStart(container, startAction, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupResult TaskContainer::continueStart(SetupResult startAction, int nextChild)
|
SetupResult TaskTreePrivate::continueStart(TaskRuntimeContainer *container, SetupResult startAction, int nextChild)
|
||||||
{
|
{
|
||||||
const SetupResult groupAction = startAction == SetupResult::Continue ? startChildren(nextChild)
|
const SetupResult groupAction = startAction == SetupResult::Continue ? startChildren(container, nextChild)
|
||||||
: startAction;
|
: startAction;
|
||||||
QT_CHECK(isRunning()); // TODO: superfluous
|
|
||||||
if (groupAction != SetupResult::Continue) {
|
if (groupAction != SetupResult::Continue) {
|
||||||
const bool bit = m_runtimeData->updateSuccessBit(groupAction == SetupResult::StopWithSuccess);
|
const bool bit = container->updateSuccessBit(groupAction == SetupResult::StopWithSuccess);
|
||||||
const bool result = invokeDoneHandler(bit ? DoneWith::Success : DoneWith::Error);
|
TaskRuntimeContainer *parentContainer = container->m_parentContainer;
|
||||||
if (TaskContainer *parentContainer = m_constData.m_parentContainer) {
|
const bool result = invokeDoneHandler(container, bit ? DoneWith::Success : DoneWith::Error);
|
||||||
QT_CHECK(parentContainer->isRunning());
|
if (parentContainer) {
|
||||||
if (!parentContainer->isStarting())
|
if (!parentContainer->isStarting())
|
||||||
parentContainer->childDone(result);
|
childDone(parentContainer, result);
|
||||||
} else {
|
} else {
|
||||||
m_constData.m_taskTreePrivate->emitDone(result ? DoneWith::Success : DoneWith::Error);
|
m_runtimeRoot.reset();
|
||||||
|
emitDone(result ? DoneWith::Success : DoneWith::Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return groupAction;
|
return groupAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupResult TaskContainer::startChildren(int nextChild)
|
SetupResult TaskTreePrivate::startChildren(TaskRuntimeContainer *container, int nextChild)
|
||||||
{
|
{
|
||||||
QT_CHECK(isRunning());
|
GuardLocker locker(container->m_startGuard);
|
||||||
GuardLocker locker(m_runtimeData->m_startGuard);
|
for (int i = nextChild; i < int(container->m_taskContainer.m_children.size()); ++i) {
|
||||||
for (int i = nextChild; i < m_constData.m_children.size(); ++i) {
|
const int limit = container->currentLimit();
|
||||||
const int limit = m_runtimeData->currentLimit();
|
|
||||||
if (i >= limit)
|
if (i >= limit)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
const SetupResult startAction = m_constData.m_children.at(i)->start();
|
TaskRuntimeNode *newTask = new TaskRuntimeNode(container->m_taskContainer.m_children.at(i), container);
|
||||||
|
container->m_children.append(newTask);
|
||||||
|
|
||||||
|
const SetupResult startAction = start(newTask);
|
||||||
if (startAction == SetupResult::Continue)
|
if (startAction == SetupResult::Continue)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const SetupResult finalizeAction = childDone(startAction == SetupResult::StopWithSuccess);
|
const SetupResult finalizeAction = childDone(container, startAction == SetupResult::StopWithSuccess);
|
||||||
if (finalizeAction == SetupResult::Continue)
|
if (finalizeAction == SetupResult::Continue)
|
||||||
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 += m_constData.m_children.at(j)->taskCount();
|
skippedTaskCount += container->m_taskContainer.m_children.at(j).taskCount();
|
||||||
m_constData.m_taskTreePrivate->advanceProgress(skippedTaskCount);
|
// TODO: Handle progress well
|
||||||
|
advanceProgress(skippedTaskCount);
|
||||||
return finalizeAction;
|
return finalizeAction;
|
||||||
}
|
}
|
||||||
return SetupResult::Continue;
|
return SetupResult::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupResult TaskContainer::childDone(bool success)
|
SetupResult TaskTreePrivate::childDone(TaskRuntimeContainer *container, bool success)
|
||||||
{
|
{
|
||||||
QT_CHECK(isRunning());
|
const int limit = container->currentLimit(); // Read before bumping m_doneCount and stop()
|
||||||
const int limit = m_runtimeData->currentLimit(); // Read before bumping m_doneCount and stop()
|
const WorkflowPolicy &workflowPolicy = container->m_taskContainer.m_workflowPolicy;
|
||||||
const bool shouldStop = m_constData.m_workflowPolicy == WorkflowPolicy::StopOnSuccessOrError
|
const bool shouldStop = workflowPolicy == WorkflowPolicy::StopOnSuccessOrError
|
||||||
|| (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnSuccess && success)
|
|| (workflowPolicy == WorkflowPolicy::StopOnSuccess && success)
|
||||||
|| (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnError && !success);
|
|| (workflowPolicy == WorkflowPolicy::StopOnError && !success);
|
||||||
if (shouldStop)
|
if (shouldStop)
|
||||||
stop();
|
stop(container);
|
||||||
|
|
||||||
++m_runtimeData->m_doneCount;
|
++container->m_doneCount;
|
||||||
const bool updatedSuccess = m_runtimeData->updateSuccessBit(success);
|
const bool updatedSuccess = container->updateSuccessBit(success);
|
||||||
const SetupResult startAction
|
const SetupResult startAction
|
||||||
= (shouldStop || m_runtimeData->m_doneCount == m_constData.m_children.size())
|
= (shouldStop || container->m_doneCount == int(container->m_taskContainer.m_children.size()))
|
||||||
? toSetupResult(updatedSuccess) : SetupResult::Continue;
|
? toSetupResult(updatedSuccess) : SetupResult::Continue;
|
||||||
|
|
||||||
if (isStarting())
|
if (container->isStarting())
|
||||||
return startAction;
|
return startAction;
|
||||||
return continueStart(startAction, limit);
|
return continueStart(container, startAction, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskContainer::stop()
|
void TaskTreePrivate::stop(TaskRuntimeContainer *container)
|
||||||
{
|
{
|
||||||
if (!isRunning())
|
const int limit = container->currentLimit();
|
||||||
return;
|
for (int i = 0; i < limit; ++i) {
|
||||||
|
if (i == container->m_children.size())
|
||||||
const int limit = m_runtimeData->currentLimit();
|
break;
|
||||||
for (int i = 0; i < limit; ++i)
|
TaskRuntimeNode *child = container->m_children.at(i);
|
||||||
m_constData.m_children.at(i)->stop();
|
if (child)
|
||||||
|
stop(child);
|
||||||
|
}
|
||||||
|
|
||||||
int skippedTaskCount = 0;
|
int skippedTaskCount = 0;
|
||||||
for (int i = limit; i < m_constData.m_children.size(); ++i)
|
for (int i = limit; i < int(container->m_taskContainer.m_children.size()); ++i)
|
||||||
skippedTaskCount += m_constData.m_children.at(i)->taskCount();
|
skippedTaskCount += container->m_taskContainer.m_children.at(i).taskCount();
|
||||||
|
|
||||||
m_constData.m_taskTreePrivate->advanceProgress(skippedTaskCount);
|
// TODO: Handle progress well
|
||||||
|
advanceProgress(skippedTaskCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldCall(CallDoneIf callDoneIf, DoneWith result)
|
static bool shouldCall(CallDoneIf callDoneIf, DoneWith result)
|
||||||
@@ -1481,73 +1511,78 @@ static bool shouldCall(CallDoneIf callDoneIf, DoneWith result)
|
|||||||
return callDoneIf != CallDoneIf::Success;
|
return callDoneIf != CallDoneIf::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TaskContainer::invokeDoneHandler(DoneWith doneWith)
|
bool TaskTreePrivate::invokeDoneHandler(TaskRuntimeContainer *container, DoneWith doneWith)
|
||||||
{
|
{
|
||||||
DoneResult result = toDoneResult(doneWith);
|
DoneResult result = toDoneResult(doneWith);
|
||||||
const GroupItem::GroupHandler &groupHandler = m_constData.m_groupHandler;
|
const GroupItem::GroupHandler &groupHandler = container->m_taskContainer.m_groupHandler;
|
||||||
if (groupHandler.m_doneHandler && shouldCall(groupHandler.m_callDoneIf, doneWith))
|
if (groupHandler.m_doneHandler && shouldCall(groupHandler.m_callDoneIf, doneWith))
|
||||||
result = invokeHandler(this, groupHandler.m_doneHandler, doneWith);
|
result = invokeHandler(container, groupHandler.m_doneHandler, doneWith);
|
||||||
m_runtimeData->m_callStorageDoneHandlersOnDestruction = true;
|
container->m_callStorageDoneHandlersOnDestruction = true;
|
||||||
m_runtimeData.reset();
|
container->m_parentNode->m_container.reset();
|
||||||
return result == DoneResult::Success;
|
return result == DoneResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetupResult TaskNode::start()
|
SetupResult TaskTreePrivate::start(TaskRuntimeNode *node)
|
||||||
{
|
{
|
||||||
QT_CHECK(!isRunning());
|
if (!node->m_taskNode.isTask()) {
|
||||||
if (!isTask())
|
node->m_container.reset(new TaskRuntimeContainer(node->m_taskNode.m_container, node,
|
||||||
return m_container.start();
|
node->m_parentContainer));
|
||||||
|
return start(node->m_container.get());
|
||||||
|
}
|
||||||
|
|
||||||
m_task.reset(m_taskHandler.m_createHandler());
|
const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler;
|
||||||
const SetupResult startAction = m_taskHandler.m_setupHandler
|
node->m_task.reset(handler.m_createHandler());
|
||||||
? invokeHandler(parentContainer(), m_taskHandler.m_setupHandler, *m_task.get())
|
const SetupResult startAction = handler.m_setupHandler
|
||||||
|
? invokeHandler(node->m_parentContainer, handler.m_setupHandler, *node->m_task.get())
|
||||||
: SetupResult::Continue;
|
: SetupResult::Continue;
|
||||||
if (startAction != SetupResult::Continue) {
|
if (startAction != SetupResult::Continue) {
|
||||||
m_container.m_constData.m_taskTreePrivate->advanceProgress(1);
|
// TODO: Handle progress well
|
||||||
m_task.reset();
|
advanceProgress(1);
|
||||||
|
node->m_task.reset();
|
||||||
return startAction;
|
return startAction;
|
||||||
}
|
}
|
||||||
const std::shared_ptr<SetupResult> unwindAction
|
const std::shared_ptr<SetupResult> unwindAction
|
||||||
= std::make_shared<SetupResult>(SetupResult::Continue);
|
= std::make_shared<SetupResult>(SetupResult::Continue);
|
||||||
QObject::connect(m_task.get(), &TaskInterface::done, taskTree(), [=](bool success) {
|
QObject::connect(node->m_task.get(), &TaskInterface::done,
|
||||||
const bool result = invokeDoneHandler(success ? DoneWith::Success : DoneWith::Error);
|
q, [this, node, unwindAction](bool success) {
|
||||||
QObject::disconnect(m_task.get(), &TaskInterface::done, taskTree(), nullptr);
|
const bool result = invokeDoneHandler(node, success ? DoneWith::Success : DoneWith::Error);
|
||||||
m_task.release()->deleteLater();
|
QObject::disconnect(node->m_task.get(), &TaskInterface::done, q, nullptr);
|
||||||
QT_ASSERT(parentContainer() && parentContainer()->isRunning(), return);
|
node->m_task.release()->deleteLater(); // TODO: delete later this???
|
||||||
if (parentContainer()->isStarting())
|
if (node->m_parentContainer->isStarting())
|
||||||
*unwindAction = toSetupResult(result);
|
*unwindAction = toSetupResult(result);
|
||||||
else
|
else
|
||||||
parentContainer()->childDone(result);
|
childDone(node->m_parentContainer, result);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_task->start();
|
node->m_task->start();
|
||||||
return *unwindAction;
|
return *unwindAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskNode::stop()
|
void TaskTreePrivate::stop(TaskRuntimeNode *node)
|
||||||
{
|
{
|
||||||
if (!isRunning())
|
if (!node->m_task) {
|
||||||
return;
|
if (!node->m_container)
|
||||||
|
return;
|
||||||
if (!m_task) {
|
stop(node->m_container.get());
|
||||||
m_container.stop();
|
node->m_container->updateSuccessBit(false);
|
||||||
m_container.m_runtimeData->updateSuccessBit(false);
|
invokeDoneHandler(node->m_container.get(), DoneWith::Cancel);
|
||||||
m_container.invokeDoneHandler(DoneWith::Cancel);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cancelHandler?
|
invokeDoneHandler(node, DoneWith::Cancel);
|
||||||
// TODO: call TaskInterface::stop() ?
|
node->m_task.reset();
|
||||||
invokeDoneHandler(DoneWith::Cancel);
|
|
||||||
m_task.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TaskNode::invokeDoneHandler(DoneWith doneWith)
|
bool TaskTreePrivate::invokeDoneHandler(TaskRuntimeNode *node, DoneWith doneWith)
|
||||||
{
|
{
|
||||||
DoneResult result = toDoneResult(doneWith);
|
DoneResult result = toDoneResult(doneWith);
|
||||||
if (m_taskHandler.m_doneHandler && shouldCall(m_taskHandler.m_callDoneIf, doneWith))
|
const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler;
|
||||||
result = invokeHandler(parentContainer(), m_taskHandler.m_doneHandler, *m_task.get(), doneWith);
|
if (handler.m_doneHandler && shouldCall(handler.m_callDoneIf, doneWith)) {
|
||||||
m_container.m_constData.m_taskTreePrivate->advanceProgress(1);
|
result = invokeHandler(node->m_parentContainer,
|
||||||
|
handler.m_doneHandler, *node->m_task.get(), doneWith);
|
||||||
|
}
|
||||||
|
// TODO: Handle progress well
|
||||||
|
advanceProgress(1);
|
||||||
return result == DoneResult::Success;
|
return result == DoneResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2359,7 +2394,7 @@ void TaskTree::stop()
|
|||||||
*/
|
*/
|
||||||
bool TaskTree::isRunning() const
|
bool TaskTree::isRunning() const
|
||||||
{
|
{
|
||||||
return d->m_root && d->m_root->isRunning();
|
return bool(d->m_runtimeRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -2586,8 +2621,8 @@ int TaskTree::progressValue() const
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void TaskTree::setupStorageHandler(const TreeStorageBase &storage,
|
void TaskTree::setupStorageHandler(const TreeStorageBase &storage,
|
||||||
StorageVoidHandler setupHandler,
|
TreeStorageBase::StorageVoidHandler setupHandler,
|
||||||
StorageVoidHandler doneHandler)
|
TreeStorageBase::StorageVoidHandler doneHandler)
|
||||||
{
|
{
|
||||||
auto it = d->m_storageHandlers.find(storage);
|
auto it = d->m_storageHandlers.find(storage);
|
||||||
if (it == d->m_storageHandlers.end()) {
|
if (it == d->m_storageHandlers.end()) {
|
||||||
|
@@ -19,9 +19,7 @@ namespace Tasking {
|
|||||||
|
|
||||||
Q_NAMESPACE_EXPORT(TASKING_EXPORT)
|
Q_NAMESPACE_EXPORT(TASKING_EXPORT)
|
||||||
|
|
||||||
class ExecutionContextActivator;
|
|
||||||
class StorageData;
|
class StorageData;
|
||||||
class TaskContainer;
|
|
||||||
class TaskTreePrivate;
|
class TaskTreePrivate;
|
||||||
|
|
||||||
class TASKING_EXPORT TaskInterface : public QObject
|
class TASKING_EXPORT TaskInterface : public QObject
|
||||||
@@ -33,7 +31,7 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename Task, typename Deleter> friend class TaskAdapter;
|
template <typename Task, typename Deleter> friend class TaskAdapter;
|
||||||
friend class TaskNode;
|
friend class TaskTreePrivate;
|
||||||
TaskInterface() = default;
|
TaskInterface() = default;
|
||||||
#ifdef Q_QDOC
|
#ifdef Q_QDOC
|
||||||
protected:
|
protected:
|
||||||
@@ -46,6 +44,7 @@ class TASKING_EXPORT TreeStorageBase
|
|||||||
public:
|
public:
|
||||||
using StorageConstructor = std::function<void *(void)>;
|
using StorageConstructor = std::function<void *(void)>;
|
||||||
using StorageDestructor = std::function<void(void *)>;
|
using StorageDestructor = std::function<void(void *)>;
|
||||||
|
using StorageVoidHandler = std::function<void(void *)>;
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
@@ -66,9 +65,9 @@ private:
|
|||||||
QSharedPointer<StorageData> m_storageData;
|
QSharedPointer<StorageData> m_storageData;
|
||||||
|
|
||||||
template <typename StorageStruct> friend class TreeStorage;
|
template <typename StorageStruct> friend class TreeStorage;
|
||||||
friend ExecutionContextActivator;
|
friend class ExecutionContextActivator;
|
||||||
friend TaskContainer;
|
friend class TaskRuntimeContainer;
|
||||||
friend TaskTreePrivate;
|
friend class TaskTreePrivate;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename StorageStruct>
|
template <typename StorageStruct>
|
||||||
@@ -461,8 +460,6 @@ private:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class TaskTreePrivate;
|
|
||||||
|
|
||||||
class TASKING_EXPORT TaskTree final : public QObject
|
class TASKING_EXPORT TaskTree final : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -515,19 +512,17 @@ signals:
|
|||||||
void progressValueChanged(int value); // updated whenever task finished / skipped / stopped
|
void progressValueChanged(int value); // updated whenever task finished / skipped / stopped
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using StorageVoidHandler = std::function<void(void *)>;
|
|
||||||
void setupStorageHandler(const TreeStorageBase &storage,
|
void setupStorageHandler(const TreeStorageBase &storage,
|
||||||
StorageVoidHandler setupHandler,
|
TreeStorageBase::StorageVoidHandler setupHandler,
|
||||||
StorageVoidHandler doneHandler);
|
TreeStorageBase::StorageVoidHandler doneHandler);
|
||||||
template <typename StorageStruct, typename StorageHandler>
|
template <typename StorageStruct, typename StorageHandler>
|
||||||
StorageVoidHandler wrapHandler(StorageHandler &&handler) {
|
TreeStorageBase::StorageVoidHandler wrapHandler(StorageHandler &&handler) {
|
||||||
return [=](void *voidStruct) {
|
return [=](void *voidStruct) {
|
||||||
auto *storageStruct = static_cast<StorageStruct *>(voidStruct);
|
auto *storageStruct = static_cast<StorageStruct *>(voidStruct);
|
||||||
std::invoke(handler, *storageStruct);
|
std::invoke(handler, *storageStruct);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
friend class TaskTreePrivate;
|
|
||||||
TaskTreePrivate *d;
|
TaskTreePrivate *d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user