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:
Jarek Kobus 2025-04-08 15:06:20 +02:00 committed by Qt Cherry-pick Bot
parent aac28b280f
commit 2eb2ae4cf4
11 changed files with 363 additions and 289 deletions

View File

@ -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),

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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();
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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