AssetDownloader: Sync TaskTree sources
Change-Id: Ib3727dbca7b27e796b6fab084cbaaa9890b78118 Reviewed-by: Kai Köhne <kai.koehne@qt.io> (cherry picked from commit f4db5ce8a8e6994b4dce407977f4e936dcd7f48e) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
d62a2decaf
commit
5fa620173e
@ -9,11 +9,13 @@ qt_internal_add_module(ExamplesAssetDownloaderPrivate
|
||||
assetdownloader.cpp assetdownloader.h
|
||||
tasking/barrier.cpp tasking/barrier.h
|
||||
tasking/concurrentcall.h
|
||||
tasking/conditional.cpp tasking/conditional.h
|
||||
tasking/networkquery.cpp tasking/networkquery.h
|
||||
tasking/qprocesstask.cpp tasking/qprocesstask.h
|
||||
tasking/tasking_global.h
|
||||
tasking/tasktree.cpp tasking/tasktree.h
|
||||
tasking/tasktreerunner.cpp tasking/tasktreerunner.h
|
||||
tasking/tcpsocket.cpp tasking/tcpsocket.h
|
||||
DEFINES
|
||||
QT_NO_CAST_FROM_ASCII
|
||||
PUBLIC_LIBRARIES
|
||||
|
@ -537,9 +537,9 @@ void AssetDownloader::start()
|
||||
onGroupSetup(onSkipIfAllAssetsPresent),
|
||||
NetworkQueryTask(onZipDownloadSetup, onZipDownloadDone),
|
||||
ConcurrentCallTask<void>(onUnzipSetup, onUnzipDone),
|
||||
Group {
|
||||
parallelIdealThreadCountLimit,
|
||||
For {
|
||||
downloadIterator,
|
||||
parallelIdealThreadCountLimit,
|
||||
onGroupSetup(onAssetsDownloadGroupSetup),
|
||||
Group {
|
||||
assetStorage,
|
||||
@ -547,9 +547,9 @@ void AssetDownloader::start()
|
||||
ConcurrentCallTask<void>(onAssetWriteSetup, onAssetWriteDone)
|
||||
}
|
||||
},
|
||||
Group {
|
||||
parallelIdealThreadCountLimit,
|
||||
For {
|
||||
copyIterator,
|
||||
parallelIdealThreadCountLimit,
|
||||
onGroupSetup(onAssetsCopyGroupSetup),
|
||||
ConcurrentCallTask<void>(onAssetCopySetup, onAssetCopyDone, CallDoneIf::Success),
|
||||
onGroupDone(onAssetsCopyGroupDone)
|
||||
|
@ -82,7 +82,7 @@ using MultiBarrier = Storage<SharedBarrier<Limit>>;
|
||||
using SingleBarrier = MultiBarrier<1>;
|
||||
|
||||
template <int Limit>
|
||||
GroupItem waitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
|
||||
ExecutableItem waitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
|
||||
{
|
||||
return BarrierTask([sharedBarrier](Barrier &barrier) {
|
||||
SharedBarrier<Limit> *activeBarrier = sharedBarrier.activeStorage();
|
||||
|
108
src/assets/downloader/tasking/conditional.cpp
Normal file
108
src/assets/downloader/tasking/conditional.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright (C) 2024 Jarek Kobus
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include "conditional.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
static Group conditionRecipe(const Storage<bool> &bodyExecutedStorage,
|
||||
const ConditionData &condition)
|
||||
{
|
||||
Storage<bool> skipContinuationStorage;
|
||||
|
||||
const auto onSetup = [bodyExecutedStorage] {
|
||||
return *bodyExecutedStorage ? SetupResult::StopWithSuccess : SetupResult::Continue;
|
||||
};
|
||||
|
||||
const auto onConditionDone = [skipContinuationStorage](DoneWith result) {
|
||||
*skipContinuationStorage = result != DoneWith::Success;
|
||||
return DoneResult::Success;
|
||||
};
|
||||
|
||||
const auto onContinuationSetup = [skipContinuationStorage, bodyExecutedStorage] {
|
||||
*bodyExecutedStorage = !*skipContinuationStorage;
|
||||
return *skipContinuationStorage ? SetupResult::StopWithSuccess : SetupResult::Continue;
|
||||
};
|
||||
|
||||
return {
|
||||
skipContinuationStorage,
|
||||
onGroupSetup(onSetup),
|
||||
condition.m_condition ? Group {
|
||||
*condition.m_condition,
|
||||
onGroupDone(onConditionDone)
|
||||
} : nullItem,
|
||||
Group {
|
||||
onGroupSetup(onContinuationSetup),
|
||||
condition.m_body
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static ExecutableItem conditionsRecipe(const QList<ConditionData> &conditions)
|
||||
{
|
||||
Storage<bool> bodyExecutedStorage;
|
||||
|
||||
QList<GroupItem> recipes;
|
||||
for (const ConditionData &condition : conditions)
|
||||
recipes << conditionRecipe(bodyExecutedStorage, condition);
|
||||
|
||||
return Group { bodyExecutedStorage, recipes };
|
||||
}
|
||||
|
||||
ThenItem::operator ExecutableItem() const
|
||||
{
|
||||
return conditionsRecipe(m_conditions);
|
||||
}
|
||||
|
||||
ThenItem::ThenItem(const If &ifItem, const Then &thenItem)
|
||||
: m_conditions{{ifItem.m_condition, thenItem.m_body}}
|
||||
{}
|
||||
|
||||
ThenItem::ThenItem(const ElseIfItem &elseIfItem, const Then &thenItem)
|
||||
: m_conditions(elseIfItem.m_conditions)
|
||||
{
|
||||
m_conditions.append({elseIfItem.m_nextCondition, thenItem.m_body});
|
||||
}
|
||||
|
||||
ElseItem::operator ExecutableItem() const
|
||||
{
|
||||
return conditionsRecipe(m_conditions);
|
||||
}
|
||||
|
||||
ElseItem::ElseItem(const ThenItem &thenItem, const Else &elseItem)
|
||||
: m_conditions(thenItem.m_conditions)
|
||||
{
|
||||
m_conditions.append({{}, elseItem.m_body});
|
||||
}
|
||||
|
||||
ElseIfItem::ElseIfItem(const ThenItem &thenItem, const ElseIf &elseIfItem)
|
||||
: m_conditions(thenItem.m_conditions)
|
||||
, m_nextCondition(elseIfItem.m_condition)
|
||||
{}
|
||||
|
||||
ThenItem operator>>(const If &ifItem, const Then &thenItem)
|
||||
{
|
||||
return {ifItem, thenItem};
|
||||
}
|
||||
|
||||
ThenItem operator>>(const ElseIfItem &elseIfItem, const Then &thenItem)
|
||||
{
|
||||
return {elseIfItem, thenItem};
|
||||
}
|
||||
|
||||
ElseIfItem operator>>(const ThenItem &thenItem, const ElseIf &elseIfItem)
|
||||
{
|
||||
return {thenItem, elseIfItem};
|
||||
}
|
||||
|
||||
ElseItem operator>>(const ThenItem &thenItem, const Else &elseItem)
|
||||
{
|
||||
return {thenItem, elseItem};
|
||||
}
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
QT_END_NAMESPACE
|
142
src/assets/downloader/tasking/conditional.h
Normal file
142
src/assets/downloader/tasking/conditional.h
Normal file
@ -0,0 +1,142 @@
|
||||
// Copyright (C) 2024 Jarek Kobus
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef TASKING_CONDITIONAL_H
|
||||
#define TASKING_CONDITIONAL_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "tasking_global.h"
|
||||
|
||||
#include "tasktree.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
class Then;
|
||||
class ThenItem;
|
||||
class ElseItem;
|
||||
class ElseIfItem;
|
||||
|
||||
class TASKING_EXPORT If
|
||||
{
|
||||
public:
|
||||
explicit If(const ExecutableItem &condition) : m_condition(condition) {}
|
||||
|
||||
template <typename Handler,
|
||||
std::enable_if_t<!std::is_base_of_v<ExecutableItem, std::decay_t<Handler>>, bool> = true>
|
||||
explicit If(Handler &&handler) : m_condition(Sync(std::forward<Handler>(handler))) {}
|
||||
|
||||
private:
|
||||
TASKING_EXPORT friend ThenItem operator>>(const If &ifItem, const Then &thenItem);
|
||||
|
||||
friend class ThenItem;
|
||||
ExecutableItem m_condition;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT ElseIf
|
||||
{
|
||||
public:
|
||||
explicit ElseIf(const ExecutableItem &condition) : m_condition(condition) {}
|
||||
|
||||
template <typename Handler,
|
||||
std::enable_if_t<!std::is_base_of_v<ExecutableItem, std::decay_t<Handler>>, bool> = true>
|
||||
explicit ElseIf(Handler &&handler) : m_condition(Sync(std::forward<Handler>(handler))) {}
|
||||
|
||||
private:
|
||||
friend class ElseIfItem;
|
||||
ExecutableItem m_condition;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT Else
|
||||
{
|
||||
public:
|
||||
explicit Else(const QList<GroupItem> &children) : m_body({children}) {}
|
||||
explicit Else(std::initializer_list<GroupItem> children) : m_body({children}) {}
|
||||
|
||||
private:
|
||||
friend class ElseItem;
|
||||
Group m_body;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT Then
|
||||
{
|
||||
public:
|
||||
explicit Then(const QList<GroupItem> &children) : m_body({children}) {}
|
||||
explicit Then(std::initializer_list<GroupItem> children) : m_body({children}) {}
|
||||
|
||||
private:
|
||||
friend class ThenItem;
|
||||
Group m_body;
|
||||
};
|
||||
|
||||
class ConditionData
|
||||
{
|
||||
public:
|
||||
std::optional<ExecutableItem> m_condition;
|
||||
Group m_body;
|
||||
};
|
||||
|
||||
class ElseIfItem;
|
||||
|
||||
class TASKING_EXPORT ThenItem
|
||||
{
|
||||
public:
|
||||
operator ExecutableItem() const;
|
||||
|
||||
private:
|
||||
ThenItem(const If &ifItem, const Then &thenItem);
|
||||
ThenItem(const ElseIfItem &elseIfItem, const Then &thenItem);
|
||||
|
||||
TASKING_EXPORT friend ElseItem operator>>(const ThenItem &thenItem, const Else &elseItem);
|
||||
TASKING_EXPORT friend ElseIfItem operator>>(const ThenItem &thenItem, const ElseIf &elseIfItem);
|
||||
TASKING_EXPORT friend ThenItem operator>>(const If &ifItem, const Then &thenItem);
|
||||
TASKING_EXPORT friend ThenItem operator>>(const ElseIfItem &elseIfItem, const Then &thenItem);
|
||||
|
||||
friend class ElseItem;
|
||||
friend class ElseIfItem;
|
||||
QList<ConditionData> m_conditions;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT ElseItem
|
||||
{
|
||||
public:
|
||||
operator ExecutableItem() const;
|
||||
|
||||
private:
|
||||
ElseItem(const ThenItem &thenItem, const Else &elseItem);
|
||||
|
||||
TASKING_EXPORT friend ElseItem operator>>(const ThenItem &thenItem, const Else &elseItem);
|
||||
|
||||
QList<ConditionData> m_conditions;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT ElseIfItem
|
||||
{
|
||||
private:
|
||||
ElseIfItem(const ThenItem &thenItem, const ElseIf &elseIfItem);
|
||||
|
||||
TASKING_EXPORT friend ThenItem operator>>(const ElseIfItem &elseIfItem, const Then &thenItem);
|
||||
TASKING_EXPORT friend ElseIfItem operator>>(const ThenItem &thenItem, const ElseIf &elseIfItem);
|
||||
|
||||
friend class ThenItem;
|
||||
QList<ConditionData> m_conditions;
|
||||
ExecutableItem m_nextCondition;
|
||||
};
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // TASKING_CONDITIONAL_H
|
@ -629,19 +629,19 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias CustomTask::Task
|
||||
\typealias Tasking::CustomTask::Task
|
||||
|
||||
Type alias for the task type associated with the custom task's \c Adapter.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias CustomTask::Deleter
|
||||
\typealias Tasking::CustomTask::Deleter
|
||||
|
||||
Type alias for the task's type deleter associated with the custom task's \c Adapter.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias CustomTask::TaskSetupHandler
|
||||
\typealias Tasking::CustomTask::TaskSetupHandler
|
||||
|
||||
Type alias for \c std::function<SetupResult(Task &)>.
|
||||
|
||||
@ -676,9 +676,9 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias CustomTask::TaskDoneHandler
|
||||
\typealias Tasking::CustomTask::TaskDoneHandler
|
||||
|
||||
Type alias for \c std::function<DoneResult(const Task &, DoneWith)>.
|
||||
Type alias for \c std::function<DoneResult(const Task &, DoneWith)> or DoneResult.
|
||||
|
||||
The \c TaskDoneHandler is an optional argument of a custom task element's constructor.
|
||||
Any function with the above signature, when passed as a task done handler,
|
||||
@ -703,6 +703,9 @@ private:
|
||||
the DoneWith argument. When the handler returns the DoneResult value,
|
||||
the task's final result may be tweaked inside the done handler's body by the returned value.
|
||||
|
||||
For a \c TaskDoneHandler of the DoneResult type, no additional handling is executed,
|
||||
and the task finishes unconditionally with the passed value of DoneResult.
|
||||
|
||||
\sa CustomTask(), TaskSetupHandler, GroupDoneHandler
|
||||
*/
|
||||
|
||||
@ -862,7 +865,83 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable sequential
|
||||
\variable Tasking::nullItem
|
||||
|
||||
A convenient global group's element indicating a no-op item.
|
||||
|
||||
This is useful in conditional expressions to indicate the absence of an optional element:
|
||||
|
||||
\code
|
||||
const ExecutableItem task = ...;
|
||||
const std::optional<ExecutableItem> optionalTask = ...;
|
||||
|
||||
Group group {
|
||||
task,
|
||||
optionalTask ? *optionalTask : nullItem
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable Tasking::successItem
|
||||
|
||||
A convenient global executable element containing an empty, successful, synchronous task.
|
||||
|
||||
This is useful in if-statements to indicate that a branch ends with success:
|
||||
|
||||
\code
|
||||
const ExecutableItem conditionalTask = ...;
|
||||
|
||||
Group group {
|
||||
stopOnDone,
|
||||
If (conditionalTask) >> Then {
|
||||
...
|
||||
} >> Else {
|
||||
successItem
|
||||
},
|
||||
nextTask
|
||||
};
|
||||
\endcode
|
||||
|
||||
In the above example, if the \c conditionalTask finishes with an error, the \c Else branch
|
||||
is chosen, which finishes immediately with success. This causes the \c nextTask to be skipped
|
||||
(because of the stopOnDone workflow policy of the \c group)
|
||||
and the \c group finishes with success.
|
||||
|
||||
\sa errorItem
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable Tasking::errorItem
|
||||
|
||||
A convenient global executable element containing an empty, erroneous, synchronous task.
|
||||
|
||||
This is useful in if-statements to indicate that a branch ends with an error:
|
||||
|
||||
\code
|
||||
const ExecutableItem conditionalTask = ...;
|
||||
|
||||
Group group {
|
||||
stopOnError,
|
||||
If (conditionalTask) >> Then {
|
||||
...
|
||||
} >> Else {
|
||||
errorItem
|
||||
},
|
||||
nextTask
|
||||
};
|
||||
\endcode
|
||||
|
||||
In the above example, if the \c conditionalTask finishes with an error, the \c Else branch
|
||||
is chosen, which finishes immediately with an error. This causes the \c nextTask to be skipped
|
||||
(because of the stopOnError workflow policy of the \c group)
|
||||
and the \c group finishes with an error.
|
||||
|
||||
\sa successItem
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable Tasking::sequential
|
||||
A convenient global group's element describing the sequential execution mode.
|
||||
|
||||
This is the default execution mode of the Group element.
|
||||
@ -877,7 +956,7 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable parallel
|
||||
\variable Tasking::parallel
|
||||
A convenient global group's element describing the parallel execution mode.
|
||||
|
||||
All the direct child tasks of a group are started after the group is started,
|
||||
@ -888,7 +967,7 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable parallelIdealThreadCountLimit
|
||||
\variable Tasking::parallelIdealThreadCountLimit
|
||||
A convenient global group's element describing the parallel execution mode with a limited
|
||||
number of tasks running simultanously. The limit is equal to the ideal number of threads
|
||||
excluding the calling thread.
|
||||
@ -902,39 +981,39 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable stopOnError
|
||||
\variable Tasking::stopOnError
|
||||
A convenient global group's element describing the StopOnError workflow policy.
|
||||
|
||||
This is the default workflow policy of the Group element.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable continueOnError
|
||||
\variable Tasking::continueOnError
|
||||
A convenient global group's element describing the ContinueOnError workflow policy.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable stopOnSuccess
|
||||
\variable Tasking::stopOnSuccess
|
||||
A convenient global group's element describing the StopOnSuccess workflow policy.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable continueOnSuccess
|
||||
\variable Tasking::continueOnSuccess
|
||||
A convenient global group's element describing the ContinueOnSuccess workflow policy.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable stopOnSuccessOrError
|
||||
\variable Tasking::stopOnSuccessOrError
|
||||
A convenient global group's element describing the StopOnSuccessOrError workflow policy.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable finishAllAndSuccess
|
||||
\variable Tasking::finishAllAndSuccess
|
||||
A convenient global group's element describing the FinishAllAndSuccess workflow policy.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\variable finishAllAndError
|
||||
\variable Tasking::finishAllAndError
|
||||
A convenient global group's element describing the FinishAllAndError workflow policy.
|
||||
*/
|
||||
|
||||
@ -1025,7 +1104,7 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias GroupItem::GroupSetupHandler
|
||||
\typealias Tasking::GroupItem::GroupSetupHandler
|
||||
|
||||
Type alias for \c std::function<SetupResult()>.
|
||||
|
||||
@ -1055,9 +1134,9 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias GroupItem::GroupDoneHandler
|
||||
\typealias Tasking::GroupItem::GroupDoneHandler
|
||||
|
||||
Type alias for \c std::function<DoneResult(DoneWith)>.
|
||||
Type alias for \c std::function<DoneResult(DoneWith)> or DoneResult.
|
||||
|
||||
The \c GroupDoneHandler is an argument of the onGroupDone() element.
|
||||
Any function with the above signature, when passed as a group done handler,
|
||||
@ -1072,6 +1151,10 @@ private:
|
||||
the DoneWith argument. When the handler returns the DoneResult value,
|
||||
the group's final result may be tweaked inside the done handler's body by the returned value.
|
||||
|
||||
For a \c GroupDoneHandler of the DoneResult type, no additional handling is executed,
|
||||
and the group finishes unconditionally with the passed value of DoneResult,
|
||||
ignoring the group's workflow policy.
|
||||
|
||||
\sa onGroupDone(), GroupSetupHandler, CustomTask::TaskDoneHandler
|
||||
*/
|
||||
|
||||
@ -1159,9 +1242,9 @@ private:
|
||||
|
||||
\sa sequential, parallel
|
||||
*/
|
||||
GroupItem parallelLimit(int limit)
|
||||
GroupItem ParallelLimitFunctor::operator()(int limit) const
|
||||
{
|
||||
return Group::parallelLimit(qMax(limit, 0));
|
||||
return GroupItem({{}, limit});
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -1172,12 +1255,13 @@ GroupItem parallelLimit(int limit)
|
||||
\sa stopOnError, continueOnError, stopOnSuccess, continueOnSuccess, stopOnSuccessOrError,
|
||||
finishAllAndSuccess, finishAllAndError, WorkflowPolicy
|
||||
*/
|
||||
GroupItem workflowPolicy(WorkflowPolicy policy)
|
||||
GroupItem WorkflowPolicyFunctor::operator()(WorkflowPolicy policy) const
|
||||
{
|
||||
return Group::workflowPolicy(policy);
|
||||
return GroupItem({{}, {}, policy});
|
||||
}
|
||||
|
||||
const GroupItem nullItem = GroupItem({});
|
||||
const ParallelLimitFunctor parallelLimit = ParallelLimitFunctor();
|
||||
const WorkflowPolicyFunctor workflowPolicy = WorkflowPolicyFunctor();
|
||||
|
||||
const GroupItem sequential = parallelLimit(1);
|
||||
const GroupItem parallel = parallelLimit(0);
|
||||
@ -1191,6 +1275,11 @@ const GroupItem stopOnSuccessOrError = workflowPolicy(WorkflowPolicy::StopOnSucc
|
||||
const GroupItem finishAllAndSuccess = workflowPolicy(WorkflowPolicy::FinishAllAndSuccess);
|
||||
const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError);
|
||||
|
||||
// Keep below the above in order to avoid static initialization fiasco.
|
||||
const GroupItem nullItem = GroupItem({});
|
||||
const ExecutableItem successItem = Group { finishAllAndSuccess };
|
||||
const ExecutableItem errorItem = Group { finishAllAndError };
|
||||
|
||||
// Please note the thread_local keyword below guarantees a separate instance per thread.
|
||||
// The s_activeTaskTrees is currently used internally only and is not exposed in the public API.
|
||||
// It serves for withLog() implementation now. Add a note here when a new usage is introduced.
|
||||
@ -1292,7 +1381,7 @@ const void *Loop::valuePtr() const
|
||||
|
||||
using StoragePtr = void *;
|
||||
|
||||
constexpr QLatin1StringView s_activeStorageWarning =
|
||||
static constexpr QLatin1StringView s_activeStorageWarning =
|
||||
"The referenced storage is not reachable in the running tree. "
|
||||
"A nullptr will be returned which might lead to a crash in the calling code. "
|
||||
"It is possible that no storage was added to the tree, "
|
||||
@ -1503,6 +1592,128 @@ ExecutableItem ExecutableItem::withLog(const QString &logName) const
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator!(const ExecutableItem &item)
|
||||
|
||||
Returns an ExecutableItem with the DoneResult of \a item negated.
|
||||
|
||||
If \a item reports DoneResult::Success, the returned item reports DoneResult::Error.
|
||||
If \a item reports DoneResult::Error, the returned item reports DoneResult::Success.
|
||||
|
||||
The returned item is equivalent to:
|
||||
\code
|
||||
Group {
|
||||
item,
|
||||
onGroupDone([](DoneWith doneWith) { return toDoneResult(doneWith == DoneWith::Error); })
|
||||
}
|
||||
\endcode
|
||||
|
||||
\sa operator&&(), operator||()
|
||||
*/
|
||||
ExecutableItem operator!(const ExecutableItem &item)
|
||||
{
|
||||
return Group {
|
||||
item,
|
||||
onGroupDone([](DoneWith doneWith) { return toDoneResult(doneWith == DoneWith::Error); })
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator&&(const ExecutableItem &first, const ExecutableItem &second)
|
||||
|
||||
Returns an ExecutableItem with \a first and \a second tasks merged with conjunction.
|
||||
|
||||
Both \a first and \a second tasks execute in sequence.
|
||||
If both tasks report DoneResult::Success, the returned item reports DoneResult::Success.
|
||||
Otherwise, the returned item reports DoneResult::Error.
|
||||
|
||||
The returned item is
|
||||
\l {https://en.wikipedia.org/wiki/Short-circuit_evaluation}{short-circuiting}:
|
||||
if the \a first task reports DoneResult::Error, the \a second task is skipped,
|
||||
and the returned item reports DoneResult::Error immediately.
|
||||
|
||||
The returned item is equivalent to:
|
||||
\code
|
||||
Group { stopOnError, first, second }
|
||||
\endcode
|
||||
|
||||
\note Parallel execution of conjunction in a short-circuit manner can be achieved with the
|
||||
following code: \c {Group { parallel, stopOnError, first, second }}. In this case:
|
||||
if the \e {first finished} task reports DoneResult::Error,
|
||||
the \e other task is canceled, and the group reports DoneResult::Error immediately.
|
||||
|
||||
\sa operator||(), operator!()
|
||||
*/
|
||||
ExecutableItem operator&&(const ExecutableItem &first, const ExecutableItem &second)
|
||||
{
|
||||
return Group { stopOnError, first, second };
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator||(const ExecutableItem &first, const ExecutableItem &second)
|
||||
|
||||
Returns an ExecutableItem with \a first and \a second tasks merged with disjunction.
|
||||
|
||||
Both \a first and \a second tasks execute in sequence.
|
||||
If both tasks report DoneResult::Error, the returned item reports DoneResult::Error.
|
||||
Otherwise, the returned item reports DoneResult::Success.
|
||||
|
||||
The returned item is
|
||||
\l {https://en.wikipedia.org/wiki/Short-circuit_evaluation}{short-circuiting}:
|
||||
if the \a first task reports DoneResult::Success, the \a second task is skipped,
|
||||
and the returned item reports DoneResult::Success immediately.
|
||||
|
||||
The returned item is equivalent to:
|
||||
\code
|
||||
Group { stopOnSuccess, first, second }
|
||||
\endcode
|
||||
|
||||
\note Parallel execution of disjunction in a short-circuit manner can be achieved with the
|
||||
following code: \c {Group { parallel, stopOnSuccess, first, second }}. In this case:
|
||||
if the \e {first finished} task reports DoneResult::Success,
|
||||
the \e other task is canceled, and the group reports DoneResult::Success immediately.
|
||||
|
||||
\sa operator&&(), operator!()
|
||||
*/
|
||||
ExecutableItem operator||(const ExecutableItem &first, const ExecutableItem &second)
|
||||
{
|
||||
return Group { stopOnSuccess, first, second };
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator&&(const ExecutableItem &item, DoneResult result)
|
||||
\overload ExecutableItem::operator&&()
|
||||
|
||||
Returns the \a item task if the \a result is DoneResult::Success; otherwise returns
|
||||
the \a item task with its done result tweaked to DoneResult::Error.
|
||||
|
||||
The \c {task && DoneResult::Error} is an eqivalent to tweaking the task's done result
|
||||
into DoneResult::Error unconditionally.
|
||||
*/
|
||||
ExecutableItem operator&&(const ExecutableItem &item, DoneResult result)
|
||||
{
|
||||
if (result == DoneResult::Success)
|
||||
return item;
|
||||
return Group { finishAllAndError, item };
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator||(const ExecutableItem &item, DoneResult result)
|
||||
\overload ExecutableItem::operator||()
|
||||
|
||||
Returns the \a item task if the \a result is DoneResult::Error; otherwise returns
|
||||
the \a item task with its done result tweaked to DoneResult::Success.
|
||||
|
||||
The \c {task || DoneResult::Success} is an eqivalent to tweaking the task's done result
|
||||
into DoneResult::Success unconditionally.
|
||||
*/
|
||||
ExecutableItem operator||(const ExecutableItem &item, DoneResult result)
|
||||
{
|
||||
if (result == DoneResult::Error)
|
||||
return item;
|
||||
return Group { finishAllAndSuccess, item };
|
||||
}
|
||||
|
||||
ExecutableItem ExecutableItem::withCancelImpl(
|
||||
const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const
|
||||
{
|
||||
@ -1974,13 +2185,6 @@ SetupResult TaskTreePrivate::start(RuntimeContainer *container)
|
||||
container->m_successBit = startAction == SetupResult::StopWithSuccess;
|
||||
}
|
||||
}
|
||||
if (startAction == SetupResult::Continue
|
||||
&& (containerNode.m_children.empty()
|
||||
|| (containerNode.m_loop && !invokeLoopHandler(container)))) {
|
||||
if (isProgressive(container))
|
||||
advanceProgress(containerNode.m_taskCount);
|
||||
startAction = toSetupResult(container->m_successBit);
|
||||
}
|
||||
return continueStart(container, startAction);
|
||||
}
|
||||
|
||||
@ -1988,23 +2192,24 @@ SetupResult TaskTreePrivate::continueStart(RuntimeContainer *container, SetupRes
|
||||
{
|
||||
const SetupResult groupAction = startAction == SetupResult::Continue ? startChildren(container)
|
||||
: startAction;
|
||||
if (groupAction != SetupResult::Continue) {
|
||||
const bool bit = container->updateSuccessBit(groupAction == SetupResult::StopWithSuccess);
|
||||
RuntimeIteration *parentIteration = container->parentIteration();
|
||||
RuntimeTask *parentTask = container->m_parentTask;
|
||||
QT_CHECK(parentTask);
|
||||
const bool result = invokeDoneHandler(container, bit ? DoneWith::Success : DoneWith::Error);
|
||||
if (parentIteration) {
|
||||
parentIteration->deleteChild(parentTask);
|
||||
if (!parentIteration->m_container->isStarting())
|
||||
childDone(parentIteration, result);
|
||||
} else {
|
||||
QT_CHECK(m_runtimeRoot.get() == parentTask);
|
||||
m_runtimeRoot.reset();
|
||||
emitDone(result ? DoneWith::Success : DoneWith::Error);
|
||||
}
|
||||
if (groupAction == SetupResult::Continue)
|
||||
return groupAction;
|
||||
|
||||
const bool bit = container->updateSuccessBit(groupAction == SetupResult::StopWithSuccess);
|
||||
RuntimeIteration *parentIteration = container->parentIteration();
|
||||
RuntimeTask *parentTask = container->m_parentTask;
|
||||
QT_CHECK(parentTask);
|
||||
const bool result = invokeDoneHandler(container, bit ? DoneWith::Success : DoneWith::Error);
|
||||
if (parentIteration) {
|
||||
parentIteration->deleteChild(parentTask);
|
||||
if (!parentIteration->m_container->isStarting())
|
||||
childDone(parentIteration, result);
|
||||
} else {
|
||||
QT_CHECK(m_runtimeRoot.get() == parentTask);
|
||||
m_runtimeRoot.reset();
|
||||
emitDone(result ? DoneWith::Success : DoneWith::Error);
|
||||
}
|
||||
return groupAction;
|
||||
return toSetupResult(result);
|
||||
}
|
||||
|
||||
SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container)
|
||||
@ -2013,14 +2218,14 @@ SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container)
|
||||
const int childCount = int(containerNode.m_children.size());
|
||||
|
||||
if (container->m_iterationCount == 0) {
|
||||
if (container->m_shouldIterate && !invokeLoopHandler(container)) {
|
||||
if (isProgressive(container))
|
||||
advanceProgress(containerNode.m_taskCount);
|
||||
return toSetupResult(container->m_successBit);
|
||||
}
|
||||
container->m_iterations.emplace_back(
|
||||
std::make_unique<RuntimeIteration>(container->m_iterationCount, container));
|
||||
++container->m_iterationCount;
|
||||
} else if (containerNode.m_parallelLimit == 0) {
|
||||
container->deleteFinishedIterations();
|
||||
if (container->m_iterations.empty())
|
||||
return toSetupResult(container->m_successBit);
|
||||
return SetupResult::Continue;
|
||||
}
|
||||
|
||||
GuardLocker locker(container->m_startGuard);
|
||||
@ -2029,17 +2234,20 @@ SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container)
|
||||
|| container->m_runningChildren < containerNode.m_parallelLimit) {
|
||||
container->deleteFinishedIterations();
|
||||
if (container->m_nextToStart == childCount) {
|
||||
if (container->m_shouldIterate && invokeLoopHandler(container)) {
|
||||
if (invokeLoopHandler(container)) {
|
||||
container->m_nextToStart = 0;
|
||||
container->m_iterations.emplace_back(
|
||||
std::make_unique<RuntimeIteration>(container->m_iterationCount, container));
|
||||
++container->m_iterationCount;
|
||||
} else if (container->m_iterations.empty()) {
|
||||
return toSetupResult(container->m_successBit);
|
||||
} else {
|
||||
if (container->m_iterations.empty())
|
||||
return toSetupResult(container->m_successBit);
|
||||
return SetupResult::Continue;
|
||||
}
|
||||
}
|
||||
if (containerNode.m_children.size() == 0) // Empty loop body.
|
||||
continue;
|
||||
|
||||
RuntimeIteration *iteration = container->m_iterations.back().get();
|
||||
RuntimeTask *newTask = new RuntimeTask{containerNode.m_children.at(container->m_nextToStart),
|
||||
iteration};
|
||||
@ -2436,7 +2644,7 @@ bool TaskTreePrivate::invokeDoneHandler(RuntimeTask *node, DoneWith doneWith)
|
||||
\section2 Task's Done Handler
|
||||
|
||||
When a running task finishes, the task tree invokes an optionally provided done handler.
|
||||
The handler should always take a \c const \e reference to the associated task class object:
|
||||
The handler should take a \c const \e reference to the associated task class object:
|
||||
|
||||
\code
|
||||
const auto onSetup = [](QProcess &process) {
|
||||
@ -3399,13 +3607,13 @@ void TimeoutTaskAdapter::start()
|
||||
}
|
||||
|
||||
/*!
|
||||
\typealias TaskTreeTask
|
||||
\typealias Tasking::TaskTreeTask
|
||||
|
||||
Type alias for the CustomTask, to be used inside recipes, associated with the TaskTree task.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias TimeoutTask
|
||||
\typealias Tasking::TimeoutTask
|
||||
|
||||
Type alias for the CustomTask, to be used inside recipes, associated with the
|
||||
\c std::chrono::milliseconds type. \c std::chrono::milliseconds is used to set up the
|
||||
|
@ -218,13 +218,12 @@ public:
|
||||
: m_type(Type::Storage)
|
||||
, m_storageList{storage} {}
|
||||
|
||||
GroupItem(const Loop &loop) : GroupItem(GroupData{{}, {}, {}, loop}) {}
|
||||
|
||||
// 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); }
|
||||
|
||||
protected:
|
||||
GroupItem(const Loop &loop) : GroupItem(GroupData{{}, {}, {}, loop}) {}
|
||||
// Internal, provided by CustomTask
|
||||
using InterfaceCreateHandler = std::function<TaskInterface *(void)>;
|
||||
// Called prior to task start, just after createHandler
|
||||
@ -271,8 +270,6 @@ protected:
|
||||
void addChildren(const QList<GroupItem> &children);
|
||||
|
||||
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}); }
|
||||
|
||||
// Checks if Function may be invoked with Args and if Function's return type is Result.
|
||||
template <typename Result, typename Function, typename ...Args,
|
||||
@ -287,8 +284,11 @@ protected:
|
||||
|
||||
private:
|
||||
friend class ContainerNode;
|
||||
friend class For;
|
||||
friend class TaskNode;
|
||||
friend class TaskTreePrivate;
|
||||
friend class ParallelLimitFunctor;
|
||||
friend class WorkflowPolicyFunctor;
|
||||
Type m_type = Type::Group;
|
||||
QList<GroupItem> m_children;
|
||||
GroupData m_groupData;
|
||||
@ -319,6 +319,14 @@ protected:
|
||||
ExecutableItem(const TaskHandler &handler) : GroupItem(handler) {}
|
||||
|
||||
private:
|
||||
TASKING_EXPORT friend ExecutableItem operator!(const ExecutableItem &item);
|
||||
TASKING_EXPORT friend ExecutableItem operator&&(const ExecutableItem &first,
|
||||
const ExecutableItem &second);
|
||||
TASKING_EXPORT friend ExecutableItem operator||(const ExecutableItem &first,
|
||||
const ExecutableItem &second);
|
||||
TASKING_EXPORT friend ExecutableItem operator&&(const ExecutableItem &item, DoneResult result);
|
||||
TASKING_EXPORT friend ExecutableItem operator||(const ExecutableItem &item, DoneResult result);
|
||||
|
||||
ExecutableItem withCancelImpl(
|
||||
const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const;
|
||||
};
|
||||
@ -338,8 +346,6 @@ public:
|
||||
static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDoneIf::SuccessOrError) {
|
||||
return groupHandler({{}, wrapGroupDone(std::forward<Handler>(handler)), callDoneIf});
|
||||
}
|
||||
using GroupItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel).
|
||||
using GroupItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError.
|
||||
|
||||
private:
|
||||
template <typename Handler>
|
||||
@ -361,24 +367,34 @@ private:
|
||||
template <typename Handler>
|
||||
static GroupDoneHandler wrapGroupDone(Handler &&handler)
|
||||
{
|
||||
// R, V, D stands for: Done[R]esult, [V]oid, [D]oneWith
|
||||
static constexpr bool isDoneResultType = std::is_same_v<Handler, DoneResult>;
|
||||
// R, B, V, D stands for: Done[R]esult, [B]ool, [V]oid, [D]oneWith
|
||||
static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>();
|
||||
static constexpr bool isR = isInvocable<DoneResult, Handler>();
|
||||
static constexpr bool isBD = isInvocable<bool, Handler, DoneWith>();
|
||||
static constexpr bool isB = isInvocable<bool, Handler>();
|
||||
static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
|
||||
static constexpr bool isV = isInvocable<void, Handler>();
|
||||
static_assert(isRD || isR || isVD || isV,
|
||||
static_assert(isDoneResultType || isRD || isR || isBD || isB || isVD || isV,
|
||||
"Group done handler needs to take (DoneWith) or (void) as an argument and has to "
|
||||
"return void or DoneResult. The passed handler doesn't fulfill these requirements.");
|
||||
"return void, bool or DoneResult. Alternatively, it may be of DoneResult type. "
|
||||
"The passed handler doesn't fulfill these requirements.");
|
||||
return [handler](DoneWith result) {
|
||||
if constexpr (isDoneResultType)
|
||||
return handler;
|
||||
if constexpr (isRD)
|
||||
return std::invoke(handler, result);
|
||||
if constexpr (isR)
|
||||
return std::invoke(handler);
|
||||
if constexpr (isBD)
|
||||
return toDoneResult(std::invoke(handler, result));
|
||||
if constexpr (isB)
|
||||
return toDoneResult(std::invoke(handler));
|
||||
if constexpr (isVD)
|
||||
std::invoke(handler, result);
|
||||
else if constexpr (isV)
|
||||
std::invoke(handler);
|
||||
return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
|
||||
return toDoneResult(result == DoneWith::Success);
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -395,10 +411,22 @@ static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDone
|
||||
return Group::onGroupDone(std::forward<Handler>(handler), callDoneIf);
|
||||
}
|
||||
|
||||
TASKING_EXPORT GroupItem parallelLimit(int limit);
|
||||
TASKING_EXPORT GroupItem workflowPolicy(WorkflowPolicy policy);
|
||||
class TASKING_EXPORT ParallelLimitFunctor
|
||||
{
|
||||
public:
|
||||
// Default: 1 (sequential). 0 means unlimited (parallel).
|
||||
GroupItem operator()(int limit) const;
|
||||
};
|
||||
|
||||
TASKING_EXPORT extern const GroupItem nullItem;
|
||||
class TASKING_EXPORT WorkflowPolicyFunctor
|
||||
{
|
||||
public:
|
||||
// Default: WorkflowPolicy::StopOnError.
|
||||
GroupItem operator()(WorkflowPolicy policy) const;
|
||||
};
|
||||
|
||||
TASKING_EXPORT extern const ParallelLimitFunctor parallelLimit;
|
||||
TASKING_EXPORT extern const WorkflowPolicyFunctor workflowPolicy;
|
||||
|
||||
TASKING_EXPORT extern const GroupItem sequential;
|
||||
TASKING_EXPORT extern const GroupItem parallel;
|
||||
@ -412,11 +440,46 @@ TASKING_EXPORT extern const GroupItem stopOnSuccessOrError;
|
||||
TASKING_EXPORT extern const GroupItem finishAllAndSuccess;
|
||||
TASKING_EXPORT extern const GroupItem finishAllAndError;
|
||||
|
||||
class TASKING_EXPORT Forever final : public Group
|
||||
TASKING_EXPORT extern const GroupItem nullItem;
|
||||
TASKING_EXPORT extern const ExecutableItem successItem;
|
||||
TASKING_EXPORT extern const ExecutableItem errorItem;
|
||||
|
||||
class TASKING_EXPORT For : public Group
|
||||
{
|
||||
public:
|
||||
Forever(const QList<GroupItem> &children) : Group({LoopForever(), children}) {}
|
||||
Forever(std::initializer_list<GroupItem> children) : Group({LoopForever(), children}) {}
|
||||
template <typename ...Args>
|
||||
For(const Loop &loop, const Args &...args)
|
||||
: Group(withLoop(loop, args...)) { }
|
||||
|
||||
protected:
|
||||
For(const Loop &loop, const QList<GroupItem> &children) : Group({loop, children}) {}
|
||||
For(const Loop &loop, std::initializer_list<GroupItem> children) : Group({loop, children}) {}
|
||||
|
||||
private:
|
||||
template <typename ...Args>
|
||||
QList<GroupItem> withLoop(const Loop &loop, const Args &...args) {
|
||||
QList<GroupItem> children{GroupItem(loop)};
|
||||
appendChildren(std::make_tuple(args...), &children);
|
||||
return children;
|
||||
}
|
||||
|
||||
template <typename Tuple, std::size_t N = 0>
|
||||
void appendChildren(const Tuple &tuple, QList<GroupItem> *children) {
|
||||
constexpr auto TupleSize = std::tuple_size_v<Tuple>;
|
||||
if constexpr (TupleSize > 0) {
|
||||
// static_assert(workflowPolicyCount<Tuple>() <= 1, "Too many workflow policies in one group.");
|
||||
children->append(std::get<N>(tuple));
|
||||
if constexpr (N + 1 < TupleSize)
|
||||
appendChildren<Tuple, N + 1>(tuple, children);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TASKING_EXPORT Forever final : public For
|
||||
{
|
||||
public:
|
||||
Forever(const QList<GroupItem> &children) : For(LoopForever(), children) {}
|
||||
Forever(std::initializer_list<GroupItem> children) : For(LoopForever(), children) {}
|
||||
};
|
||||
|
||||
// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()
|
||||
@ -425,26 +488,20 @@ class TASKING_EXPORT Sync final : public ExecutableItem
|
||||
public:
|
||||
template <typename Handler>
|
||||
Sync(Handler &&handler) {
|
||||
addChildren({ onGroupSetup(wrapHandler(std::forward<Handler>(handler))) });
|
||||
addChildren({ onGroupDone(wrapHandler(std::forward<Handler>(handler))) });
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Handler>
|
||||
static GroupSetupHandler wrapHandler(Handler &&handler) {
|
||||
// R, V stands for: Done[R]esult, [V]oid
|
||||
static auto wrapHandler(Handler &&handler) {
|
||||
// R, B, V stands for: Done[R]esult, [B]ool, [V]oid
|
||||
static constexpr bool isR = isInvocable<DoneResult, Handler>();
|
||||
static constexpr bool isB = isInvocable<bool, Handler>();
|
||||
static constexpr bool isV = isInvocable<void, Handler>();
|
||||
static_assert(isR || isV,
|
||||
"Sync handler needs to take no arguments and has to return void or DoneResult. "
|
||||
static_assert(isR || isB || isV,
|
||||
"Sync handler needs to take no arguments and has to return void, bool or DoneResult. "
|
||||
"The passed handler doesn't fulfill these requirements.");
|
||||
return [handler] {
|
||||
if constexpr (isR) {
|
||||
return std::invoke(handler) == DoneResult::Success ? SetupResult::StopWithSuccess
|
||||
: SetupResult::StopWithError;
|
||||
}
|
||||
std::invoke(handler);
|
||||
return SetupResult::StopWithSuccess;
|
||||
};
|
||||
return handler;
|
||||
}
|
||||
};
|
||||
|
||||
@ -507,21 +564,31 @@ private:
|
||||
template <typename Handler>
|
||||
static InterfaceDoneHandler wrapDone(Handler &&handler) {
|
||||
if constexpr (std::is_same_v<Handler, TaskDoneHandler>)
|
||||
return {}; // When user passed {} for the done handler.
|
||||
// R, V, T, D stands for: Done[R]esult, [V]oid, [T]ask, [D]oneWith
|
||||
return {}; // User passed {} for the done handler.
|
||||
static constexpr bool isDoneResultType = std::is_same_v<Handler, DoneResult>;
|
||||
// R, B, V, T, D stands for: Done[R]esult, [B]ool, [V]oid, [T]ask, [D]oneWith
|
||||
static constexpr bool isRTD = isInvocable<DoneResult, Handler, const Task &, DoneWith>();
|
||||
static constexpr bool isRT = isInvocable<DoneResult, Handler, const Task &>();
|
||||
static constexpr bool isRD = isInvocable<DoneResult, Handler, DoneWith>();
|
||||
static constexpr bool isR = isInvocable<DoneResult, Handler>();
|
||||
static constexpr bool isBTD = isInvocable<bool, Handler, const Task &, DoneWith>();
|
||||
static constexpr bool isBT = isInvocable<bool, Handler, const Task &>();
|
||||
static constexpr bool isBD = isInvocable<bool, Handler, DoneWith>();
|
||||
static constexpr bool isB = isInvocable<bool, Handler>();
|
||||
static constexpr bool isVTD = isInvocable<void, Handler, const Task &, DoneWith>();
|
||||
static constexpr bool isVT = isInvocable<void, Handler, const Task &>();
|
||||
static constexpr bool isVD = isInvocable<void, Handler, DoneWith>();
|
||||
static constexpr bool isV = isInvocable<void, Handler>();
|
||||
static_assert(isRTD || isRT || isRD || isR || isVTD || isVT || isVD || isV,
|
||||
static_assert(isDoneResultType || isRTD || isRT || isRD || isR
|
||||
|| isBTD || isBT || isBD || isB
|
||||
|| isVTD || isVT || isVD || isV,
|
||||
"Task done handler needs to take (const Task &, DoneWith), (const Task &), "
|
||||
"(DoneWith) or (void) as arguments and has to return void or DoneResult. "
|
||||
"(DoneWith) or (void) as arguments and has to return void, bool or DoneResult. "
|
||||
"Alternatively, it may be of DoneResult type. "
|
||||
"The passed handler doesn't fulfill these requirements.");
|
||||
return [handler](const TaskInterface &taskInterface, DoneWith result) {
|
||||
if constexpr (isDoneResultType)
|
||||
return handler;
|
||||
const Adapter &adapter = static_cast<const Adapter &>(taskInterface);
|
||||
if constexpr (isRTD)
|
||||
return std::invoke(handler, *adapter.task(), result);
|
||||
@ -531,6 +598,14 @@ private:
|
||||
return std::invoke(handler, result);
|
||||
if constexpr (isR)
|
||||
return std::invoke(handler);
|
||||
if constexpr (isBTD)
|
||||
return toDoneResult(std::invoke(handler, *adapter.task(), result));
|
||||
if constexpr (isBT)
|
||||
return toDoneResult(std::invoke(handler, *adapter.task()));
|
||||
if constexpr (isBD)
|
||||
return toDoneResult(std::invoke(handler, result));
|
||||
if constexpr (isB)
|
||||
return toDoneResult(std::invoke(handler));
|
||||
if constexpr (isVTD)
|
||||
std::invoke(handler, *adapter.task(), result);
|
||||
else if constexpr (isVT)
|
||||
@ -539,7 +614,7 @@ private:
|
||||
std::invoke(handler, result);
|
||||
else if constexpr (isV)
|
||||
std::invoke(handler);
|
||||
return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
|
||||
return toDoneResult(result == DoneWith::Success);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
67
src/assets/downloader/tasking/tcpsocket.cpp
Normal file
67
src/assets/downloader/tasking/tcpsocket.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright (C) 2024 Jarek Kobus
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include "tcpsocket.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
void TcpSocket::start()
|
||||
{
|
||||
if (m_socket) {
|
||||
qWarning("The TcpSocket is already running. Ignoring the call to start().");
|
||||
return;
|
||||
}
|
||||
if (m_address.isNull()) {
|
||||
qWarning("Can't start the TcpSocket with invalid address. "
|
||||
"Stopping with an error.");
|
||||
m_error = QAbstractSocket::HostNotFoundError;
|
||||
emit done(DoneResult::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
m_socket.reset(new QTcpSocket);
|
||||
connect(m_socket.get(), &QAbstractSocket::errorOccurred, this,
|
||||
[this](QAbstractSocket::SocketError error) {
|
||||
m_error = error;
|
||||
m_socket->disconnect();
|
||||
emit done(DoneResult::Error);
|
||||
m_socket.release()->deleteLater();
|
||||
});
|
||||
connect(m_socket.get(), &QAbstractSocket::connected, this, [this] {
|
||||
if (!m_writeData.isEmpty())
|
||||
m_socket->write(m_writeData);
|
||||
emit started();
|
||||
});
|
||||
connect(m_socket.get(), &QAbstractSocket::disconnected, this, [this] {
|
||||
m_socket->disconnect();
|
||||
emit done(DoneResult::Success);
|
||||
m_socket.release()->deleteLater();
|
||||
});
|
||||
|
||||
m_socket->connectToHost(m_address, m_port);
|
||||
}
|
||||
|
||||
TcpSocket::~TcpSocket()
|
||||
{
|
||||
if (m_socket) {
|
||||
m_socket->disconnect();
|
||||
m_socket->abort();
|
||||
}
|
||||
}
|
||||
|
||||
TcpSocketTaskAdapter::TcpSocketTaskAdapter()
|
||||
{
|
||||
connect(task(), &TcpSocket::done, this, &TaskInterface::done);
|
||||
}
|
||||
|
||||
void TcpSocketTaskAdapter::start()
|
||||
{
|
||||
task()->start();
|
||||
}
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
QT_END_NAMESPACE
|
72
src/assets/downloader/tasking/tcpsocket.h
Normal file
72
src/assets/downloader/tasking/tcpsocket.h
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright (C) 2024 Jarek Kobus
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef TASKING_TCPSOCKET_H
|
||||
#define TASKING_TCPSOCKET_H
|
||||
|
||||
#include "tasking_global.h"
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "tasktree.h"
|
||||
|
||||
#include <QtNetwork/QTcpSocket>
|
||||
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
// This class introduces the dependency to Qt::Network, otherwise Tasking namespace
|
||||
// is independent on Qt::Network.
|
||||
// Possibly, it could be placed inside Qt::Network library, as a wrapper around QTcpSocket.
|
||||
|
||||
class TASKING_EXPORT TcpSocket final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~TcpSocket();
|
||||
void setAddress(const QHostAddress &address) { m_address = address; }
|
||||
void setPort(quint16 port) { m_port = port; }
|
||||
void setWriteData(const QByteArray &data) { m_writeData = data; }
|
||||
QTcpSocket *socket() const { return m_socket.get(); }
|
||||
void start();
|
||||
|
||||
Q_SIGNALS:
|
||||
void started();
|
||||
void done(DoneResult result);
|
||||
|
||||
private:
|
||||
QHostAddress m_address;
|
||||
quint16 m_port = 0;
|
||||
QByteArray m_writeData;
|
||||
std::unique_ptr<QTcpSocket> m_socket;
|
||||
QAbstractSocket::SocketError m_error = QAbstractSocket::UnknownSocketError;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT TcpSocketTaskAdapter final : public TaskAdapter<TcpSocket>
|
||||
{
|
||||
public:
|
||||
TcpSocketTaskAdapter();
|
||||
void start() final;
|
||||
};
|
||||
|
||||
using TcpSocketTask = CustomTask<TcpSocketTaskAdapter>;
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // TASKING_TCPSOCKET_H
|
Loading…
x
Reference in New Issue
Block a user