TaskTree: Sync sources
Source sync Creator -> AssetDownloader. Change-Id: Ic503ba04c272718611a4a266bef40e73517e32fd Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> (cherry picked from commit 2ab472f6cbcb8505a84dac8acd8e6795b9af0b8d) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit cabdf3f94ab4973e1c6ac0d70ab196356ce44a32)
This commit is contained in:
parent
aac28b280f
commit
2eb2ae4cf4
@ -537,8 +537,7 @@ void AssetDownloader::start()
|
||||
onGroupSetup(onSkipIfAllAssetsPresent),
|
||||
NetworkQueryTask(onZipDownloadSetup, onZipDownloadDone),
|
||||
ConcurrentCallTask<void>(onUnzipSetup, onUnzipDone),
|
||||
For {
|
||||
downloadIterator,
|
||||
For (downloadIterator) >> Do {
|
||||
parallelIdealThreadCountLimit,
|
||||
onGroupSetup(onAssetsDownloadGroupSetup),
|
||||
Group {
|
||||
@ -547,8 +546,7 @@ void AssetDownloader::start()
|
||||
ConcurrentCallTask<void>(onAssetWriteSetup, onAssetWriteDone)
|
||||
}
|
||||
},
|
||||
For {
|
||||
copyIterator,
|
||||
For (copyIterator) >> Do {
|
||||
parallelIdealThreadCountLimit,
|
||||
onGroupSetup(onAssetsCopyGroupSetup),
|
||||
ConcurrentCallTask<void>(onAssetCopySetup, onAssetCopyDone, CallDoneIf::Success),
|
||||
|
@ -49,14 +49,7 @@ private:
|
||||
int m_current = -1;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT BarrierTaskAdapter : public TaskAdapter<Barrier>
|
||||
{
|
||||
public:
|
||||
BarrierTaskAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); }
|
||||
void start() final { task()->start(); }
|
||||
};
|
||||
|
||||
using BarrierTask = CustomTask<BarrierTaskAdapter>;
|
||||
using BarrierTask = SimpleCustomTask<Barrier>;
|
||||
|
||||
template <int Limit = 1>
|
||||
class SharedBarrier
|
||||
@ -90,21 +83,42 @@ ExecutableItem waitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
|
||||
qWarning("The barrier referenced from WaitForBarrier element "
|
||||
"is not reachable in the running tree. "
|
||||
"It is possible that no barrier was added to the tree, "
|
||||
"or the storage is not reachable from where it is referenced. "
|
||||
"or the barrier is not reachable from where it is referenced. "
|
||||
"The WaitForBarrier task finishes with an error. ");
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
Barrier *activeSharedBarrier = activeBarrier->barrier();
|
||||
const std::optional<DoneResult> result = activeSharedBarrier->result();
|
||||
if (result.has_value()) {
|
||||
return result.value() == DoneResult::Success ? SetupResult::StopWithSuccess
|
||||
: SetupResult::StopWithError;
|
||||
return *result == DoneResult::Success ? SetupResult::StopWithSuccess
|
||||
: SetupResult::StopWithError;
|
||||
}
|
||||
QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult);
|
||||
return SetupResult::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Signal>
|
||||
ExecutableItem signalAwaiter(const typename QtPrivate::FunctionPointer<Signal>::Object *sender, Signal signal)
|
||||
{
|
||||
return BarrierTask([sender, signal](Barrier &barrier) {
|
||||
QObject::connect(sender, signal, &barrier, &Barrier::advance, Qt::SingleShotConnection);
|
||||
});
|
||||
}
|
||||
|
||||
using BarrierKickerGetter = std::function<ExecutableItem(const SingleBarrier &)>;
|
||||
|
||||
class TASKING_EXPORT When final
|
||||
{
|
||||
public:
|
||||
explicit When(const BarrierKickerGetter &kicker) : m_barrierKicker(kicker) {}
|
||||
|
||||
private:
|
||||
TASKING_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem);
|
||||
|
||||
BarrierKickerGetter m_barrierKicker;
|
||||
};
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -39,7 +39,7 @@ public:
|
||||
template <typename Function, typename ...Args>
|
||||
void setConcurrentCallData(Function &&function, Args &&...args)
|
||||
{
|
||||
return wrapConcurrent(std::forward<Function>(function), std::forward<Args>(args)...);
|
||||
wrapConcurrent(std::forward<Function>(function), std::forward<Args>(args)...);
|
||||
}
|
||||
void setThreadPool(QThreadPool *pool) { m_threadPool = pool; }
|
||||
ResultType result() const
|
||||
|
@ -8,36 +8,19 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
static Group conditionRecipe(const Storage<bool> &bodyExecutedStorage,
|
||||
const ConditionData &condition)
|
||||
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 onBodyDone = [bodyExecutedStorage] { *bodyExecutedStorage = true; };
|
||||
|
||||
const auto onContinuationSetup = [skipContinuationStorage, bodyExecutedStorage] {
|
||||
*bodyExecutedStorage = !*skipContinuationStorage;
|
||||
return *skipContinuationStorage ? SetupResult::StopWithSuccess : SetupResult::Continue;
|
||||
};
|
||||
const Group bodyTask { condition.m_body, onGroupDone(onBodyDone) };
|
||||
|
||||
return {
|
||||
skipContinuationStorage,
|
||||
onGroupSetup(onSetup),
|
||||
condition.m_condition ? Group {
|
||||
*condition.m_condition,
|
||||
onGroupDone(onConditionDone)
|
||||
} : nullItem,
|
||||
Group {
|
||||
onGroupSetup(onContinuationSetup),
|
||||
condition.m_body
|
||||
}
|
||||
condition.m_condition ? Group{ !*condition.m_condition || bodyTask } : bodyTask
|
||||
};
|
||||
}
|
||||
|
||||
@ -45,7 +28,7 @@ static ExecutableItem conditionsRecipe(const QList<ConditionData> &conditions)
|
||||
{
|
||||
Storage<bool> bodyExecutedStorage;
|
||||
|
||||
QList<GroupItem> recipes;
|
||||
GroupItems recipes;
|
||||
for (const ConditionData &condition : conditions)
|
||||
recipes << conditionRecipe(bodyExecutedStorage, condition);
|
||||
|
||||
|
@ -62,7 +62,7 @@ private:
|
||||
class TASKING_EXPORT Else
|
||||
{
|
||||
public:
|
||||
explicit Else(const QList<GroupItem> &children) : m_body({children}) {}
|
||||
explicit Else(const GroupItems &children) : m_body({children}) {}
|
||||
explicit Else(std::initializer_list<GroupItem> children) : m_body({children}) {}
|
||||
|
||||
private:
|
||||
@ -73,7 +73,7 @@ private:
|
||||
class TASKING_EXPORT Then
|
||||
{
|
||||
public:
|
||||
explicit Then(const QList<GroupItem> &children) : m_body({children}) {}
|
||||
explicit Then(const GroupItems &children) : m_body({children}) {}
|
||||
explicit Then(std::initializer_list<GroupItem> children) : m_body({children}) {}
|
||||
|
||||
private:
|
||||
|
@ -36,6 +36,11 @@ void NetworkQuery::start()
|
||||
m_reply.reset(m_manager->deleteResource(m_request));
|
||||
break;
|
||||
}
|
||||
|
||||
connect(m_reply.get(), &QNetworkReply::downloadProgress, this, &NetworkQuery::downloadProgress);
|
||||
#if QT_CONFIG(ssl)
|
||||
connect(m_reply.get(), &QNetworkReply::sslErrors, this, &NetworkQuery::sslErrors);
|
||||
#endif
|
||||
connect(m_reply.get(), &QNetworkReply::finished, this, [this] {
|
||||
disconnect(m_reply.get(), &QNetworkReply::finished, this, nullptr);
|
||||
emit done(toDoneResult(m_reply->error() == QNetworkReply::NoError));
|
||||
@ -48,7 +53,7 @@ void NetworkQuery::start()
|
||||
NetworkQuery::~NetworkQuery()
|
||||
{
|
||||
if (m_reply) {
|
||||
disconnect(m_reply.get(), &QNetworkReply::finished, this, nullptr);
|
||||
disconnect(m_reply.get(), nullptr, this, nullptr);
|
||||
m_reply->abort();
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,10 @@ public:
|
||||
|
||||
Q_SIGNALS:
|
||||
void started();
|
||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
#if QT_CONFIG(ssl)
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
#endif
|
||||
void done(DoneResult result);
|
||||
|
||||
private:
|
||||
@ -61,14 +65,7 @@ private:
|
||||
std::unique_ptr<QNetworkReply> m_reply;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT NetworkQueryTaskAdapter : public TaskAdapter<NetworkQuery>
|
||||
{
|
||||
public:
|
||||
NetworkQueryTaskAdapter() { connect(task(), &NetworkQuery::done, this, &TaskInterface::done); }
|
||||
void start() final { task()->start(); }
|
||||
};
|
||||
|
||||
using NetworkQueryTask = CustomTask<NetworkQueryTaskAdapter>;
|
||||
using NetworkQueryTask = SimpleCustomTask<NetworkQuery>;
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "tasktree.h"
|
||||
|
||||
#include "barrier.h"
|
||||
#include "conditional.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QEventLoop>
|
||||
@ -319,6 +320,12 @@ private:
|
||||
\sa operator->(), operator*()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias Tasking::GroupItems
|
||||
|
||||
Type alias for QList<GroupItem>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class Tasking::GroupItem
|
||||
\inheaderfile solutions/tasking/tasktree.h
|
||||
@ -383,7 +390,7 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn GroupItem::GroupItem(const QList<GroupItem> &items)
|
||||
\fn GroupItem::GroupItem(const GroupItems &items)
|
||||
|
||||
Constructs a \c GroupItem element with a given list of \a items.
|
||||
|
||||
@ -417,9 +424,9 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn GroupItem::GroupItem(std::initializer_list<GroupItem> items)
|
||||
\fn Tasking::GroupItem(std::initializer_list<GroupItem> items)
|
||||
\overload
|
||||
\sa GroupItem(const QList<Tasking::GroupItem> &items)
|
||||
\sa GroupItem(const GroupItems &items)
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -508,7 +515,7 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn Group::Group(const QList<GroupItem> &children)
|
||||
\fn Group::Group(const GroupItems &children)
|
||||
|
||||
Constructs a group with a given list of \a children.
|
||||
|
||||
@ -518,7 +525,7 @@ private:
|
||||
\code
|
||||
const QStringList sourceList = ...;
|
||||
|
||||
QList<GroupItem> groupItems { parallel };
|
||||
GroupItems groupItems { parallel };
|
||||
|
||||
for (const QString &source : sourceList) {
|
||||
const NetworkQueryTask task(...); // use source for setup handler
|
||||
@ -1242,9 +1249,13 @@ private:
|
||||
|
||||
\sa sequential, parallel
|
||||
*/
|
||||
GroupItem ParallelLimitFunctor::operator()(int limit) const
|
||||
|
||||
GroupItem parallelLimit(int limit)
|
||||
{
|
||||
return GroupItem({{}, limit});
|
||||
struct ParallelLimit : GroupItem {
|
||||
ParallelLimit(int limit) : GroupItem({{}, limit}) {}
|
||||
};
|
||||
return ParallelLimit(limit);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -1255,14 +1266,14 @@ GroupItem ParallelLimitFunctor::operator()(int limit) const
|
||||
\sa stopOnError, continueOnError, stopOnSuccess, continueOnSuccess, stopOnSuccessOrError,
|
||||
finishAllAndSuccess, finishAllAndError, WorkflowPolicy
|
||||
*/
|
||||
GroupItem WorkflowPolicyFunctor::operator()(WorkflowPolicy policy) const
|
||||
GroupItem workflowPolicy(WorkflowPolicy policy)
|
||||
{
|
||||
return GroupItem({{}, {}, policy});
|
||||
struct WorkflowPolicyItem : GroupItem {
|
||||
WorkflowPolicyItem(WorkflowPolicy policy) : GroupItem({{}, {}, policy}) {}
|
||||
};
|
||||
return WorkflowPolicyItem(policy);
|
||||
}
|
||||
|
||||
const ParallelLimitFunctor parallelLimit = ParallelLimitFunctor();
|
||||
const WorkflowPolicyFunctor workflowPolicy = WorkflowPolicyFunctor();
|
||||
|
||||
const GroupItem sequential = parallelLimit(1);
|
||||
const GroupItem parallel = parallelLimit(0);
|
||||
const GroupItem parallelIdealThreadCountLimit = parallelLimit(qMax(QThread::idealThreadCount() - 1, 1));
|
||||
@ -1280,6 +1291,26 @@ const GroupItem nullItem = GroupItem({});
|
||||
const ExecutableItem successItem = Group { finishAllAndSuccess };
|
||||
const ExecutableItem errorItem = Group { finishAllAndError };
|
||||
|
||||
Group operator>>(const For &forItem, const Do &doItem)
|
||||
{
|
||||
return {forItem.m_loop, doItem.m_children};
|
||||
}
|
||||
|
||||
Group operator>>(const When &whenItem, const Do &doItem)
|
||||
{
|
||||
const SingleBarrier barrier;
|
||||
|
||||
return {
|
||||
barrier,
|
||||
parallel,
|
||||
whenItem.m_barrierKicker(barrier),
|
||||
Group {
|
||||
waitForBarrierTask(barrier),
|
||||
doItem.m_children
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -1441,7 +1472,7 @@ void *StorageBase::activeStorageVoid() const
|
||||
return m_storageData->threadData().activeStorage();
|
||||
}
|
||||
|
||||
void GroupItem::addChildren(const QList<GroupItem> &children)
|
||||
void GroupItem::addChildren(const GroupItems &children)
|
||||
{
|
||||
QT_ASSERT(m_type == Type::Group || m_type == Type::List,
|
||||
qWarning("Only Group or List may have children, skipping..."); return);
|
||||
@ -1528,8 +1559,8 @@ void GroupItem::addChildren(const QList<GroupItem> &children)
|
||||
immediately with the task's result. Otherwise, \a handler is invoked (if provided),
|
||||
the task is canceled, and the returned item finishes with an error.
|
||||
*/
|
||||
ExecutableItem ExecutableItem::withTimeout(milliseconds timeout,
|
||||
const std::function<void()> &handler) const
|
||||
Group ExecutableItem::withTimeout(milliseconds timeout,
|
||||
const std::function<void()> &handler) const
|
||||
{
|
||||
const auto onSetup = [timeout](milliseconds &timeoutData) { timeoutData = timeout; };
|
||||
return Group {
|
||||
@ -1562,7 +1593,7 @@ static QString logHeader(const QString &logName)
|
||||
synchronous or asynchronous, its result (the value described by the DoneWith enum),
|
||||
and the total execution time in milliseconds.
|
||||
*/
|
||||
ExecutableItem ExecutableItem::withLog(const QString &logName) const
|
||||
Group ExecutableItem::withLog(const QString &logName) const
|
||||
{
|
||||
struct LogStorage
|
||||
{
|
||||
@ -1593,9 +1624,9 @@ ExecutableItem ExecutableItem::withLog(const QString &logName) const
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator!(const ExecutableItem &item)
|
||||
\fn Group ExecutableItem::operator!(const ExecutableItem &item)
|
||||
|
||||
Returns an ExecutableItem with the DoneResult of \a item negated.
|
||||
Returns a Group 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.
|
||||
@ -1610,18 +1641,18 @@ ExecutableItem ExecutableItem::withLog(const QString &logName) const
|
||||
|
||||
\sa operator&&(), operator||()
|
||||
*/
|
||||
ExecutableItem operator!(const ExecutableItem &item)
|
||||
Group operator!(const ExecutableItem &item)
|
||||
{
|
||||
return Group {
|
||||
return {
|
||||
item,
|
||||
onGroupDone([](DoneWith doneWith) { return toDoneResult(doneWith == DoneWith::Error); })
|
||||
};
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator&&(const ExecutableItem &first, const ExecutableItem &second)
|
||||
\fn Group ExecutableItem::operator&&(const ExecutableItem &first, const ExecutableItem &second)
|
||||
|
||||
Returns an ExecutableItem with \a first and \a second tasks merged with conjunction.
|
||||
Returns a Group 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.
|
||||
@ -1644,15 +1675,15 @@ ExecutableItem operator!(const ExecutableItem &item)
|
||||
|
||||
\sa operator||(), operator!()
|
||||
*/
|
||||
ExecutableItem operator&&(const ExecutableItem &first, const ExecutableItem &second)
|
||||
Group operator&&(const ExecutableItem &first, const ExecutableItem &second)
|
||||
{
|
||||
return Group { stopOnError, first, second };
|
||||
return { stopOnError, first, second };
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator||(const ExecutableItem &first, const ExecutableItem &second)
|
||||
\fn Group ExecutableItem::operator||(const ExecutableItem &first, const ExecutableItem &second)
|
||||
|
||||
Returns an ExecutableItem with \a first and \a second tasks merged with disjunction.
|
||||
Returns a Group 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.
|
||||
@ -1675,13 +1706,13 @@ ExecutableItem operator&&(const ExecutableItem &first, const ExecutableItem &sec
|
||||
|
||||
\sa operator&&(), operator!()
|
||||
*/
|
||||
ExecutableItem operator||(const ExecutableItem &first, const ExecutableItem &second)
|
||||
Group operator||(const ExecutableItem &first, const ExecutableItem &second)
|
||||
{
|
||||
return Group { stopOnSuccess, first, second };
|
||||
return { stopOnSuccess, first, second };
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator&&(const ExecutableItem &item, DoneResult result)
|
||||
\fn Group ExecutableItem::operator&&(const ExecutableItem &item, DoneResult result)
|
||||
\overload ExecutableItem::operator&&()
|
||||
|
||||
Returns the \a item task if the \a result is DoneResult::Success; otherwise returns
|
||||
@ -1690,15 +1721,13 @@ ExecutableItem operator||(const ExecutableItem &first, const ExecutableItem &sec
|
||||
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)
|
||||
Group operator&&(const ExecutableItem &item, DoneResult result)
|
||||
{
|
||||
if (result == DoneResult::Success)
|
||||
return item;
|
||||
return Group { finishAllAndError, item };
|
||||
return { result == DoneResult::Success ? stopOnError : finishAllAndError, item };
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn ExecutableItem ExecutableItem::operator||(const ExecutableItem &item, DoneResult result)
|
||||
\fn Group ExecutableItem::operator||(const ExecutableItem &item, DoneResult result)
|
||||
\overload ExecutableItem::operator||()
|
||||
|
||||
Returns the \a item task if the \a result is DoneResult::Error; otherwise returns
|
||||
@ -1707,14 +1736,42 @@ ExecutableItem operator&&(const ExecutableItem &item, DoneResult result)
|
||||
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)
|
||||
Group operator||(const ExecutableItem &item, DoneResult result)
|
||||
{
|
||||
if (result == DoneResult::Error)
|
||||
return item;
|
||||
return Group { finishAllAndSuccess, item };
|
||||
return { result == DoneResult::Error ? stopOnError : finishAllAndSuccess, item };
|
||||
}
|
||||
|
||||
ExecutableItem ExecutableItem::withCancelImpl(
|
||||
Group ExecutableItem::withCancelImpl(
|
||||
const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper,
|
||||
const GroupItems &postCancelRecipe) const
|
||||
{
|
||||
const Storage<bool> canceledStorage(false);
|
||||
|
||||
const auto onSetup = [connectWrapper, canceledStorage](Barrier &barrier) {
|
||||
connectWrapper(&barrier, [barrierPtr = &barrier, canceled = canceledStorage.activeStorage()] {
|
||||
*canceled = true;
|
||||
barrierPtr->advance();
|
||||
});
|
||||
};
|
||||
|
||||
const auto wasCanceled = [canceledStorage] { return *canceledStorage; };
|
||||
|
||||
return {
|
||||
continueOnError,
|
||||
canceledStorage,
|
||||
Group {
|
||||
parallel,
|
||||
stopOnSuccessOrError,
|
||||
BarrierTask(onSetup) && errorItem,
|
||||
*this
|
||||
},
|
||||
If (wasCanceled) >> Then {
|
||||
postCancelRecipe
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Group ExecutableItem::withAcceptImpl(
|
||||
const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const
|
||||
{
|
||||
const auto onSetup = [connectWrapper](Barrier &barrier) {
|
||||
@ -1722,11 +1779,7 @@ ExecutableItem ExecutableItem::withCancelImpl(
|
||||
};
|
||||
return Group {
|
||||
parallel,
|
||||
stopOnSuccessOrError,
|
||||
Group {
|
||||
finishAllAndError,
|
||||
BarrierTask(onSetup)
|
||||
},
|
||||
BarrierTask(onSetup),
|
||||
*this
|
||||
};
|
||||
}
|
||||
@ -1843,17 +1896,16 @@ public:
|
||||
|
||||
// If returned value != Continue, childDone() needs to be called in parent container (in caller)
|
||||
// in order to unwind properly.
|
||||
SetupResult start(RuntimeTask *node);
|
||||
void stop(RuntimeTask *node);
|
||||
bool invokeDoneHandler(RuntimeTask *node, DoneWith doneWith);
|
||||
void startTask(const std::shared_ptr<RuntimeTask> &node);
|
||||
void stopTask(RuntimeTask *node);
|
||||
bool invokeTaskDoneHandler(RuntimeTask *node, DoneWith doneWith);
|
||||
|
||||
// Container related methods
|
||||
|
||||
SetupResult start(RuntimeContainer *container);
|
||||
SetupResult continueStart(RuntimeContainer *container, SetupResult startAction);
|
||||
SetupResult startChildren(RuntimeContainer *container);
|
||||
SetupResult childDone(RuntimeIteration *iteration, bool success);
|
||||
void stop(RuntimeContainer *container);
|
||||
void continueContainer(RuntimeContainer *container);
|
||||
void startChildren(RuntimeContainer *container);
|
||||
void childDone(RuntimeIteration *iteration, bool success);
|
||||
void stopContainer(RuntimeContainer *container);
|
||||
bool invokeDoneHandler(RuntimeContainer *container, DoneWith doneWith);
|
||||
bool invokeLoopHandler(RuntimeContainer *container);
|
||||
|
||||
@ -1861,6 +1913,16 @@ public:
|
||||
typename ReturnType = std::invoke_result_t<Handler, Args...>>
|
||||
ReturnType invokeHandler(Container *container, Handler &&handler, Args &&...args)
|
||||
{
|
||||
QT_ASSERT(!m_guard.isLocked(), qWarning("Nested execution of handlers detected."
|
||||
"This may happen when one task's handler has entered a nested event loop,"
|
||||
"and other task finished during nested event loop's processing, "
|
||||
"causing stopping (canceling) the task executing the nested event loop. "
|
||||
"This includes the case when QCoreApplication::processEvents() was called from "
|
||||
"the handler. It may also happen when the Barrier task is advanced directly "
|
||||
"from some other task handler. This will lead to a crash. "
|
||||
"Avoid event processing during handlers' execution. "
|
||||
"If it can't be avoided, make sure no other tasks are run in parallel when "
|
||||
"processing events from the handler."));
|
||||
ExecutionContextActivator activator(container);
|
||||
GuardLocker locker(m_guard);
|
||||
return std::invoke(std::forward<Handler>(handler), std::forward<Args>(args)...);
|
||||
@ -1878,7 +1940,7 @@ public:
|
||||
QSet<StorageBase> m_storages;
|
||||
QHash<StorageBase, StorageHandler> m_storageHandlers;
|
||||
std::optional<TaskNode> m_root;
|
||||
std::unique_ptr<RuntimeTask> m_runtimeRoot; // Keep me last in order to destruct first
|
||||
std::shared_ptr<RuntimeTask> m_runtimeRoot; // Keep me last in order to destruct first
|
||||
};
|
||||
|
||||
static bool initialSuccessBit(WorkflowPolicy workflowPolicy)
|
||||
@ -1908,13 +1970,13 @@ public:
|
||||
RuntimeIteration(int index, RuntimeContainer *container);
|
||||
~RuntimeIteration();
|
||||
std::optional<Loop> loop() const;
|
||||
void deleteChild(RuntimeTask *node);
|
||||
void removeChild(RuntimeTask *node);
|
||||
|
||||
const int m_iterationIndex = 0;
|
||||
const bool m_isProgressive = true;
|
||||
RuntimeContainer *m_container = nullptr;
|
||||
int m_doneCount = 0;
|
||||
std::vector<std::unique_ptr<RuntimeTask>> m_children = {}; // Owning.
|
||||
std::vector<std::shared_ptr<RuntimeTask>> m_children = {}; // Owning.
|
||||
};
|
||||
|
||||
class RuntimeContainer
|
||||
@ -1973,8 +2035,7 @@ public:
|
||||
{
|
||||
if (m_task) {
|
||||
// Ensures the running task's d'tor doesn't emit done() signal. QTCREATORBUG-30204.
|
||||
QObject::disconnect(m_task.get(), &TaskInterface::done,
|
||||
m_taskNode.m_container.m_taskTreePrivate->q, nullptr);
|
||||
QObject::disconnect(m_task.get(), &TaskInterface::done, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1982,12 +2043,14 @@ public:
|
||||
RuntimeIteration *m_parentIteration = nullptr; // Not owning.
|
||||
std::optional<RuntimeContainer> m_container = {}; // Owning.
|
||||
std::unique_ptr<TaskInterface> m_task = {}; // Owning.
|
||||
SetupResult m_setupResult = SetupResult::Continue;
|
||||
};
|
||||
|
||||
RuntimeIteration::~RuntimeIteration() = default;
|
||||
|
||||
TaskTreePrivate::TaskTreePrivate(TaskTree *taskTree)
|
||||
: q(taskTree) {}
|
||||
|
||||
TaskTreePrivate::~TaskTreePrivate() = default;
|
||||
|
||||
static bool isProgressive(RuntimeContainer *container)
|
||||
@ -2050,7 +2113,7 @@ void TaskTreePrivate::start()
|
||||
"exist in task tree. Its handlers will never be called."));
|
||||
}
|
||||
m_runtimeRoot.reset(new RuntimeTask{*m_root});
|
||||
start(m_runtimeRoot.get());
|
||||
startTask(m_runtimeRoot);
|
||||
bumpAsyncCount();
|
||||
}
|
||||
|
||||
@ -2059,7 +2122,7 @@ void TaskTreePrivate::stop()
|
||||
QT_ASSERT(m_root, return);
|
||||
if (!m_runtimeRoot)
|
||||
return;
|
||||
stop(m_runtimeRoot.get());
|
||||
stopTask(m_runtimeRoot.get());
|
||||
m_runtimeRoot.reset();
|
||||
emitDone(DoneWith::Cancel);
|
||||
}
|
||||
@ -2102,7 +2165,7 @@ std::optional<Loop> RuntimeIteration::loop() const
|
||||
return m_container->m_containerNode.m_loop;
|
||||
}
|
||||
|
||||
void RuntimeIteration::deleteChild(RuntimeTask *task)
|
||||
void RuntimeIteration::removeChild(RuntimeTask *task)
|
||||
{
|
||||
const auto it = std::find_if(m_children.cbegin(), m_children.cend(), [task](const auto &ptr) {
|
||||
return ptr.get() == task;
|
||||
@ -2112,7 +2175,7 @@ void RuntimeIteration::deleteChild(RuntimeTask *task)
|
||||
}
|
||||
|
||||
static std::vector<TaskNode> createChildren(TaskTreePrivate *taskTreePrivate,
|
||||
const QList<GroupItem> &children)
|
||||
const GroupItems &children)
|
||||
{
|
||||
std::vector<TaskNode> result;
|
||||
result.reserve(children.size());
|
||||
@ -2179,36 +2242,21 @@ void RuntimeContainer::deleteFinishedIterations()
|
||||
}
|
||||
}
|
||||
|
||||
SetupResult TaskTreePrivate::start(RuntimeContainer *container)
|
||||
void TaskTreePrivate::continueContainer(RuntimeContainer *container)
|
||||
{
|
||||
const ContainerNode &containerNode = container->m_containerNode;
|
||||
SetupResult startAction = SetupResult::Continue;
|
||||
if (containerNode.m_groupHandler.m_setupHandler) {
|
||||
startAction = invokeHandler(container, containerNode.m_groupHandler.m_setupHandler);
|
||||
if (startAction != SetupResult::Continue) {
|
||||
if (isProgressive(container))
|
||||
advanceProgress(containerNode.m_taskCount);
|
||||
// Non-Continue SetupResult takes precedence over the workflow policy.
|
||||
container->m_successBit = startAction == SetupResult::StopWithSuccess;
|
||||
}
|
||||
}
|
||||
return continueStart(container, startAction);
|
||||
}
|
||||
|
||||
SetupResult TaskTreePrivate::continueStart(RuntimeContainer *container, SetupResult startAction)
|
||||
{
|
||||
const SetupResult groupAction = startAction == SetupResult::Continue ? startChildren(container)
|
||||
: startAction;
|
||||
if (groupAction == SetupResult::Continue)
|
||||
return groupAction;
|
||||
|
||||
const bool bit = container->updateSuccessBit(groupAction == SetupResult::StopWithSuccess);
|
||||
RuntimeIteration *parentIteration = container->parentIteration();
|
||||
RuntimeTask *parentTask = container->m_parentTask;
|
||||
if (parentTask->m_setupResult == SetupResult::Continue)
|
||||
startChildren(container);
|
||||
if (parentTask->m_setupResult == SetupResult::Continue)
|
||||
return;
|
||||
|
||||
const bool bit = container->updateSuccessBit(parentTask->m_setupResult == SetupResult::StopWithSuccess);
|
||||
RuntimeIteration *parentIteration = container->parentIteration();
|
||||
QT_CHECK(parentTask);
|
||||
const bool result = invokeDoneHandler(container, bit ? DoneWith::Success : DoneWith::Error);
|
||||
parentTask->m_setupResult = toSetupResult(result);
|
||||
if (parentIteration) {
|
||||
parentIteration->deleteChild(parentTask);
|
||||
parentIteration->removeChild(parentTask);
|
||||
if (!parentIteration->m_container->isStarting())
|
||||
childDone(parentIteration, result);
|
||||
} else {
|
||||
@ -2216,10 +2264,9 @@ SetupResult TaskTreePrivate::continueStart(RuntimeContainer *container, SetupRes
|
||||
m_runtimeRoot.reset();
|
||||
emitDone(result ? DoneWith::Success : DoneWith::Error);
|
||||
}
|
||||
return toSetupResult(result);
|
||||
}
|
||||
|
||||
SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container)
|
||||
void TaskTreePrivate::startChildren(RuntimeContainer *container)
|
||||
{
|
||||
const ContainerNode &containerNode = container->m_containerNode;
|
||||
const int childCount = int(containerNode.m_children.size());
|
||||
@ -2228,7 +2275,8 @@ SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container)
|
||||
if (container->m_shouldIterate && !invokeLoopHandler(container)) {
|
||||
if (isProgressive(container))
|
||||
advanceProgress(containerNode.m_taskCount);
|
||||
return toSetupResult(container->m_successBit);
|
||||
container->m_parentTask->m_setupResult = toSetupResult(container->m_successBit);
|
||||
return;
|
||||
}
|
||||
container->m_iterations.emplace_back(
|
||||
std::make_unique<RuntimeIteration>(container->m_iterationCount, container));
|
||||
@ -2247,34 +2295,34 @@ SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container)
|
||||
std::make_unique<RuntimeIteration>(container->m_iterationCount, container));
|
||||
++container->m_iterationCount;
|
||||
} else if (container->m_iterations.empty()) {
|
||||
return toSetupResult(container->m_successBit);
|
||||
container->m_parentTask->m_setupResult = toSetupResult(container->m_successBit);
|
||||
return;
|
||||
} else {
|
||||
return SetupResult::Continue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
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};
|
||||
iteration->m_children.emplace_back(newTask);
|
||||
const std::shared_ptr<RuntimeTask> task(
|
||||
new RuntimeTask{containerNode.m_children.at(container->m_nextToStart), iteration});
|
||||
iteration->m_children.emplace_back(task);
|
||||
++container->m_runningChildren;
|
||||
++container->m_nextToStart;
|
||||
|
||||
const SetupResult startAction = start(newTask);
|
||||
if (startAction == SetupResult::Continue)
|
||||
startTask(task);
|
||||
if (task->m_setupResult == SetupResult::Continue)
|
||||
continue;
|
||||
|
||||
const SetupResult finalizeAction = childDone(iteration,
|
||||
startAction == SetupResult::StopWithSuccess);
|
||||
if (finalizeAction != SetupResult::Continue)
|
||||
return finalizeAction;
|
||||
task->m_parentIteration->removeChild(task.get());
|
||||
childDone(iteration, task->m_setupResult == SetupResult::StopWithSuccess);
|
||||
if (container->m_parentTask->m_setupResult != SetupResult::Continue)
|
||||
return;
|
||||
}
|
||||
return SetupResult::Continue;
|
||||
}
|
||||
|
||||
SetupResult TaskTreePrivate::childDone(RuntimeIteration *iteration, bool success)
|
||||
void TaskTreePrivate::childDone(RuntimeIteration *iteration, bool success)
|
||||
{
|
||||
RuntimeContainer *container = iteration->m_container;
|
||||
const WorkflowPolicy &workflowPolicy = container->m_containerNode.m_workflowPolicy;
|
||||
@ -2283,25 +2331,23 @@ SetupResult TaskTreePrivate::childDone(RuntimeIteration *iteration, bool success
|
||||
|| (workflowPolicy == WorkflowPolicy::StopOnError && !success);
|
||||
++iteration->m_doneCount;
|
||||
--container->m_runningChildren;
|
||||
if (shouldStop)
|
||||
stop(container);
|
||||
|
||||
const bool updatedSuccess = container->updateSuccessBit(success);
|
||||
const SetupResult startAction = shouldStop ? toSetupResult(updatedSuccess)
|
||||
: SetupResult::Continue;
|
||||
container->m_parentTask->m_setupResult = shouldStop ? toSetupResult(updatedSuccess) : SetupResult::Continue;
|
||||
if (shouldStop)
|
||||
stopContainer(container);
|
||||
|
||||
if (container->isStarting())
|
||||
return startAction;
|
||||
return continueStart(container, startAction);
|
||||
return;
|
||||
continueContainer(container);
|
||||
}
|
||||
|
||||
void TaskTreePrivate::stop(RuntimeContainer *container)
|
||||
void TaskTreePrivate::stopContainer(RuntimeContainer *container)
|
||||
{
|
||||
const ContainerNode &containerNode = container->m_containerNode;
|
||||
for (auto &iteration : container->m_iterations) {
|
||||
for (auto &child : iteration->m_children) {
|
||||
++iteration->m_doneCount;
|
||||
stop(child.get());
|
||||
stopTask(child.get());
|
||||
}
|
||||
|
||||
if (iteration->m_isProgressive) {
|
||||
@ -2332,8 +2378,6 @@ bool TaskTreePrivate::invokeDoneHandler(RuntimeContainer *container, DoneWith do
|
||||
if (groupHandler.m_doneHandler && shouldCall(groupHandler.m_callDoneIf, doneWith))
|
||||
result = invokeHandler(container, groupHandler.m_doneHandler, doneWith);
|
||||
container->m_callStorageDoneHandlersOnDestruction = true;
|
||||
// TODO: is it needed?
|
||||
container->m_parentTask->m_container.reset();
|
||||
return result == DoneResult::Success;
|
||||
}
|
||||
|
||||
@ -2351,61 +2395,69 @@ bool TaskTreePrivate::invokeLoopHandler(RuntimeContainer *container)
|
||||
return container->m_shouldIterate;
|
||||
}
|
||||
|
||||
SetupResult TaskTreePrivate::start(RuntimeTask *node)
|
||||
void TaskTreePrivate::startTask(const std::shared_ptr<RuntimeTask> &node)
|
||||
{
|
||||
if (!node->m_taskNode.isTask()) {
|
||||
node->m_container.emplace(node->m_taskNode.m_container, node);
|
||||
return start(&*node->m_container);
|
||||
const ContainerNode &containerNode = node->m_taskNode.m_container;
|
||||
node->m_container.emplace(containerNode, node.get());
|
||||
RuntimeContainer *container = &*node->m_container;
|
||||
if (containerNode.m_groupHandler.m_setupHandler) {
|
||||
container->m_parentTask->m_setupResult = invokeHandler(container, containerNode.m_groupHandler.m_setupHandler);
|
||||
if (container->m_parentTask->m_setupResult != SetupResult::Continue) {
|
||||
if (isProgressive(container))
|
||||
advanceProgress(containerNode.m_taskCount);
|
||||
// Non-Continue SetupResult takes precedence over the workflow policy.
|
||||
container->m_successBit = container->m_parentTask->m_setupResult == SetupResult::StopWithSuccess;
|
||||
}
|
||||
}
|
||||
continueContainer(container);
|
||||
return;
|
||||
}
|
||||
|
||||
const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler;
|
||||
node->m_task.reset(handler.m_createHandler());
|
||||
const SetupResult startAction = handler.m_setupHandler
|
||||
node->m_setupResult = handler.m_setupHandler
|
||||
? invokeHandler(node->m_parentIteration, handler.m_setupHandler, *node->m_task.get())
|
||||
: SetupResult::Continue;
|
||||
if (startAction != SetupResult::Continue) {
|
||||
if (node->m_setupResult != SetupResult::Continue) {
|
||||
if (node->m_parentIteration->m_isProgressive)
|
||||
advanceProgress(1);
|
||||
node->m_parentIteration->deleteChild(node);
|
||||
return startAction;
|
||||
node->m_parentIteration->removeChild(node.get());
|
||||
return;
|
||||
}
|
||||
const std::shared_ptr<SetupResult> unwindAction
|
||||
= std::make_shared<SetupResult>(SetupResult::Continue);
|
||||
QObject::connect(node->m_task.get(), &TaskInterface::done,
|
||||
q, [this, node, unwindAction](DoneResult doneResult) {
|
||||
const bool result = invokeDoneHandler(node, toDoneWith(doneResult));
|
||||
q, [this, node](DoneResult doneResult) {
|
||||
const bool result = invokeTaskDoneHandler(node.get(), toDoneWith(doneResult));
|
||||
node->m_setupResult = toSetupResult(result);
|
||||
QObject::disconnect(node->m_task.get(), &TaskInterface::done, q, nullptr);
|
||||
node->m_task.release()->deleteLater();
|
||||
RuntimeIteration *parentIteration = node->m_parentIteration;
|
||||
parentIteration->deleteChild(node);
|
||||
if (parentIteration->m_container->isStarting()) {
|
||||
*unwindAction = toSetupResult(result);
|
||||
} else {
|
||||
childDone(parentIteration, result);
|
||||
bumpAsyncCount();
|
||||
}
|
||||
});
|
||||
if (parentIteration->m_container->isStarting())
|
||||
return;
|
||||
|
||||
parentIteration->removeChild(node.get());
|
||||
childDone(parentIteration, result);
|
||||
bumpAsyncCount();
|
||||
});
|
||||
node->m_task->start();
|
||||
return *unwindAction;
|
||||
}
|
||||
|
||||
void TaskTreePrivate::stop(RuntimeTask *node)
|
||||
void TaskTreePrivate::stopTask(RuntimeTask *node)
|
||||
{
|
||||
if (!node->m_task) {
|
||||
if (!node->m_container)
|
||||
return;
|
||||
stop(&*node->m_container);
|
||||
stopContainer(&*node->m_container);
|
||||
node->m_container->updateSuccessBit(false);
|
||||
invokeDoneHandler(&*node->m_container, DoneWith::Cancel);
|
||||
return;
|
||||
}
|
||||
|
||||
invokeDoneHandler(node, DoneWith::Cancel);
|
||||
invokeTaskDoneHandler(node, DoneWith::Cancel);
|
||||
node->m_task.reset();
|
||||
}
|
||||
|
||||
bool TaskTreePrivate::invokeDoneHandler(RuntimeTask *node, DoneWith doneWith)
|
||||
bool TaskTreePrivate::invokeTaskDoneHandler(RuntimeTask *node, DoneWith doneWith)
|
||||
{
|
||||
DoneResult result = toDoneResult(doneWith);
|
||||
const GroupItem::TaskHandler &handler = node->m_taskNode.m_taskHandler;
|
||||
@ -2608,7 +2660,8 @@ bool TaskTreePrivate::invokeDoneHandler(RuntimeTask *node, DoneWith doneWith)
|
||||
|
||||
\code
|
||||
const auto onSetup = [](QProcess &process) {
|
||||
process.setCommand({"sleep", {"3"}});
|
||||
process.setProgram("sleep");
|
||||
process.setArguments({"3"});
|
||||
};
|
||||
const Group root {
|
||||
QProcessTask(onSetup)
|
||||
@ -2655,7 +2708,8 @@ bool TaskTreePrivate::invokeDoneHandler(RuntimeTask *node, DoneWith doneWith)
|
||||
|
||||
\code
|
||||
const auto onSetup = [](QProcess &process) {
|
||||
process.setCommand({"sleep", {"3"}});
|
||||
process.setProgram("sleep");
|
||||
process.setArguments({"3"});
|
||||
};
|
||||
const auto onDone = [](const QProcess &process, DoneWith result) {
|
||||
if (result == DoneWith::Success)
|
||||
@ -3272,9 +3326,6 @@ DoneWith TaskTree::runBlocking(const QFuture<void> &future)
|
||||
/*!
|
||||
Constructs a temporary task tree using the passed \a recipe and runs it blocking.
|
||||
|
||||
The optionally provided \a timeout is used to cancel the tree automatically after
|
||||
\a timeout milliseconds have passed.
|
||||
|
||||
Returns DoneWith::Success if the task tree finished successfully;
|
||||
otherwise returns DoneWith::Error.
|
||||
|
||||
@ -3283,24 +3334,22 @@ DoneWith TaskTree::runBlocking(const QFuture<void> &future)
|
||||
|
||||
\sa start()
|
||||
*/
|
||||
DoneWith TaskTree::runBlocking(const Group &recipe, milliseconds timeout)
|
||||
DoneWith TaskTree::runBlocking(const Group &recipe)
|
||||
{
|
||||
QPromise<void> dummy;
|
||||
dummy.start();
|
||||
return TaskTree::runBlocking(recipe, dummy.future(), timeout);
|
||||
return TaskTree::runBlocking(recipe, dummy.future());
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload runBlocking(const Group &recipe, milliseconds timeout)
|
||||
\overload runBlocking(const Group &recipe)
|
||||
|
||||
The passed \a future is used for listening to the cancel event.
|
||||
When the task tree is canceled, this method cancels the passed \a future.
|
||||
*/
|
||||
DoneWith TaskTree::runBlocking(const Group &recipe, const QFuture<void> &future, milliseconds timeout)
|
||||
DoneWith TaskTree::runBlocking(const Group &recipe, const QFuture<void> &future)
|
||||
{
|
||||
const Group root = timeout == milliseconds::max() ? recipe
|
||||
: Group { recipe.withTimeout(timeout) };
|
||||
TaskTree taskTree(root);
|
||||
TaskTree taskTree(recipe);
|
||||
return taskTree.runBlocking(future);
|
||||
}
|
||||
|
||||
@ -3613,6 +3662,11 @@ void TimeoutTaskAdapter::start()
|
||||
});
|
||||
}
|
||||
|
||||
ExecutableItem timeoutTask(const std::chrono::milliseconds &timeout, DoneResult result)
|
||||
{
|
||||
return TimeoutTask([timeout](std::chrono::milliseconds &t) { t = timeout; }, result);
|
||||
}
|
||||
|
||||
/*!
|
||||
\typealias Tasking::TaskTreeTask
|
||||
|
||||
|
@ -29,7 +29,13 @@ class QFuture;
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
Q_NAMESPACE
|
||||
class Do;
|
||||
class For;
|
||||
class Group;
|
||||
class GroupItem;
|
||||
using GroupItems = QList<GroupItem>;
|
||||
|
||||
Q_NAMESPACE_EXPORT(TASKING_EXPORT)
|
||||
|
||||
// WorkflowPolicy:
|
||||
// 1. When all children finished with success -> report success, otherwise:
|
||||
@ -192,6 +198,17 @@ class Storage final : public StorageBase
|
||||
{
|
||||
public:
|
||||
Storage() : StorageBase(Storage::ctor(), Storage::dtor()) {}
|
||||
#if __cplusplus >= 201803L // C++20: Allow pack expansion in lambda init-capture.
|
||||
template <typename ...Args>
|
||||
Storage(const Args &...args)
|
||||
: StorageBase([...args = args] { return new StorageStruct(args...); }, Storage::dtor()) {}
|
||||
#else // C++17
|
||||
template <typename ...Args>
|
||||
Storage(const Args &...args)
|
||||
: StorageBase([argsTuple = std::tuple(args...)] {
|
||||
return std::apply([](const Args &...arguments) { return new StorageStruct(arguments...); }, argsTuple);
|
||||
}, Storage::dtor()) {}
|
||||
#endif
|
||||
StorageStruct &operator*() const noexcept { return *activeStorage(); }
|
||||
StorageStruct *operator->() const noexcept { return activeStorage(); }
|
||||
StorageStruct *activeStorage() const {
|
||||
@ -219,7 +236,7 @@ public:
|
||||
, m_storageList{storage} {}
|
||||
|
||||
// TODO: Add tests.
|
||||
GroupItem(const QList<GroupItem> &children) : m_type(Type::List) { addChildren(children); }
|
||||
GroupItem(const GroupItems &children) : m_type(Type::List) { addChildren(children); }
|
||||
GroupItem(std::initializer_list<GroupItem> children) : m_type(Type::List) { addChildren(children); }
|
||||
|
||||
protected:
|
||||
@ -267,7 +284,7 @@ protected:
|
||||
GroupItem(const TaskHandler &handler)
|
||||
: m_type(Type::TaskHandler)
|
||||
, m_taskHandler(handler) {}
|
||||
void addChildren(const QList<GroupItem> &children);
|
||||
void addChildren(const GroupItems &children);
|
||||
|
||||
static GroupItem groupHandler(const GroupHandler &handler) { return GroupItem({handler}); }
|
||||
|
||||
@ -283,14 +300,14 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
|
||||
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;
|
||||
GroupItems m_children;
|
||||
GroupData m_groupData;
|
||||
QList<StorageBase> m_storageList;
|
||||
TaskHandler m_taskHandler;
|
||||
@ -299,42 +316,38 @@ private:
|
||||
class TASKING_EXPORT ExecutableItem : public GroupItem
|
||||
{
|
||||
public:
|
||||
ExecutableItem withTimeout(std::chrono::milliseconds timeout,
|
||||
const std::function<void()> &handler = {}) const;
|
||||
ExecutableItem withLog(const QString &logName) const;
|
||||
Group withTimeout(std::chrono::milliseconds timeout,
|
||||
const std::function<void()> &handler = {}) const;
|
||||
Group withLog(const QString &logName) const;
|
||||
template <typename SenderSignalPairGetter>
|
||||
ExecutableItem withCancel(SenderSignalPairGetter &&getter) const
|
||||
{
|
||||
const auto connectWrapper = [getter](QObject *guard, const std::function<void()> &trigger) {
|
||||
const auto senderSignalPair = getter();
|
||||
QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
|
||||
trigger();
|
||||
}, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
||||
};
|
||||
return withCancelImpl(connectWrapper);
|
||||
}
|
||||
Group withCancel(SenderSignalPairGetter &&getter, std::initializer_list<GroupItem> postCancelRecipe = {}) const;
|
||||
template <typename SenderSignalPairGetter>
|
||||
Group withAccept(SenderSignalPairGetter &&getter) const;
|
||||
|
||||
protected:
|
||||
ExecutableItem() = default;
|
||||
ExecutableItem(const TaskHandler &handler) : GroupItem(handler) {}
|
||||
|
||||
private:
|
||||
TASKING_EXPORT friend ExecutableItem operator!(const ExecutableItem &item);
|
||||
TASKING_EXPORT friend ExecutableItem operator&&(const ExecutableItem &first,
|
||||
TASKING_EXPORT friend Group operator!(const ExecutableItem &item);
|
||||
TASKING_EXPORT friend Group operator&&(const ExecutableItem &first,
|
||||
const ExecutableItem &second);
|
||||
TASKING_EXPORT friend ExecutableItem operator||(const ExecutableItem &first,
|
||||
TASKING_EXPORT friend Group 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);
|
||||
TASKING_EXPORT friend Group operator&&(const ExecutableItem &item, DoneResult result);
|
||||
TASKING_EXPORT friend Group operator||(const ExecutableItem &item, DoneResult result);
|
||||
|
||||
ExecutableItem withCancelImpl(
|
||||
Group withCancelImpl(
|
||||
const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper,
|
||||
const GroupItems &postCancelRecipe) const;
|
||||
Group withAcceptImpl(
|
||||
const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT Group : public ExecutableItem
|
||||
{
|
||||
public:
|
||||
Group(const QList<GroupItem> &children) { addChildren(children); }
|
||||
Group(const GroupItems &children) { addChildren(children); }
|
||||
Group(std::initializer_list<GroupItem> children) { addChildren(children); }
|
||||
|
||||
// GroupData related:
|
||||
@ -367,7 +380,7 @@ private:
|
||||
template <typename Handler>
|
||||
static GroupDoneHandler wrapGroupDone(Handler &&handler)
|
||||
{
|
||||
static constexpr bool isDoneResultType = std::is_same_v<Handler, DoneResult>;
|
||||
static constexpr bool isDoneResultType = std::is_same_v<std::decay_t<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>();
|
||||
@ -399,6 +412,31 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename SenderSignalPairGetter>
|
||||
Group ExecutableItem::withCancel(SenderSignalPairGetter &&getter,
|
||||
std::initializer_list<GroupItem> postCancelRecipe) const
|
||||
{
|
||||
const auto connectWrapper = [getter](QObject *guard, const std::function<void()> &trigger) {
|
||||
const auto senderSignalPair = getter();
|
||||
QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
|
||||
trigger();
|
||||
}, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
||||
};
|
||||
return withCancelImpl(connectWrapper, postCancelRecipe);
|
||||
}
|
||||
|
||||
template <typename SenderSignalPairGetter>
|
||||
Group ExecutableItem::withAccept(SenderSignalPairGetter &&getter) const
|
||||
{
|
||||
const auto connectWrapper = [getter](QObject *guard, const std::function<void()> &trigger) {
|
||||
const auto senderSignalPair = getter();
|
||||
QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
|
||||
trigger();
|
||||
}, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
|
||||
};
|
||||
return withAcceptImpl(connectWrapper);
|
||||
}
|
||||
|
||||
template <typename Handler>
|
||||
static GroupItem onGroupSetup(Handler &&handler)
|
||||
{
|
||||
@ -411,22 +449,11 @@ static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDone
|
||||
return Group::onGroupDone(std::forward<Handler>(handler), callDoneIf);
|
||||
}
|
||||
|
||||
class TASKING_EXPORT ParallelLimitFunctor
|
||||
{
|
||||
public:
|
||||
// Default: 1 (sequential). 0 means unlimited (parallel).
|
||||
GroupItem operator()(int limit) const;
|
||||
};
|
||||
// Default: 1 (sequential). 0 means unlimited (parallel).
|
||||
TASKING_EXPORT GroupItem parallelLimit(int limit);
|
||||
|
||||
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;
|
||||
// Default: WorkflowPolicy::StopOnError.
|
||||
TASKING_EXPORT GroupItem workflowPolicy(WorkflowPolicy policy);
|
||||
|
||||
TASKING_EXPORT extern const GroupItem sequential;
|
||||
TASKING_EXPORT extern const GroupItem parallel;
|
||||
@ -444,42 +471,39 @@ TASKING_EXPORT extern const GroupItem nullItem;
|
||||
TASKING_EXPORT extern const ExecutableItem successItem;
|
||||
TASKING_EXPORT extern const ExecutableItem errorItem;
|
||||
|
||||
class TASKING_EXPORT For : public Group
|
||||
class TASKING_EXPORT For final
|
||||
{
|
||||
public:
|
||||
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}) {}
|
||||
explicit For(const Loop &loop) : m_loop(loop) {}
|
||||
|
||||
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;
|
||||
}
|
||||
TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Loop m_loop;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT Forever final : public For
|
||||
class When;
|
||||
|
||||
class TASKING_EXPORT Do final
|
||||
{
|
||||
public:
|
||||
Forever(const QList<GroupItem> &children) : For(LoopForever(), children) {}
|
||||
Forever(std::initializer_list<GroupItem> children) : For(LoopForever(), children) {}
|
||||
explicit Do(const GroupItems &children) : m_children(children) {}
|
||||
explicit Do(std::initializer_list<GroupItem> children) : m_children(children) {}
|
||||
|
||||
private:
|
||||
TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem);
|
||||
TASKING_EXPORT friend Group operator>>(const When &whenItem, const Do &doItem);
|
||||
|
||||
GroupItem m_children;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT Forever final : public ExecutableItem
|
||||
{
|
||||
public:
|
||||
explicit Forever(const GroupItems &children)
|
||||
{ addChildren({ For (LoopForever()) >> Do { children } } ); }
|
||||
explicit Forever(std::initializer_list<GroupItem> children)
|
||||
{ addChildren({ For (LoopForever()) >> Do { children } } ); }
|
||||
};
|
||||
|
||||
// Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount()
|
||||
@ -565,7 +589,7 @@ private:
|
||||
static InterfaceDoneHandler wrapDone(Handler &&handler) {
|
||||
if constexpr (std::is_same_v<Handler, TaskDoneHandler>)
|
||||
return {}; // User passed {} for the done handler.
|
||||
static constexpr bool isDoneResultType = std::is_same_v<Handler, DoneResult>;
|
||||
static constexpr bool isDoneResultType = std::is_same_v<std::decay_t<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 &>();
|
||||
@ -619,6 +643,21 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Task>
|
||||
class SimpleTaskAdapter final : public TaskAdapter<Task>
|
||||
{
|
||||
public:
|
||||
SimpleTaskAdapter() { this->connect(this->task(), &Task::done, this, &TaskInterface::done); }
|
||||
void start() final { this->task()->start(); }
|
||||
};
|
||||
|
||||
// A convenient helper, when:
|
||||
// 1. Task is derived from QObject.
|
||||
// 2. Task::start() method starts the task.
|
||||
// 3. Task::done(DoneResult) signal is emitted when the task is finished.
|
||||
template <typename Task>
|
||||
using SimpleCustomTask = CustomTask<SimpleTaskAdapter<Task>>;
|
||||
|
||||
class TASKING_EXPORT TaskTree final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -639,10 +678,8 @@ public:
|
||||
// Don't use it in main thread. To be used in non-main threads or in auto tests.
|
||||
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());
|
||||
static DoneWith runBlocking(const Group &recipe);
|
||||
static DoneWith runBlocking(const Group &recipe, const QFuture<void> &future);
|
||||
|
||||
int asyncCount() const;
|
||||
int taskCount() const;
|
||||
@ -710,6 +747,9 @@ private:
|
||||
using TaskTreeTask = CustomTask<TaskTreeTaskAdapter>;
|
||||
using TimeoutTask = CustomTask<TimeoutTaskAdapter>;
|
||||
|
||||
TASKING_EXPORT ExecutableItem timeoutTask(const std::chrono::milliseconds &timeout,
|
||||
DoneResult result = DoneResult::Error);
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -52,16 +52,6 @@ TcpSocket::~TcpSocket()
|
||||
}
|
||||
}
|
||||
|
||||
TcpSocketTaskAdapter::TcpSocketTaskAdapter()
|
||||
{
|
||||
connect(task(), &TcpSocket::done, this, &TaskInterface::done);
|
||||
}
|
||||
|
||||
void TcpSocketTaskAdapter::start()
|
||||
{
|
||||
task()->start();
|
||||
}
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -56,14 +56,7 @@ private:
|
||||
QAbstractSocket::SocketError m_error = QAbstractSocket::UnknownSocketError;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT TcpSocketTaskAdapter final : public TaskAdapter<TcpSocket>
|
||||
{
|
||||
public:
|
||||
TcpSocketTaskAdapter();
|
||||
void start() final;
|
||||
};
|
||||
|
||||
using TcpSocketTask = CustomTask<TcpSocketTaskAdapter>;
|
||||
using TcpSocketTask = SimpleCustomTask<TcpSocket>;
|
||||
|
||||
} // namespace Tasking
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user