2023-05-10 21:38:41 +02:00
|
|
|
// Copyright (C) 2023 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
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
2023-05-10 21:38:41 +02:00
|
|
|
#include "tasking_global.h"
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-01-20 10:44:21 +01:00
|
|
|
#include <QHash>
|
2022-10-12 14:30:24 +02:00
|
|
|
#include <QObject>
|
2022-11-21 12:20:21 +01:00
|
|
|
#include <QSharedPointer>
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-05-15 10:52:45 +02:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
template <class T>
|
|
|
|
|
class QFuture;
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
2023-05-10 19:54:52 +02:00
|
|
|
namespace Tasking {
|
2022-11-21 12:20:21 +01:00
|
|
|
|
2023-05-29 11:46:20 +02:00
|
|
|
Q_NAMESPACE
|
|
|
|
|
|
2023-02-20 21:32:00 +01:00
|
|
|
class ExecutionContextActivator;
|
2022-11-21 12:20:21 +01:00
|
|
|
class TaskContainer;
|
2022-12-05 11:20:57 +01:00
|
|
|
class TaskTreePrivate;
|
|
|
|
|
|
2023-05-10 21:38:41 +02:00
|
|
|
class TASKING_EXPORT TaskInterface : public QObject
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
TaskInterface() = default;
|
|
|
|
|
virtual void start() = 0;
|
|
|
|
|
|
|
|
|
|
signals:
|
|
|
|
|
void done(bool success);
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-10 21:38:41 +02:00
|
|
|
class TASKING_EXPORT TreeStorageBase
|
2022-11-21 12:20:21 +01:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
bool isValid() const;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
using StorageConstructor = std::function<void *(void)>;
|
|
|
|
|
using StorageDestructor = std::function<void(void *)>;
|
|
|
|
|
|
|
|
|
|
TreeStorageBase(StorageConstructor ctor, StorageDestructor dtor);
|
|
|
|
|
void *activeStorageVoid() const;
|
|
|
|
|
|
|
|
|
|
private:
|
2023-01-24 21:33:56 +01:00
|
|
|
int createStorage() const;
|
|
|
|
|
void deleteStorage(int id) const;
|
|
|
|
|
void activateStorage(int id) const;
|
2022-11-21 12:20:21 +01:00
|
|
|
|
2023-01-05 17:21:37 +01:00
|
|
|
friend bool operator==(const TreeStorageBase &first, const TreeStorageBase &second)
|
|
|
|
|
{ return first.m_storageData == second.m_storageData; }
|
|
|
|
|
|
|
|
|
|
friend bool operator!=(const TreeStorageBase &first, const TreeStorageBase &second)
|
|
|
|
|
{ return first.m_storageData != second.m_storageData; }
|
|
|
|
|
|
|
|
|
|
friend size_t qHash(const TreeStorageBase &storage, uint seed = 0)
|
|
|
|
|
{ return size_t(storage.m_storageData.get()) ^ seed; }
|
|
|
|
|
|
2022-11-21 12:20:21 +01:00
|
|
|
struct StorageData {
|
2022-11-22 15:21:45 +01:00
|
|
|
~StorageData();
|
2022-11-21 12:20:21 +01:00
|
|
|
StorageConstructor m_constructor = {};
|
|
|
|
|
StorageDestructor m_destructor = {};
|
|
|
|
|
QHash<int, void *> m_storageHash = {};
|
|
|
|
|
int m_activeStorage = 0; // 0 means no active storage
|
|
|
|
|
int m_storageCounter = 0;
|
|
|
|
|
};
|
|
|
|
|
QSharedPointer<StorageData> m_storageData;
|
2023-05-18 13:16:40 +02:00
|
|
|
friend ExecutionContextActivator;
|
2022-11-21 12:20:21 +01:00
|
|
|
friend TaskContainer;
|
2022-12-05 11:20:57 +01:00
|
|
|
friend TaskTreePrivate;
|
2022-11-21 12:20:21 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename StorageStruct>
|
|
|
|
|
class TreeStorage : public TreeStorageBase
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
TreeStorage() : TreeStorageBase(TreeStorage::ctor(), TreeStorage::dtor()) {}
|
2023-02-20 20:58:38 +01:00
|
|
|
StorageStruct &operator*() const noexcept { return *activeStorage(); }
|
2022-11-21 12:20:21 +01:00
|
|
|
StorageStruct *operator->() const noexcept { return activeStorage(); }
|
|
|
|
|
StorageStruct *activeStorage() const {
|
|
|
|
|
return static_cast<StorageStruct *>(activeStorageVoid());
|
|
|
|
|
}
|
2022-11-23 11:37:34 +01:00
|
|
|
|
|
|
|
|
private:
|
2022-11-21 12:20:21 +01:00
|
|
|
static StorageConstructor ctor() { return [] { return new StorageStruct; }; }
|
|
|
|
|
static StorageDestructor dtor() {
|
|
|
|
|
return [](void *storage) { delete static_cast<StorageStruct *>(storage); };
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-02-20 20:33:59 +01:00
|
|
|
// WorkflowPolicy:
|
2022-11-11 10:37:02 +01:00
|
|
|
// 1. When all children finished with done -> report done, otherwise:
|
2023-05-17 19:28:57 +02:00
|
|
|
// a) Report error on first error and stop executing other children (including their subtree).
|
|
|
|
|
// b) On first error - continue executing all children and report error afterwards.
|
2022-11-11 10:37:02 +01:00
|
|
|
// 2. When all children finished with error -> report error, otherwise:
|
2023-05-17 19:28:57 +02:00
|
|
|
// a) Report done on first done and stop executing other children (including their subtree).
|
|
|
|
|
// b) On first done - continue executing all children and report done afterwards.
|
|
|
|
|
// 3. Stops on first finished child. In sequential mode it will never run other children then the first one.
|
|
|
|
|
// Useful only in parallel mode.
|
2023-05-28 17:20:55 +02:00
|
|
|
// 4. Always run all children, let them finish, ignore their results and report done afterwards.
|
2023-05-28 22:07:17 +02:00
|
|
|
// 5. Always run all children, let them finish, ignore their results and report error afterwards.
|
2022-11-11 10:37:02 +01:00
|
|
|
|
|
|
|
|
enum class WorkflowPolicy {
|
2023-05-28 17:20:55 +02:00
|
|
|
StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done).
|
|
|
|
|
ContinueOnError, // 1b - The same, but children execution continues. Reports done when no children.
|
|
|
|
|
StopOnDone, // 2a - Reports done on first child done, otherwise error (if all children were error).
|
|
|
|
|
ContinueOnDone, // 2b - The same, but children execution continues. Reports error when no children.
|
|
|
|
|
StopOnFinished, // 3 - Stops on first finished child and report its result.
|
2023-05-28 22:07:17 +02:00
|
|
|
FinishAllAndDone, // 4 - Reports done after all children finished.
|
|
|
|
|
FinishAllAndError // 5 - Reports error after all children finished.
|
2022-11-11 10:37:02 +01:00
|
|
|
};
|
2023-05-29 11:46:20 +02:00
|
|
|
Q_ENUM_NS(WorkflowPolicy);
|
2022-11-11 10:37:02 +01:00
|
|
|
|
2023-01-09 20:52:12 +01:00
|
|
|
enum class TaskAction
|
|
|
|
|
{
|
|
|
|
|
Continue,
|
|
|
|
|
StopWithDone,
|
|
|
|
|
StopWithError
|
|
|
|
|
};
|
2023-05-29 11:46:20 +02:00
|
|
|
Q_ENUM_NS(TaskAction);
|
2023-01-09 20:52:12 +01:00
|
|
|
|
2023-05-10 21:38:41 +02:00
|
|
|
class TASKING_EXPORT TaskItem
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
// Internal, provided by QTC_DECLARE_CUSTOM_TASK
|
|
|
|
|
using TaskCreateHandler = std::function<TaskInterface *(void)>;
|
|
|
|
|
// Called prior to task start, just after createHandler
|
2023-01-09 20:52:12 +01:00
|
|
|
using TaskSetupHandler = std::function<TaskAction(TaskInterface &)>;
|
2022-10-12 14:30:24 +02:00
|
|
|
// Called on task done / error
|
|
|
|
|
using TaskEndHandler = std::function<void(const TaskInterface &)>;
|
2022-11-04 14:08:32 +01:00
|
|
|
// Called when group entered
|
2023-01-20 10:44:21 +01:00
|
|
|
using GroupSetupHandler = std::function<TaskAction()>;
|
2023-01-20 17:05:33 +01:00
|
|
|
// Called when group done / error
|
|
|
|
|
using GroupEndHandler = std::function<void()>;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
struct TaskHandler {
|
|
|
|
|
TaskCreateHandler m_createHandler;
|
2023-05-02 14:01:49 +02:00
|
|
|
TaskSetupHandler m_setupHandler = {};
|
|
|
|
|
TaskEndHandler m_doneHandler = {};
|
|
|
|
|
TaskEndHandler m_errorHandler = {};
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct GroupHandler {
|
2023-01-20 17:05:33 +01:00
|
|
|
GroupSetupHandler m_setupHandler;
|
|
|
|
|
GroupEndHandler m_doneHandler = {};
|
|
|
|
|
GroupEndHandler m_errorHandler = {};
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
2023-05-18 13:16:40 +02:00
|
|
|
struct GroupData {
|
|
|
|
|
GroupHandler m_groupHandler = {};
|
|
|
|
|
std::optional<int> m_parallelLimit = {};
|
|
|
|
|
std::optional<WorkflowPolicy> m_workflowPolicy = {};
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
QList<TaskItem> children() const { return m_children; }
|
2023-05-18 13:16:40 +02:00
|
|
|
GroupData groupData() const { return m_groupData; }
|
2022-11-21 15:05:39 +01:00
|
|
|
QList<TreeStorageBase> storageList() const { return m_storageList; }
|
2023-05-18 13:16:40 +02:00
|
|
|
TaskHandler taskHandler() const { return m_taskHandler; }
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
enum class Type {
|
|
|
|
|
Group,
|
2023-05-18 13:16:40 +02:00
|
|
|
GroupData,
|
2022-11-21 15:05:39 +01:00
|
|
|
Storage,
|
2023-05-18 13:16:40 +02:00
|
|
|
TaskHandler
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TaskItem() = default;
|
2023-05-18 13:16:40 +02:00
|
|
|
TaskItem(const GroupData &data)
|
|
|
|
|
: m_type(Type::GroupData)
|
|
|
|
|
, m_groupData(data) {}
|
2022-11-21 15:05:39 +01:00
|
|
|
TaskItem(const TreeStorageBase &storage)
|
|
|
|
|
: m_type(Type::Storage)
|
|
|
|
|
, m_storageList{storage} {}
|
2023-05-18 13:16:40 +02:00
|
|
|
TaskItem(const TaskHandler &handler)
|
|
|
|
|
: m_type(Type::TaskHandler)
|
|
|
|
|
, m_taskHandler(handler) {}
|
2022-10-12 14:30:24 +02:00
|
|
|
void addChildren(const QList<TaskItem> &children);
|
|
|
|
|
|
2023-05-02 14:01:49 +02:00
|
|
|
void setTaskSetupHandler(const TaskSetupHandler &handler);
|
|
|
|
|
void setTaskDoneHandler(const TaskEndHandler &handler);
|
|
|
|
|
void setTaskErrorHandler(const TaskEndHandler &handler);
|
2023-05-18 13:16:40 +02:00
|
|
|
static TaskItem groupHandler(const GroupHandler &handler) { return TaskItem({handler}); }
|
|
|
|
|
static TaskItem parallelLimit(int limit) { return TaskItem({{}, limit}); }
|
|
|
|
|
static TaskItem workflowPolicy(WorkflowPolicy policy) { return TaskItem({{}, {}, policy}); }
|
2023-05-29 00:13:12 +02:00
|
|
|
static TaskItem withTimeout(const TaskItem &item, std::chrono::milliseconds timeout,
|
|
|
|
|
const GroupEndHandler &handler = {});
|
2023-05-02 14:01:49 +02:00
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
private:
|
|
|
|
|
Type m_type = Type::Group;
|
|
|
|
|
QList<TaskItem> m_children;
|
2023-05-18 13:16:40 +02:00
|
|
|
GroupData m_groupData;
|
|
|
|
|
QList<TreeStorageBase> m_storageList;
|
|
|
|
|
TaskHandler m_taskHandler;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
2023-05-10 21:38:41 +02:00
|
|
|
class TASKING_EXPORT Group : public TaskItem
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Group(const QList<TaskItem> &children) { addChildren(children); }
|
|
|
|
|
Group(std::initializer_list<TaskItem> children) { addChildren(children); }
|
2023-05-17 19:57:23 +02:00
|
|
|
|
2023-05-18 13:16:40 +02:00
|
|
|
// GroupData related:
|
2023-05-17 19:57:23 +02:00
|
|
|
template <typename SetupHandler>
|
|
|
|
|
static TaskItem onGroupSetup(SetupHandler &&handler) {
|
2023-05-18 13:16:40 +02:00
|
|
|
return groupHandler({wrapGroupSetup(std::forward<SetupHandler>(handler))});
|
2023-05-17 19:57:23 +02:00
|
|
|
}
|
|
|
|
|
static TaskItem onGroupDone(const GroupEndHandler &handler) {
|
2023-05-18 13:16:40 +02:00
|
|
|
return groupHandler({{}, handler});
|
2023-05-17 19:57:23 +02:00
|
|
|
}
|
|
|
|
|
static TaskItem onGroupError(const GroupEndHandler &handler) {
|
2023-05-18 13:16:40 +02:00
|
|
|
return groupHandler({{}, {}, handler});
|
2023-05-17 19:57:23 +02:00
|
|
|
}
|
2023-05-18 13:16:40 +02:00
|
|
|
using TaskItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel).
|
|
|
|
|
using TaskItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError.
|
2023-05-17 19:57:23 +02:00
|
|
|
|
2023-05-29 00:13:12 +02:00
|
|
|
TaskItem withTimeout(std::chrono::milliseconds timeout,
|
|
|
|
|
const GroupEndHandler &handler = {}) const {
|
|
|
|
|
return TaskItem::withTimeout(*this, timeout, handler);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-17 19:57:23 +02:00
|
|
|
private:
|
|
|
|
|
template<typename SetupHandler>
|
|
|
|
|
static GroupSetupHandler wrapGroupSetup(SetupHandler &&handler)
|
|
|
|
|
{
|
|
|
|
|
static constexpr bool isDynamic
|
|
|
|
|
= std::is_same_v<TaskAction, std::invoke_result_t<std::decay_t<SetupHandler>>>;
|
|
|
|
|
constexpr bool isVoid
|
|
|
|
|
= std::is_same_v<void, std::invoke_result_t<std::decay_t<SetupHandler>>>;
|
|
|
|
|
static_assert(isDynamic || isVoid,
|
|
|
|
|
"Group setup handler needs to take no arguments and has to return "
|
|
|
|
|
"void or TaskAction. The passed handler doesn't fulfill these requirements.");
|
|
|
|
|
return [=] {
|
|
|
|
|
if constexpr (isDynamic)
|
|
|
|
|
return std::invoke(handler);
|
|
|
|
|
std::invoke(handler);
|
|
|
|
|
return TaskAction::Continue;
|
|
|
|
|
};
|
|
|
|
|
};
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
2023-05-17 19:57:23 +02:00
|
|
|
template <typename SetupHandler>
|
|
|
|
|
static TaskItem onGroupSetup(SetupHandler &&handler)
|
|
|
|
|
{
|
|
|
|
|
return Group::onGroupSetup(std::forward<SetupHandler>(handler));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TASKING_EXPORT TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler);
|
|
|
|
|
TASKING_EXPORT TaskItem onGroupError(const TaskItem::GroupEndHandler &handler);
|
2023-05-18 13:16:40 +02:00
|
|
|
TASKING_EXPORT TaskItem parallelLimit(int limit);
|
|
|
|
|
TASKING_EXPORT TaskItem workflowPolicy(WorkflowPolicy policy);
|
|
|
|
|
|
|
|
|
|
TASKING_EXPORT extern const TaskItem sequential;
|
|
|
|
|
TASKING_EXPORT extern const TaskItem parallel;
|
2023-05-28 22:07:17 +02:00
|
|
|
|
2023-05-18 13:16:40 +02:00
|
|
|
TASKING_EXPORT extern const TaskItem stopOnError;
|
|
|
|
|
TASKING_EXPORT extern const TaskItem continueOnError;
|
|
|
|
|
TASKING_EXPORT extern const TaskItem stopOnDone;
|
|
|
|
|
TASKING_EXPORT extern const TaskItem continueOnDone;
|
|
|
|
|
TASKING_EXPORT extern const TaskItem stopOnFinished;
|
2023-05-28 17:20:55 +02:00
|
|
|
TASKING_EXPORT extern const TaskItem finishAllAndDone;
|
2023-05-28 22:07:17 +02:00
|
|
|
TASKING_EXPORT extern const TaskItem finishAllAndError;
|
2023-05-17 19:57:23 +02:00
|
|
|
|
2023-05-10 21:38:41 +02:00
|
|
|
class TASKING_EXPORT Storage : public TaskItem
|
2022-11-21 15:05:39 +01:00
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
Storage(const TreeStorageBase &storage) : TaskItem(storage) { }
|
|
|
|
|
};
|
|
|
|
|
|
2023-02-06 20:27:59 +01:00
|
|
|
// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()
|
2023-05-10 21:38:41 +02:00
|
|
|
class TASKING_EXPORT Sync : public Group
|
2023-02-06 20:27:59 +01:00
|
|
|
{
|
2023-04-26 12:33:11 +02:00
|
|
|
|
2023-02-06 20:27:59 +01:00
|
|
|
public:
|
2023-04-26 12:33:11 +02:00
|
|
|
template<typename Function>
|
|
|
|
|
Sync(Function &&function) : Group(init(std::forward<Function>(function))) {}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
template<typename Function>
|
|
|
|
|
static QList<TaskItem> init(Function &&function) {
|
|
|
|
|
constexpr bool isInvocable = std::is_invocable_v<std::decay_t<Function>>;
|
|
|
|
|
static_assert(isInvocable,
|
|
|
|
|
"Sync element: The synchronous function can't take any arguments.");
|
|
|
|
|
constexpr bool isBool = std::is_same_v<bool, std::invoke_result_t<std::decay_t<Function>>>;
|
|
|
|
|
constexpr bool isVoid = std::is_same_v<void, std::invoke_result_t<std::decay_t<Function>>>;
|
|
|
|
|
static_assert(isBool || isVoid,
|
|
|
|
|
"Sync element: The synchronous function has to return void or bool.");
|
|
|
|
|
if constexpr (isBool) {
|
2023-05-17 19:57:23 +02:00
|
|
|
return {onGroupSetup([function] { return function() ? TaskAction::StopWithDone
|
2023-04-26 12:33:11 +02:00
|
|
|
: TaskAction::StopWithError; })};
|
|
|
|
|
}
|
2023-05-17 19:57:23 +02:00
|
|
|
return {onGroupSetup([function] { function(); return TaskAction::StopWithDone; })};
|
2023-04-26 12:33:11 +02:00
|
|
|
};
|
2023-02-06 20:27:59 +01:00
|
|
|
};
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
template <typename Task>
|
|
|
|
|
class TaskAdapter : public TaskInterface
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
using Type = Task;
|
|
|
|
|
TaskAdapter() = default;
|
|
|
|
|
Task *task() { return &m_task; }
|
|
|
|
|
const Task *task() const { return &m_task; }
|
|
|
|
|
private:
|
|
|
|
|
Task m_task;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename Adapter>
|
|
|
|
|
class CustomTask : public TaskItem
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
using Task = typename Adapter::Type;
|
|
|
|
|
using EndHandler = std::function<void(const Task &)>;
|
|
|
|
|
static Adapter *createAdapter() { return new Adapter; }
|
2023-05-02 14:01:49 +02:00
|
|
|
CustomTask() : TaskItem({&createAdapter}) {}
|
2023-01-09 20:52:12 +01:00
|
|
|
template <typename SetupFunction>
|
|
|
|
|
CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {})
|
2023-01-20 15:13:42 +01:00
|
|
|
: TaskItem({&createAdapter, wrapSetup(std::forward<SetupFunction>(function)),
|
|
|
|
|
wrapEnd(done), wrapEnd(error)}) {}
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-05-02 14:01:49 +02:00
|
|
|
template <typename SetupFunction>
|
|
|
|
|
CustomTask &onSetup(SetupFunction &&function) {
|
|
|
|
|
setTaskSetupHandler(wrapSetup(std::forward<SetupFunction>(function)));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
CustomTask &onDone(const EndHandler &handler) {
|
|
|
|
|
setTaskDoneHandler(wrapEnd(handler));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
CustomTask &onError(const EndHandler &handler) {
|
|
|
|
|
setTaskErrorHandler(wrapEnd(handler));
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 00:13:12 +02:00
|
|
|
TaskItem withTimeout(std::chrono::milliseconds timeout,
|
|
|
|
|
const GroupEndHandler &handler = {}) const {
|
|
|
|
|
return TaskItem::withTimeout(*this, timeout, handler);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
private:
|
2023-01-09 20:52:12 +01:00
|
|
|
template<typename SetupFunction>
|
|
|
|
|
static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {
|
2023-01-23 09:57:45 +01:00
|
|
|
static constexpr bool isDynamic = std::is_same_v<TaskAction,
|
2023-01-20 15:13:42 +01:00
|
|
|
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
|
|
|
|
|
constexpr bool isVoid = std::is_same_v<void,
|
|
|
|
|
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
|
|
|
|
|
static_assert(isDynamic || isVoid,
|
|
|
|
|
"Task setup handler needs to take (Task &) as an argument and has to return "
|
|
|
|
|
"void or TaskAction. The passed handler doesn't fulfill these requirements.");
|
2023-01-09 20:52:12 +01:00
|
|
|
return [=](TaskInterface &taskInterface) {
|
|
|
|
|
Adapter &adapter = static_cast<Adapter &>(taskInterface);
|
2023-01-20 15:13:42 +01:00
|
|
|
if constexpr (isDynamic)
|
|
|
|
|
return std::invoke(function, *adapter.task());
|
2023-01-09 20:52:12 +01:00
|
|
|
std::invoke(function, *adapter.task());
|
|
|
|
|
return TaskAction::Continue;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
};
|
2023-01-09 20:52:12 +01:00
|
|
|
|
2022-11-29 19:58:54 +01:00
|
|
|
static TaskEndHandler wrapEnd(const EndHandler &handler) {
|
2022-10-12 14:30:24 +02:00
|
|
|
if (!handler)
|
|
|
|
|
return {};
|
|
|
|
|
return [handler](const TaskInterface &taskInterface) {
|
|
|
|
|
const Adapter &adapter = static_cast<const Adapter &>(taskInterface);
|
|
|
|
|
handler(*adapter.task());
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TaskTreePrivate;
|
|
|
|
|
|
2023-05-10 21:38:41 +02:00
|
|
|
class TASKING_EXPORT TaskTree final : public QObject
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
2022-11-16 08:07:29 +01:00
|
|
|
TaskTree();
|
2023-05-10 19:54:52 +02:00
|
|
|
TaskTree(const Group &root);
|
2022-10-12 14:30:24 +02:00
|
|
|
~TaskTree();
|
|
|
|
|
|
2023-05-10 19:54:52 +02:00
|
|
|
void setupRoot(const Group &root);
|
2022-11-16 08:07:29 +01:00
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
void start();
|
|
|
|
|
void stop();
|
|
|
|
|
bool isRunning() const;
|
2022-12-05 11:20:57 +01:00
|
|
|
|
2023-05-15 10:52:45 +02:00
|
|
|
// Helper methods. They execute a local event loop with ExcludeUserInputEvents.
|
|
|
|
|
// The passed future is used for listening to the cancel event.
|
|
|
|
|
// Don't use it in main thread. To be used in non-main threads or in auto tests.
|
|
|
|
|
bool runBlocking(const QFuture<void> &future, int timeoutMs = 0);
|
|
|
|
|
bool runBlocking(int timeoutMs = 0);
|
|
|
|
|
|
2022-11-10 14:31:25 +01:00
|
|
|
int taskCount() const;
|
2022-11-10 15:59:54 +01:00
|
|
|
int progressMaximum() const { return taskCount(); }
|
|
|
|
|
int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2022-12-05 11:20:57 +01:00
|
|
|
template <typename StorageStruct, typename StorageHandler>
|
2023-05-10 19:54:52 +02:00
|
|
|
void onStorageSetup(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
|
2023-01-20 15:13:42 +01:00
|
|
|
setupStorageHandler(storage,
|
|
|
|
|
wrapHandler<StorageStruct>(std::forward<StorageHandler>(handler)), {});
|
2022-12-05 11:20:57 +01:00
|
|
|
}
|
|
|
|
|
template <typename StorageStruct, typename StorageHandler>
|
2023-05-10 19:54:52 +02:00
|
|
|
void onStorageDone(const TreeStorage<StorageStruct> &storage, StorageHandler &&handler) {
|
2023-01-20 15:13:42 +01:00
|
|
|
setupStorageHandler(storage,
|
|
|
|
|
{}, wrapHandler<StorageStruct>(std::forward<StorageHandler>(handler)));
|
2022-12-05 11:20:57 +01:00
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
signals:
|
2022-11-10 15:59:54 +01:00
|
|
|
void started();
|
2022-10-12 14:30:24 +02:00
|
|
|
void done();
|
|
|
|
|
void errorOccurred();
|
2022-11-10 15:59:54 +01:00
|
|
|
void progressValueChanged(int value); // updated whenever task finished / skipped / stopped
|
2022-10-12 14:30:24 +02:00
|
|
|
|
|
|
|
|
private:
|
2022-12-05 11:20:57 +01:00
|
|
|
using StorageVoidHandler = std::function<void(void *)>;
|
2023-05-10 19:54:52 +02:00
|
|
|
void setupStorageHandler(const TreeStorageBase &storage,
|
2022-12-05 11:20:57 +01:00
|
|
|
StorageVoidHandler setupHandler,
|
|
|
|
|
StorageVoidHandler doneHandler);
|
2023-01-20 15:13:42 +01:00
|
|
|
template <typename StorageStruct, typename StorageHandler>
|
|
|
|
|
StorageVoidHandler wrapHandler(StorageHandler &&handler) {
|
|
|
|
|
return [=](void *voidStruct) {
|
2022-12-05 11:20:57 +01:00
|
|
|
StorageStruct *storageStruct = static_cast<StorageStruct *>(voidStruct);
|
2023-01-20 15:13:42 +01:00
|
|
|
std::invoke(handler, storageStruct);
|
2022-12-05 11:20:57 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend class TaskTreePrivate;
|
2022-10-12 14:30:24 +02:00
|
|
|
TaskTreePrivate *d;
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-10 21:38:41 +02:00
|
|
|
class TASKING_EXPORT TaskTreeTaskAdapter : public TaskAdapter<TaskTree>
|
2022-11-16 09:06:32 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2023-05-09 23:43:18 +02:00
|
|
|
TaskTreeTaskAdapter();
|
2022-11-16 09:06:32 +01:00
|
|
|
void start() final;
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-28 22:47:59 +02:00
|
|
|
class TASKING_EXPORT TimeoutTaskAdapter : public TaskAdapter<std::chrono::milliseconds>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
TimeoutTaskAdapter();
|
2023-05-31 18:29:11 +02:00
|
|
|
~TimeoutTaskAdapter();
|
2023-05-28 22:47:59 +02:00
|
|
|
void start() final;
|
2023-05-31 18:29:11 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::optional<int> m_timerId;
|
2023-05-28 22:47:59 +02:00
|
|
|
};
|
|
|
|
|
|
2023-05-10 19:54:52 +02:00
|
|
|
} // namespace Tasking
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-05-10 20:20:21 +02:00
|
|
|
#define TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)\
|
2023-05-10 19:54:52 +02:00
|
|
|
namespace Tasking { using CustomTaskName = CustomTask<TaskAdapterClass>; }
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-05-10 20:20:21 +02:00
|
|
|
#define TASKING_DECLARE_TEMPLATE_TASK(CustomTaskName, TaskAdapterClass)\
|
2023-05-10 19:54:52 +02:00
|
|
|
namespace Tasking {\
|
2022-10-12 14:30:24 +02:00
|
|
|
template <typename ...Args>\
|
|
|
|
|
using CustomTaskName = CustomTask<TaskAdapterClass<Args...>>;\
|
2023-05-10 19:54:52 +02:00
|
|
|
} // namespace Tasking
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-05-10 20:20:21 +02:00
|
|
|
TASKING_DECLARE_TASK(TaskTreeTask, TaskTreeTaskAdapter);
|
2023-05-28 22:47:59 +02:00
|
|
|
TASKING_DECLARE_TASK(TimeoutTask, TimeoutTaskAdapter);
|