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
|
|
|
|
|
|
|
|
#include <QObject>
|
2022-11-21 12:20:21 +01:00
|
|
|
#include <QSharedPointer>
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-10-23 22:41:47 +02:00
|
|
|
#include <memory>
|
|
|
|
|
|
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-06-01 16:02:26 +02:00
|
|
|
Q_NAMESPACE_EXPORT(TASKING_EXPORT)
|
2023-05-29 11:46:20 +02:00
|
|
|
|
2023-11-15 10:33:31 +01:00
|
|
|
// WorkflowPolicy:
|
|
|
|
|
// 1. When all children finished with success -> report success, otherwise:
|
|
|
|
|
// 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.
|
|
|
|
|
// 2. When all children finished with error -> report error, otherwise:
|
|
|
|
|
// a) Report success on first success and stop executing other children (including their subtree).
|
|
|
|
|
// b) On first success - continue executing all children and report success 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.
|
|
|
|
|
// 4. Always run all children, let them finish, ignore their results and report success afterwards.
|
|
|
|
|
// 5. Always run all children, let them finish, ignore their results and report error afterwards.
|
|
|
|
|
|
|
|
|
|
enum class WorkflowPolicy
|
|
|
|
|
{
|
|
|
|
|
StopOnError, // 1a - Reports error on first child error, otherwise success (if all children were success).
|
|
|
|
|
ContinueOnError, // 1b - The same, but children execution continues. Reports success when no children.
|
|
|
|
|
StopOnSuccess, // 2a - Reports success on first child success, otherwise error (if all children were error).
|
|
|
|
|
ContinueOnSuccess, // 2b - The same, but children execution continues. Reports error when no children.
|
|
|
|
|
StopOnSuccessOrError, // 3 - Stops on first finished child and report its result.
|
|
|
|
|
FinishAllAndSuccess, // 4 - Reports success after all children finished.
|
|
|
|
|
FinishAllAndError // 5 - Reports error after all children finished.
|
|
|
|
|
};
|
|
|
|
|
Q_ENUM_NS(WorkflowPolicy);
|
|
|
|
|
|
|
|
|
|
enum class SetupResult
|
|
|
|
|
{
|
|
|
|
|
Continue,
|
|
|
|
|
StopWithSuccess,
|
|
|
|
|
StopWithError
|
|
|
|
|
};
|
|
|
|
|
Q_ENUM_NS(SetupResult);
|
|
|
|
|
|
|
|
|
|
enum class DoneResult
|
|
|
|
|
{
|
|
|
|
|
Success,
|
|
|
|
|
Error
|
|
|
|
|
};
|
|
|
|
|
Q_ENUM_NS(DoneResult);
|
|
|
|
|
|
|
|
|
|
enum class DoneWith
|
|
|
|
|
{
|
|
|
|
|
Success,
|
|
|
|
|
Error,
|
|
|
|
|
Cancel
|
|
|
|
|
};
|
|
|
|
|
Q_ENUM_NS(DoneWith);
|
|
|
|
|
|
|
|
|
|
enum class CallDoneIf
|
|
|
|
|
{
|
|
|
|
|
SuccessOrError,
|
|
|
|
|
Success,
|
|
|
|
|
Error
|
|
|
|
|
};
|
|
|
|
|
Q_ENUM_NS(CallDoneIf);
|
|
|
|
|
|
|
|
|
|
TASKING_EXPORT DoneResult toDoneResult(bool success);
|
|
|
|
|
|
2023-11-07 00:28:06 +01:00
|
|
|
class StorageData;
|
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
|
|
|
|
|
|
|
|
|
|
signals:
|
2023-11-15 10:33:31 +01:00
|
|
|
void done(DoneResult result);
|
2023-06-21 19:09:02 +02:00
|
|
|
|
|
|
|
|
private:
|
2023-10-23 22:41:47 +02:00
|
|
|
template <typename Task, typename Deleter> friend class TaskAdapter;
|
2023-11-07 12:44:11 +01:00
|
|
|
friend class TaskTreePrivate;
|
2023-06-21 19:09:02 +02:00
|
|
|
TaskInterface() = default;
|
2023-08-18 08:08:46 +02:00
|
|
|
#ifdef Q_QDOC
|
|
|
|
|
protected:
|
|
|
|
|
#endif
|
2023-06-21 19:09:02 +02:00
|
|
|
virtual void start() = 0;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
2023-11-19 14:40:49 +01:00
|
|
|
class TASKING_EXPORT StorageBase
|
2022-11-21 12:20:21 +01:00
|
|
|
{
|
2023-11-07 00:28:06 +01:00
|
|
|
private:
|
2023-11-19 14:40:49 +01:00
|
|
|
using StorageConstructor = std::function<void *(void)>;
|
|
|
|
|
using StorageDestructor = std::function<void(void *)>;
|
|
|
|
|
using StorageHandler = std::function<void(void *)>;
|
|
|
|
|
|
|
|
|
|
StorageBase(StorageConstructor ctor, StorageDestructor dtor);
|
2023-11-05 16:55:00 +01:00
|
|
|
|
2023-11-07 00:28:06 +01:00
|
|
|
void *activeStorageVoid() const;
|
2022-11-21 12:20:21 +01:00
|
|
|
|
2023-11-19 14:40:49 +01:00
|
|
|
friend bool operator==(const StorageBase &first, const StorageBase &second)
|
2023-01-05 17:21:37 +01:00
|
|
|
{ return first.m_storageData == second.m_storageData; }
|
|
|
|
|
|
2023-11-19 14:40:49 +01:00
|
|
|
friend bool operator!=(const StorageBase &first, const StorageBase &second)
|
2023-01-05 17:21:37 +01:00
|
|
|
{ return first.m_storageData != second.m_storageData; }
|
|
|
|
|
|
2023-11-19 14:40:49 +01:00
|
|
|
friend size_t qHash(const StorageBase &storage, uint seed = 0)
|
2023-01-05 17:21:37 +01:00
|
|
|
{ return size_t(storage.m_storageData.get()) ^ seed; }
|
|
|
|
|
|
2022-11-21 12:20:21 +01:00
|
|
|
QSharedPointer<StorageData> m_storageData;
|
2023-08-15 13:05:06 +02:00
|
|
|
|
2023-11-19 14:50:55 +01:00
|
|
|
template <typename StorageStruct> friend class Storage;
|
2023-11-07 12:44:11 +01:00
|
|
|
friend class ExecutionContextActivator;
|
2023-11-19 14:40:49 +01:00
|
|
|
friend class StorageData;
|
2024-01-03 18:48:08 +01:00
|
|
|
friend class RuntimeContainer;
|
2023-11-19 14:40:49 +01:00
|
|
|
friend class TaskTree;
|
2023-11-07 12:44:11 +01:00
|
|
|
friend class TaskTreePrivate;
|
2022-11-21 12:20:21 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename StorageStruct>
|
2023-11-19 14:50:55 +01:00
|
|
|
class Storage final : public StorageBase
|
2022-11-21 12:20:21 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2023-11-19 14:50:55 +01:00
|
|
|
Storage() : StorageBase(Storage::ctor(), Storage::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 {
|
2023-11-07 00:28:06 +01:00
|
|
|
return static_cast<StorageStruct *>(activeStorageVoid());
|
2022-11-21 12:20:21 +01:00
|
|
|
}
|
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-05-29 20:16:19 +02:00
|
|
|
class TASKING_EXPORT GroupItem
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2023-11-03 18:50:32 +01:00
|
|
|
// Called when group entered, after group's storages are created
|
2023-06-16 21:33:59 +02:00
|
|
|
using GroupSetupHandler = std::function<SetupResult()>;
|
2023-11-03 18:50:32 +01:00
|
|
|
// Called when group done, before group's storages are deleted
|
2023-11-06 19:17:28 +01:00
|
|
|
using GroupDoneHandler = std::function<DoneResult(DoneWith)>;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-11-22 22:47:13 +01:00
|
|
|
template <typename StorageStruct>
|
|
|
|
|
GroupItem(const Storage<StorageStruct> &storage)
|
2023-11-19 14:18:41 +01:00
|
|
|
: m_type(Type::Storage)
|
|
|
|
|
, m_storageList{storage} {}
|
|
|
|
|
|
2023-12-17 10:35:57 +01:00
|
|
|
// TODO: Add tests.
|
|
|
|
|
GroupItem(const QList<GroupItem> &children) : m_type(Type::List) { addChildren(children); }
|
|
|
|
|
GroupItem(std::initializer_list<GroupItem> children) : m_type(Type::List) { addChildren(children); }
|
|
|
|
|
|
2023-11-16 23:40:22 +01:00
|
|
|
protected:
|
|
|
|
|
// Internal, provided by CustomTask
|
|
|
|
|
using InterfaceCreateHandler = std::function<TaskInterface *(void)>;
|
|
|
|
|
// Called prior to task start, just after createHandler
|
|
|
|
|
using InterfaceSetupHandler = std::function<SetupResult(TaskInterface &)>;
|
|
|
|
|
// Called on task done, just before deleteLater
|
|
|
|
|
using InterfaceDoneHandler = std::function<DoneResult(const TaskInterface &, DoneWith)>;
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
struct TaskHandler {
|
2023-11-16 23:40:22 +01:00
|
|
|
InterfaceCreateHandler m_createHandler;
|
|
|
|
|
InterfaceSetupHandler m_setupHandler = {};
|
|
|
|
|
InterfaceDoneHandler m_doneHandler = {};
|
2023-11-02 18:47:38 +01:00
|
|
|
CallDoneIf m_callDoneIf = CallDoneIf::SuccessOrError;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct GroupHandler {
|
2023-01-20 17:05:33 +01:00
|
|
|
GroupSetupHandler m_setupHandler;
|
2023-11-03 18:50:32 +01:00
|
|
|
GroupDoneHandler m_doneHandler = {};
|
|
|
|
|
CallDoneIf m_callDoneIf = CallDoneIf::SuccessOrError;
|
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
|
|
|
enum class Type {
|
2023-11-03 23:07:26 +01:00
|
|
|
List,
|
2022-10-12 14:30:24 +02:00
|
|
|
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
|
|
|
};
|
|
|
|
|
|
2023-05-29 20:16:19 +02:00
|
|
|
GroupItem() = default;
|
2023-11-03 23:07:26 +01:00
|
|
|
GroupItem(Type type) : m_type(type) { }
|
2023-05-29 20:16:19 +02:00
|
|
|
GroupItem(const GroupData &data)
|
2023-05-18 13:16:40 +02:00
|
|
|
: m_type(Type::GroupData)
|
|
|
|
|
, m_groupData(data) {}
|
2023-05-29 20:16:19 +02:00
|
|
|
GroupItem(const TaskHandler &handler)
|
2023-05-18 13:16:40 +02:00
|
|
|
: m_type(Type::TaskHandler)
|
|
|
|
|
, m_taskHandler(handler) {}
|
2023-05-29 20:16:19 +02:00
|
|
|
void addChildren(const QList<GroupItem> &children);
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-05-29 20:16:19 +02:00
|
|
|
static GroupItem groupHandler(const GroupHandler &handler) { return GroupItem({handler}); }
|
|
|
|
|
static GroupItem parallelLimit(int limit) { return GroupItem({{}, limit}); }
|
|
|
|
|
static GroupItem workflowPolicy(WorkflowPolicy policy) { return GroupItem({{}, {}, policy}); }
|
|
|
|
|
static GroupItem withTimeout(const GroupItem &item, std::chrono::milliseconds timeout,
|
2023-11-03 18:50:32 +01:00
|
|
|
const std::function<void()> &handler = {});
|
2023-05-02 14:01:49 +02:00
|
|
|
|
2023-11-04 17:52:18 +01:00
|
|
|
// Checks if Function may be invoked with Args and if Function's return type is Result.
|
|
|
|
|
template <typename Result, typename Function, typename ...Args,
|
|
|
|
|
typename DecayedFunction = std::decay_t<Function>>
|
|
|
|
|
static constexpr bool isInvocable()
|
|
|
|
|
{
|
|
|
|
|
// Note, that std::is_invocable_r_v doesn't check Result type properly.
|
|
|
|
|
if constexpr (std::is_invocable_r_v<Result, DecayedFunction, Args...>)
|
|
|
|
|
return std::is_same_v<Result, std::invoke_result_t<DecayedFunction, Args...>>;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
private:
|
2024-01-03 18:48:08 +01:00
|
|
|
friend class ContainerNode;
|
2023-11-05 00:25:16 +01:00
|
|
|
friend class TaskNode;
|
2023-11-16 23:40:22 +01:00
|
|
|
friend class TaskTreePrivate;
|
2022-10-12 14:30:24 +02:00
|
|
|
Type m_type = Type::Group;
|
2023-05-29 20:16:19 +02:00
|
|
|
QList<GroupItem> m_children;
|
2023-05-18 13:16:40 +02:00
|
|
|
GroupData m_groupData;
|
2023-11-19 14:40:49 +01:00
|
|
|
QList<StorageBase> m_storageList;
|
2023-05-18 13:16:40 +02:00
|
|
|
TaskHandler m_taskHandler;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
2023-08-01 13:14:53 +02:00
|
|
|
class TASKING_EXPORT Group final : public GroupItem
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2023-05-29 20:16:19 +02:00
|
|
|
Group(const QList<GroupItem> &children) { addChildren(children); }
|
|
|
|
|
Group(std::initializer_list<GroupItem> children) { addChildren(children); }
|
2023-05-17 19:57:23 +02:00
|
|
|
|
2023-05-18 13:16:40 +02:00
|
|
|
// GroupData related:
|
2023-11-03 18:50:32 +01:00
|
|
|
template <typename Handler>
|
|
|
|
|
static GroupItem onGroupSetup(Handler &&handler) {
|
|
|
|
|
return groupHandler({wrapGroupSetup(std::forward<Handler>(handler))});
|
2023-05-17 19:57:23 +02:00
|
|
|
}
|
2023-11-03 18:50:32 +01:00
|
|
|
template <typename Handler>
|
|
|
|
|
static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDoneIf::SuccessOrError) {
|
|
|
|
|
return groupHandler({{}, wrapGroupDone(std::forward<Handler>(handler)), callDoneIf});
|
2023-05-17 19:57:23 +02:00
|
|
|
}
|
2023-05-29 20:16:19 +02:00
|
|
|
using GroupItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel).
|
|
|
|
|
using GroupItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError.
|
2023-05-17 19:57:23 +02:00
|
|
|
|
2023-05-29 20:16:19 +02:00
|
|
|
GroupItem withTimeout(std::chrono::milliseconds timeout,
|
2023-11-03 18:50:32 +01:00
|
|
|
const std::function<void()> &handler = {}) const {
|
2023-05-29 20:16:19 +02:00
|
|
|
return GroupItem::withTimeout(*this, timeout, handler);
|
2023-05-29 00:13:12 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-17 19:57:23 +02:00
|
|
|
private:
|
2023-11-04 17:52:18 +01:00
|
|
|
template <typename Handler>
|
2023-11-03 18:50:32 +01:00
|
|
|
static GroupSetupHandler wrapGroupSetup(Handler &&handler)
|
2023-05-17 19:57:23 +02:00
|
|
|
{
|
2023-11-04 17:52:18 +01:00
|
|
|
// S, V stands for: [S]etupResult, [V]oid
|
|
|
|
|
static constexpr bool isS = isInvocable<SetupResult, Handler>();
|
|
|
|
|
static constexpr bool isV = isInvocable<void, Handler>();
|
|
|
|
|
static_assert(isS || isV,
|
|
|
|
|
"Group setup handler needs to take no arguments and has to return void or SetupResult. "
|
|
|
|
|
"The passed handler doesn't fulfill these requirements.");
|
2023-12-07 16:26:25 +01:00
|
|
|
return [handler] {
|
2023-11-04 17:52:18 +01:00
|
|
|
if constexpr (isS)
|
2023-05-17 19:57:23 +02:00
|
|
|
return std::invoke(handler);
|
|
|
|
|
std::invoke(handler);
|
2023-06-16 21:33:59 +02:00
|
|
|
return SetupResult::Continue;
|
2023-05-17 19:57:23 +02:00
|
|
|
};
|
|
|
|
|
};
|
2023-11-04 17:52:18 +01:00
|
|
|
template <typename Handler>
|
2023-11-03 18:50:32 +01:00
|
|
|
static GroupDoneHandler wrapGroupDone(Handler &&handler)
|
|
|
|
|
{
|
2023-11-06 19:17:28 +01:00
|
|
|
// D, V, W stands for: [D]oneResult, [V]oid, Done[W]ith
|
|
|
|
|
static constexpr bool isDW = isInvocable<DoneResult, Handler, DoneWith>();
|
|
|
|
|
static constexpr bool isD = isInvocable<DoneResult, Handler>();
|
|
|
|
|
static constexpr bool isVW = isInvocable<void, Handler, DoneWith>();
|
2023-11-04 17:52:18 +01:00
|
|
|
static constexpr bool isV = isInvocable<void, Handler>();
|
2023-11-06 19:17:28 +01:00
|
|
|
static_assert(isDW || isD || isVW || isV,
|
2023-11-04 17:52:18 +01:00
|
|
|
"Group done handler needs to take (DoneWith) or (void) as an argument and has to "
|
2023-11-06 19:17:28 +01:00
|
|
|
"return void or DoneResult. The passed handler doesn't fulfill these requirements.");
|
2023-12-07 16:26:25 +01:00
|
|
|
return [handler](DoneWith result) {
|
2023-11-06 19:17:28 +01:00
|
|
|
if constexpr (isDW)
|
2023-11-03 18:50:32 +01:00
|
|
|
return std::invoke(handler, result);
|
2023-11-06 19:17:28 +01:00
|
|
|
if constexpr (isD)
|
2023-11-03 18:50:32 +01:00
|
|
|
return std::invoke(handler);
|
2023-11-06 19:17:28 +01:00
|
|
|
if constexpr (isVW)
|
2023-11-03 18:50:32 +01:00
|
|
|
std::invoke(handler, result);
|
|
|
|
|
else if constexpr (isV)
|
|
|
|
|
std::invoke(handler);
|
2023-11-06 19:17:28 +01:00
|
|
|
return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
|
2023-11-03 18:50:32 +01:00
|
|
|
};
|
|
|
|
|
};
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
2023-11-03 18:50:32 +01:00
|
|
|
template <typename Handler>
|
|
|
|
|
static GroupItem onGroupSetup(Handler &&handler)
|
2023-05-17 19:57:23 +02:00
|
|
|
{
|
2023-11-03 18:50:32 +01:00
|
|
|
return Group::onGroupSetup(std::forward<Handler>(handler));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <typename Handler>
|
|
|
|
|
static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDoneIf::SuccessOrError)
|
|
|
|
|
{
|
|
|
|
|
return Group::onGroupDone(std::forward<Handler>(handler), callDoneIf);
|
2023-05-17 19:57:23 +02:00
|
|
|
}
|
|
|
|
|
|
2023-05-29 20:16:19 +02:00
|
|
|
TASKING_EXPORT GroupItem parallelLimit(int limit);
|
|
|
|
|
TASKING_EXPORT GroupItem workflowPolicy(WorkflowPolicy policy);
|
2023-05-18 13:16:40 +02:00
|
|
|
|
2023-05-29 20:16:19 +02:00
|
|
|
TASKING_EXPORT extern const GroupItem sequential;
|
|
|
|
|
TASKING_EXPORT extern const GroupItem parallel;
|
2023-05-28 22:07:17 +02:00
|
|
|
|
2023-05-29 20:16:19 +02:00
|
|
|
TASKING_EXPORT extern const GroupItem stopOnError;
|
|
|
|
|
TASKING_EXPORT extern const GroupItem continueOnError;
|
2023-11-04 12:44:19 +01:00
|
|
|
TASKING_EXPORT extern const GroupItem stopOnSuccess;
|
|
|
|
|
TASKING_EXPORT extern const GroupItem continueOnSuccess;
|
2023-11-08 13:52:27 +01:00
|
|
|
TASKING_EXPORT extern const GroupItem stopOnSuccessOrError;
|
2023-11-04 12:44:19 +01:00
|
|
|
TASKING_EXPORT extern const GroupItem finishAllAndSuccess;
|
2023-05-29 20:16:19 +02:00
|
|
|
TASKING_EXPORT extern const GroupItem finishAllAndError;
|
2023-05-17 19:57:23 +02:00
|
|
|
|
2023-02-06 20:27:59 +01:00
|
|
|
// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()
|
2023-08-01 13:08:59 +02:00
|
|
|
class TASKING_EXPORT Sync final : public GroupItem
|
2023-02-06 20:27:59 +01:00
|
|
|
{
|
|
|
|
|
public:
|
2023-11-04 17:52:18 +01:00
|
|
|
template <typename Handler>
|
|
|
|
|
Sync(Handler &&handler) {
|
|
|
|
|
addChildren({ onGroupSetup(wrapHandler(std::forward<Handler>(handler))) });
|
|
|
|
|
}
|
2023-04-26 12:33:11 +02:00
|
|
|
|
|
|
|
|
private:
|
2023-11-04 17:52:18 +01:00
|
|
|
template <typename Handler>
|
|
|
|
|
static GroupSetupHandler wrapHandler(Handler &&handler) {
|
2023-11-06 17:48:56 +01:00
|
|
|
// D, V stands for: [D]oneResult, [V]oid
|
|
|
|
|
static constexpr bool isB = isInvocable<DoneResult, Handler>();
|
2023-11-04 17:52:18 +01:00
|
|
|
static constexpr bool isV = isInvocable<void, Handler>();
|
|
|
|
|
static_assert(isB || isV,
|
2023-11-06 17:48:56 +01:00
|
|
|
"Sync handler needs to take no arguments and has to return void or DoneResult. "
|
2023-11-04 17:52:18 +01:00
|
|
|
"The passed handler doesn't fulfill these requirements.");
|
2023-12-07 16:26:25 +01:00
|
|
|
return [handler] {
|
2023-11-04 17:52:18 +01:00
|
|
|
if constexpr (isB) {
|
2023-11-06 17:48:56 +01:00
|
|
|
return std::invoke(handler) == DoneResult::Success ? SetupResult::StopWithSuccess
|
|
|
|
|
: SetupResult::StopWithError;
|
2023-11-04 17:52:18 +01:00
|
|
|
}
|
|
|
|
|
std::invoke(handler);
|
|
|
|
|
return SetupResult::StopWithSuccess;
|
|
|
|
|
};
|
2023-04-26 12:33:11 +02:00
|
|
|
};
|
2023-02-06 20:27:59 +01:00
|
|
|
};
|
|
|
|
|
|
2023-10-23 22:41:47 +02:00
|
|
|
template <typename Task, typename Deleter = std::default_delete<Task>>
|
2022-10-12 14:30:24 +02:00
|
|
|
class TaskAdapter : public TaskInterface
|
|
|
|
|
{
|
2023-06-21 19:09:02 +02:00
|
|
|
protected:
|
2023-10-23 22:41:47 +02:00
|
|
|
TaskAdapter() : m_task(new Task) {}
|
|
|
|
|
Task *task() { return m_task.get(); }
|
|
|
|
|
const Task *task() const { return m_task.get(); }
|
2023-06-21 19:09:02 +02:00
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
private:
|
2023-10-23 22:41:47 +02:00
|
|
|
using TaskType = Task;
|
|
|
|
|
using DeleterType = Deleter;
|
2023-06-21 19:09:02 +02:00
|
|
|
template <typename Adapter> friend class CustomTask;
|
2023-10-23 22:41:47 +02:00
|
|
|
std::unique_ptr<Task, Deleter> m_task;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <typename Adapter>
|
2023-08-01 12:39:52 +02:00
|
|
|
class CustomTask final : public GroupItem
|
2022-10-12 14:30:24 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2023-10-23 22:41:47 +02:00
|
|
|
using Task = typename Adapter::TaskType;
|
|
|
|
|
using Deleter = typename Adapter::DeleterType;
|
|
|
|
|
static_assert(std::is_base_of_v<TaskAdapter<Task, Deleter>, Adapter>,
|
2023-08-01 09:42:56 +02:00
|
|
|
"The Adapter type for the CustomTask<Adapter> needs to be derived from "
|
|
|
|
|
"TaskAdapter<Task>.");
|
2023-11-16 23:40:22 +01:00
|
|
|
using TaskSetupHandler = std::function<SetupResult(Task &)>;
|
|
|
|
|
using TaskDoneHandler = std::function<DoneResult(const Task &, DoneWith)>;
|
2022-10-12 14:30:24 +02:00
|
|
|
static Adapter *createAdapter() { return new Adapter; }
|
|
|
|
|
|
2023-11-16 23:40:22 +01:00
|
|
|
template <typename SetupHandler = TaskSetupHandler, typename DoneHandler = TaskDoneHandler>
|
|
|
|
|
CustomTask(SetupHandler &&setup = TaskSetupHandler(), DoneHandler &&done = TaskDoneHandler(),
|
2023-11-02 18:47:38 +01:00
|
|
|
CallDoneIf callDoneIf = CallDoneIf::SuccessOrError)
|
2023-11-02 19:10:55 +01:00
|
|
|
: GroupItem({&createAdapter, wrapSetup(std::forward<SetupHandler>(setup)),
|
|
|
|
|
wrapDone(std::forward<DoneHandler>(done)), callDoneIf})
|
2023-10-31 17:03:48 +01:00
|
|
|
{}
|
|
|
|
|
|
2023-05-29 20:16:19 +02:00
|
|
|
GroupItem withTimeout(std::chrono::milliseconds timeout,
|
2023-11-03 18:50:32 +01:00
|
|
|
const std::function<void()> &handler = {}) const
|
|
|
|
|
{
|
2023-05-29 20:16:19 +02:00
|
|
|
return GroupItem::withTimeout(*this, timeout, handler);
|
2023-05-29 00:13:12 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-12 14:30:24 +02:00
|
|
|
private:
|
2023-11-04 17:52:18 +01:00
|
|
|
template <typename Handler>
|
2023-11-16 23:40:22 +01:00
|
|
|
static InterfaceSetupHandler wrapSetup(Handler &&handler) {
|
|
|
|
|
if constexpr (std::is_same_v<Handler, TaskSetupHandler>)
|
2023-11-02 19:10:55 +01:00
|
|
|
return {}; // When user passed {} for the setup handler.
|
2023-11-04 17:52:18 +01:00
|
|
|
// S, V stands for: [S]etupResult, [V]oid
|
|
|
|
|
static constexpr bool isS = isInvocable<SetupResult, Handler, Task &>();
|
|
|
|
|
static constexpr bool isV = isInvocable<void, Handler, Task &>();
|
|
|
|
|
static_assert(isS || isV,
|
|
|
|
|
"Task setup handler needs to take (Task &) as an argument and has to return void or "
|
|
|
|
|
"SetupResult. The passed handler doesn't fulfill these requirements.");
|
2023-12-07 16:26:25 +01:00
|
|
|
return [handler](TaskInterface &taskInterface) {
|
2023-01-09 20:52:12 +01:00
|
|
|
Adapter &adapter = static_cast<Adapter &>(taskInterface);
|
2023-11-04 17:52:18 +01:00
|
|
|
if constexpr (isS)
|
2023-06-20 18:54:38 +02:00
|
|
|
return std::invoke(handler, *adapter.task());
|
|
|
|
|
std::invoke(handler, *adapter.task());
|
2023-06-16 21:33:59 +02:00
|
|
|
return SetupResult::Continue;
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
};
|
2023-01-09 20:52:12 +01:00
|
|
|
|
2023-11-04 17:52:18 +01:00
|
|
|
template <typename Handler>
|
2023-11-16 23:40:22 +01:00
|
|
|
static InterfaceDoneHandler wrapDone(Handler &&handler) {
|
|
|
|
|
if constexpr (std::is_same_v<Handler, TaskDoneHandler>)
|
2023-11-02 19:10:55 +01:00
|
|
|
return {}; // When user passed {} for the done handler.
|
2023-11-06 18:52:19 +01:00
|
|
|
// D, V, T, W stands for: [D]oneResult, [V]oid, [T]ask, done[W]ith
|
|
|
|
|
static constexpr bool isDTW = isInvocable<DoneResult, Handler, const Task &, DoneWith>();
|
|
|
|
|
static constexpr bool isDT = isInvocable<DoneResult, Handler, const Task &>();
|
|
|
|
|
static constexpr bool isDW = isInvocable<DoneResult, Handler, DoneWith>();
|
|
|
|
|
static constexpr bool isD = isInvocable<DoneResult, Handler>();
|
|
|
|
|
static constexpr bool isVTW = isInvocable<void, Handler, const Task &, DoneWith>();
|
2023-11-04 17:52:18 +01:00
|
|
|
static constexpr bool isVT = isInvocable<void, Handler, const Task &>();
|
2023-11-06 18:52:19 +01:00
|
|
|
static constexpr bool isVW = isInvocable<void, Handler, DoneWith>();
|
2023-11-04 17:52:18 +01:00
|
|
|
static constexpr bool isV = isInvocable<void, Handler>();
|
2023-11-06 18:52:19 +01:00
|
|
|
static_assert(isDTW || isDT || isDW || isD || isVTW || isVT || isVW || isV,
|
2023-11-03 14:42:55 +01:00
|
|
|
"Task done handler needs to take (const Task &, DoneWith), (const Task &), "
|
2023-11-06 18:52:19 +01:00
|
|
|
"(DoneWith) or (void) as arguments and has to return void or DoneResult. "
|
2023-11-03 14:42:55 +01:00
|
|
|
"The passed handler doesn't fulfill these requirements.");
|
2023-12-07 16:26:25 +01:00
|
|
|
return [handler](const TaskInterface &taskInterface, DoneWith result) {
|
2023-10-31 17:03:48 +01:00
|
|
|
const Adapter &adapter = static_cast<const Adapter &>(taskInterface);
|
2023-11-06 18:52:19 +01:00
|
|
|
if constexpr (isDTW)
|
2023-11-03 09:42:55 +01:00
|
|
|
return std::invoke(handler, *adapter.task(), result);
|
2023-11-06 18:52:19 +01:00
|
|
|
if constexpr (isDT)
|
2023-11-02 19:10:55 +01:00
|
|
|
return std::invoke(handler, *adapter.task());
|
2023-11-06 18:52:19 +01:00
|
|
|
if constexpr (isDW)
|
2023-11-03 14:42:55 +01:00
|
|
|
return std::invoke(handler, result);
|
2023-11-06 18:52:19 +01:00
|
|
|
if constexpr (isD)
|
2023-11-02 19:10:55 +01:00
|
|
|
return std::invoke(handler);
|
2023-11-06 18:52:19 +01:00
|
|
|
if constexpr (isVTW)
|
2023-11-03 09:42:55 +01:00
|
|
|
std::invoke(handler, *adapter.task(), result);
|
2023-11-03 14:42:55 +01:00
|
|
|
else if constexpr (isVT)
|
2023-11-02 19:10:55 +01:00
|
|
|
std::invoke(handler, *adapter.task());
|
2023-11-06 18:52:19 +01:00
|
|
|
else if constexpr (isVW)
|
2023-11-03 14:42:55 +01:00
|
|
|
std::invoke(handler, result);
|
|
|
|
|
else if constexpr (isV)
|
2023-11-02 19:10:55 +01:00
|
|
|
std::invoke(handler);
|
2023-11-06 18:52:19 +01:00
|
|
|
return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
|
2023-10-31 17:03:48 +01:00
|
|
|
};
|
|
|
|
|
};
|
2022-10-12 14:30:24 +02:00
|
|
|
};
|
|
|
|
|
|
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-06-06 11:37:27 +02:00
|
|
|
TaskTree(const Group &recipe);
|
2022-10-12 14:30:24 +02:00
|
|
|
~TaskTree();
|
|
|
|
|
|
2023-06-06 11:37:27 +02:00
|
|
|
void setRecipe(const Group &recipe);
|
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.
|
2023-11-06 20:08:42 +01:00
|
|
|
DoneWith runBlocking();
|
|
|
|
|
DoneWith runBlocking(const QFuture<void> &future);
|
|
|
|
|
static DoneWith runBlocking(const Group &recipe,
|
|
|
|
|
std::chrono::milliseconds timeout = std::chrono::milliseconds::max());
|
|
|
|
|
static DoneWith runBlocking(const Group &recipe, const QFuture<void> &future,
|
|
|
|
|
std::chrono::milliseconds timeout = std::chrono::milliseconds::max());
|
2023-05-15 10:52:45 +02:00
|
|
|
|
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
|
|
|
|
2023-11-19 14:40:49 +01:00
|
|
|
template <typename StorageStruct, typename Handler>
|
2023-11-19 14:50:55 +01:00
|
|
|
void onStorageSetup(const Storage<StorageStruct> &storage, Handler &&handler) {
|
2023-11-19 14:40:49 +01:00
|
|
|
static_assert(std::is_invocable_v<std::decay_t<Handler>, StorageStruct &>,
|
2023-08-31 19:25:59 +02:00
|
|
|
"Storage setup handler needs to take (Storage &) as an argument. "
|
2023-11-04 17:52:18 +01:00
|
|
|
"The passed handler doesn't fulfill this requirement.");
|
2023-01-20 15:13:42 +01:00
|
|
|
setupStorageHandler(storage,
|
2023-11-19 14:40:49 +01:00
|
|
|
wrapHandler<StorageStruct>(std::forward<Handler>(handler)), {});
|
2022-12-05 11:20:57 +01:00
|
|
|
}
|
2023-11-19 14:40:49 +01:00
|
|
|
template <typename StorageStruct, typename Handler>
|
2023-11-19 14:50:55 +01:00
|
|
|
void onStorageDone(const Storage<StorageStruct> &storage, Handler &&handler) {
|
2023-11-19 14:40:49 +01:00
|
|
|
static_assert(std::is_invocable_v<std::decay_t<Handler>, const StorageStruct &>,
|
2023-08-31 19:25:59 +02:00
|
|
|
"Storage done handler needs to take (const Storage &) as an argument. "
|
2023-11-04 17:52:18 +01:00
|
|
|
"The passed handler doesn't fulfill this requirement.");
|
|
|
|
|
setupStorageHandler(storage, {},
|
2023-11-19 14:40:49 +01:00
|
|
|
wrapHandler<const StorageStruct>(std::forward<Handler>(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();
|
2023-11-03 15:53:01 +01:00
|
|
|
void done(DoneWith result);
|
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:
|
2023-11-19 14:40:49 +01:00
|
|
|
void setupStorageHandler(const StorageBase &storage,
|
|
|
|
|
StorageBase::StorageHandler setupHandler,
|
|
|
|
|
StorageBase::StorageHandler doneHandler);
|
|
|
|
|
template <typename StorageStruct, typename Handler>
|
|
|
|
|
StorageBase::StorageHandler wrapHandler(Handler &&handler) {
|
2023-12-07 16:26:25 +01:00
|
|
|
return [handler](void *voidStruct) {
|
2023-11-04 17:52:18 +01:00
|
|
|
auto *storageStruct = static_cast<StorageStruct *>(voidStruct);
|
2023-08-31 18:31:34 +02:00
|
|
|
std::invoke(handler, *storageStruct);
|
2022-12-05 11:20:57 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
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();
|
2023-11-22 22:47:13 +01:00
|
|
|
|
|
|
|
|
private:
|
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();
|
|
|
|
|
|
|
|
|
|
private:
|
2023-11-22 22:47:13 +01:00
|
|
|
void start() final;
|
2023-05-31 18:29:11 +02:00
|
|
|
std::optional<int> m_timerId;
|
2023-05-28 22:47:59 +02:00
|
|
|
};
|
|
|
|
|
|
2023-08-17 11:08:21 +02:00
|
|
|
using TaskTreeTask = CustomTask<TaskTreeTaskAdapter>;
|
|
|
|
|
using TimeoutTask = CustomTask<TimeoutTaskAdapter>;
|
2022-10-12 14:30:24 +02:00
|
|
|
|
2023-05-10 19:54:52 +02:00
|
|
|
} // namespace Tasking
|