From 2ab472f6cbcb8505a84dac8acd8e6795b9af0b8d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 8 Apr 2025 15:06:20 +0200 Subject: [PATCH] TaskTree: Sync sources Source sync Creator -> AssetDownloader. Pick-to: 6.9 6.8 Change-Id: Ic503ba04c272718611a4a266bef40e73517e32fd Reviewed-by: Ivan Solovev --- src/assets/downloader/assetdownloader.cpp | 6 +- src/assets/downloader/tasking/barrier.h | 36 +- .../downloader/tasking/concurrentcall.h | 2 +- src/assets/downloader/tasking/conditional.cpp | 27 +- src/assets/downloader/tasking/conditional.h | 4 +- .../downloader/tasking/networkquery.cpp | 7 +- src/assets/downloader/tasking/networkquery.h | 13 +- src/assets/downloader/tasking/tasktree.cpp | 352 ++++++++++-------- src/assets/downloader/tasking/tasktree.h | 186 +++++---- src/assets/downloader/tasking/tcpsocket.cpp | 10 - src/assets/downloader/tasking/tcpsocket.h | 9 +- 11 files changed, 363 insertions(+), 289 deletions(-) diff --git a/src/assets/downloader/assetdownloader.cpp b/src/assets/downloader/assetdownloader.cpp index 2964b10e088..cb3493aa1d4 100644 --- a/src/assets/downloader/assetdownloader.cpp +++ b/src/assets/downloader/assetdownloader.cpp @@ -537,8 +537,7 @@ void AssetDownloader::start() onGroupSetup(onSkipIfAllAssetsPresent), NetworkQueryTask(onZipDownloadSetup, onZipDownloadDone), ConcurrentCallTask(onUnzipSetup, onUnzipDone), - For { - downloadIterator, + For (downloadIterator) >> Do { parallelIdealThreadCountLimit, onGroupSetup(onAssetsDownloadGroupSetup), Group { @@ -547,8 +546,7 @@ void AssetDownloader::start() ConcurrentCallTask(onAssetWriteSetup, onAssetWriteDone) } }, - For { - copyIterator, + For (copyIterator) >> Do { parallelIdealThreadCountLimit, onGroupSetup(onAssetsCopyGroupSetup), ConcurrentCallTask(onAssetCopySetup, onAssetCopyDone, CallDoneIf::Success), diff --git a/src/assets/downloader/tasking/barrier.h b/src/assets/downloader/tasking/barrier.h index c8a6627a8c4..d489d2722a4 100644 --- a/src/assets/downloader/tasking/barrier.h +++ b/src/assets/downloader/tasking/barrier.h @@ -49,14 +49,7 @@ private: int m_current = -1; }; -class TASKING_EXPORT BarrierTaskAdapter : public TaskAdapter -{ -public: - BarrierTaskAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); } - void start() final { task()->start(); } -}; - -using BarrierTask = CustomTask; +using BarrierTask = SimpleCustomTask; template class SharedBarrier @@ -90,21 +83,42 @@ ExecutableItem waitForBarrierTask(const MultiBarrier &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 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 +ExecutableItem signalAwaiter(const typename QtPrivate::FunctionPointer::Object *sender, Signal signal) +{ + return BarrierTask([sender, signal](Barrier &barrier) { + QObject::connect(sender, signal, &barrier, &Barrier::advance, Qt::SingleShotConnection); + }); +} + +using BarrierKickerGetter = std::function; + +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 diff --git a/src/assets/downloader/tasking/concurrentcall.h b/src/assets/downloader/tasking/concurrentcall.h index a43d77be15d..cc701297d13 100644 --- a/src/assets/downloader/tasking/concurrentcall.h +++ b/src/assets/downloader/tasking/concurrentcall.h @@ -39,7 +39,7 @@ public: template void setConcurrentCallData(Function &&function, Args &&...args) { - return wrapConcurrent(std::forward(function), std::forward(args)...); + wrapConcurrent(std::forward(function), std::forward(args)...); } void setThreadPool(QThreadPool *pool) { m_threadPool = pool; } ResultType result() const diff --git a/src/assets/downloader/tasking/conditional.cpp b/src/assets/downloader/tasking/conditional.cpp index 73636d659dd..24a03fb703e 100644 --- a/src/assets/downloader/tasking/conditional.cpp +++ b/src/assets/downloader/tasking/conditional.cpp @@ -8,36 +8,19 @@ QT_BEGIN_NAMESPACE namespace Tasking { -static Group conditionRecipe(const Storage &bodyExecutedStorage, - const ConditionData &condition) +static Group conditionRecipe(const Storage &bodyExecutedStorage, const ConditionData &condition) { - Storage 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 &conditions) { Storage bodyExecutedStorage; - QList recipes; + GroupItems recipes; for (const ConditionData &condition : conditions) recipes << conditionRecipe(bodyExecutedStorage, condition); diff --git a/src/assets/downloader/tasking/conditional.h b/src/assets/downloader/tasking/conditional.h index ab30b492f3a..52fdfd64cb7 100644 --- a/src/assets/downloader/tasking/conditional.h +++ b/src/assets/downloader/tasking/conditional.h @@ -62,7 +62,7 @@ private: class TASKING_EXPORT Else { public: - explicit Else(const QList &children) : m_body({children}) {} + explicit Else(const GroupItems &children) : m_body({children}) {} explicit Else(std::initializer_list children) : m_body({children}) {} private: @@ -73,7 +73,7 @@ private: class TASKING_EXPORT Then { public: - explicit Then(const QList &children) : m_body({children}) {} + explicit Then(const GroupItems &children) : m_body({children}) {} explicit Then(std::initializer_list children) : m_body({children}) {} private: diff --git a/src/assets/downloader/tasking/networkquery.cpp b/src/assets/downloader/tasking/networkquery.cpp index 1003e0c30a3..3f15fed90e0 100644 --- a/src/assets/downloader/tasking/networkquery.cpp +++ b/src/assets/downloader/tasking/networkquery.cpp @@ -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(); } } diff --git a/src/assets/downloader/tasking/networkquery.h b/src/assets/downloader/tasking/networkquery.h index 5deb1a4e7c7..2733fef8352 100644 --- a/src/assets/downloader/tasking/networkquery.h +++ b/src/assets/downloader/tasking/networkquery.h @@ -51,6 +51,10 @@ public: Q_SIGNALS: void started(); + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); +#if QT_CONFIG(ssl) + void sslErrors(const QList &errors); +#endif void done(DoneResult result); private: @@ -61,14 +65,7 @@ private: std::unique_ptr m_reply; }; -class TASKING_EXPORT NetworkQueryTaskAdapter : public TaskAdapter -{ -public: - NetworkQueryTaskAdapter() { connect(task(), &NetworkQuery::done, this, &TaskInterface::done); } - void start() final { task()->start(); } -}; - -using NetworkQueryTask = CustomTask; +using NetworkQueryTask = SimpleCustomTask; } // namespace Tasking diff --git a/src/assets/downloader/tasking/tasktree.cpp b/src/assets/downloader/tasking/tasktree.cpp index b89b0f52eee..f9d5cd46540 100644 --- a/src/assets/downloader/tasking/tasktree.cpp +++ b/src/assets/downloader/tasking/tasktree.cpp @@ -5,6 +5,7 @@ #include "tasktree.h" #include "barrier.h" +#include "conditional.h" #include #include @@ -319,6 +320,12 @@ private: \sa operator->(), operator*() */ +/*! + \typealias Tasking::GroupItems + + Type alias for QList. +*/ + /*! \class Tasking::GroupItem \inheaderfile solutions/tasking/tasktree.h @@ -383,7 +390,7 @@ private: */ /*! - \fn GroupItem::GroupItem(const QList &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 items) + \fn Tasking::GroupItem(std::initializer_list items) \overload - \sa GroupItem(const QList &items) + \sa GroupItem(const GroupItems &items) */ /*! @@ -508,7 +515,7 @@ private: */ /*! - \fn Group::Group(const QList &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 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 &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 &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 &handler) const +Group ExecutableItem::withTimeout(milliseconds timeout, + const std::function &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 &)> &connectWrapper, + const GroupItems &postCancelRecipe) const +{ + const Storage 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 &)> &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 &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> 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), std::forward(args)...); @@ -1878,7 +1940,7 @@ public: QSet m_storages; QHash m_storageHandlers; std::optional m_root; - std::unique_ptr m_runtimeRoot; // Keep me last in order to destruct first + std::shared_ptr 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() 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> m_children = {}; // Owning. + std::vector> 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 m_container = {}; // Owning. std::unique_ptr 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 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 createChildren(TaskTreePrivate *taskTreePrivate, - const QList &children) + const GroupItems &children) { std::vector 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(container->m_iterationCount, container)); @@ -2247,34 +2295,34 @@ SetupResult TaskTreePrivate::startChildren(RuntimeContainer *container) std::make_unique(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 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 &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 unwindAction - = std::make_shared(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 &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 &future) \sa start() */ -DoneWith TaskTree::runBlocking(const Group &recipe, milliseconds timeout) +DoneWith TaskTree::runBlocking(const Group &recipe) { QPromise 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 &future, milliseconds timeout) +DoneWith TaskTree::runBlocking(const Group &recipe, const QFuture &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 diff --git a/src/assets/downloader/tasking/tasktree.h b/src/assets/downloader/tasking/tasktree.h index 53595628c2f..75489ca87cc 100644 --- a/src/assets/downloader/tasking/tasktree.h +++ b/src/assets/downloader/tasking/tasktree.h @@ -29,7 +29,13 @@ class QFuture; namespace Tasking { -Q_NAMESPACE +class Do; +class For; +class Group; +class GroupItem; +using GroupItems = QList; + +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 + Storage(const Args &...args) + : StorageBase([...args = args] { return new StorageStruct(args...); }, Storage::dtor()) {} +#else // C++17 + template + 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 &children) : m_type(Type::List) { addChildren(children); } + GroupItem(const GroupItems &children) : m_type(Type::List) { addChildren(children); } GroupItem(std::initializer_list 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 &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 m_children; + GroupItems m_children; GroupData m_groupData; QList 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 &handler = {}) const; - ExecutableItem withLog(const QString &logName) const; + Group withTimeout(std::chrono::milliseconds timeout, + const std::function &handler = {}) const; + Group withLog(const QString &logName) const; template - ExecutableItem withCancel(SenderSignalPairGetter &&getter) const - { - const auto connectWrapper = [getter](QObject *guard, const std::function &trigger) { - const auto senderSignalPair = getter(); - QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] { - trigger(); - }, static_cast(Qt::QueuedConnection | Qt::SingleShotConnection)); - }; - return withCancelImpl(connectWrapper); - } + Group withCancel(SenderSignalPairGetter &&getter, std::initializer_list postCancelRecipe = {}) const; + template + 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 &)> &connectWrapper, + const GroupItems &postCancelRecipe) const; + Group withAcceptImpl( const std::function &)> &connectWrapper) const; }; class TASKING_EXPORT Group : public ExecutableItem { public: - Group(const QList &children) { addChildren(children); } + Group(const GroupItems &children) { addChildren(children); } Group(std::initializer_list children) { addChildren(children); } // GroupData related: @@ -367,7 +380,7 @@ private: template static GroupDoneHandler wrapGroupDone(Handler &&handler) { - static constexpr bool isDoneResultType = std::is_same_v; + static constexpr bool isDoneResultType = std::is_same_v, DoneResult>; // R, B, V, D stands for: Done[R]esult, [B]ool, [V]oid, [D]oneWith static constexpr bool isRD = isInvocable(); static constexpr bool isR = isInvocable(); @@ -399,6 +412,31 @@ private: } }; +template +Group ExecutableItem::withCancel(SenderSignalPairGetter &&getter, + std::initializer_list postCancelRecipe) const +{ + const auto connectWrapper = [getter](QObject *guard, const std::function &trigger) { + const auto senderSignalPair = getter(); + QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] { + trigger(); + }, static_cast(Qt::QueuedConnection | Qt::SingleShotConnection)); + }; + return withCancelImpl(connectWrapper, postCancelRecipe); +} + +template +Group ExecutableItem::withAccept(SenderSignalPairGetter &&getter) const +{ + const auto connectWrapper = [getter](QObject *guard, const std::function &trigger) { + const auto senderSignalPair = getter(); + QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] { + trigger(); + }, static_cast(Qt::QueuedConnection | Qt::SingleShotConnection)); + }; + return withAcceptImpl(connectWrapper); +} + template static GroupItem onGroupSetup(Handler &&handler) { @@ -411,22 +449,11 @@ static GroupItem onGroupDone(Handler &&handler, CallDoneIf callDoneIf = CallDone return Group::onGroupDone(std::forward(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 - For(const Loop &loop, const Args &...args) - : Group(withLoop(loop, args...)) { } - -protected: - For(const Loop &loop, const QList &children) : Group({loop, children}) {} - For(const Loop &loop, std::initializer_list children) : Group({loop, children}) {} + explicit For(const Loop &loop) : m_loop(loop) {} private: - template - QList withLoop(const Loop &loop, const Args &...args) { - QList children{GroupItem(loop)}; - appendChildren(std::make_tuple(args...), &children); - return children; - } + TASKING_EXPORT friend Group operator>>(const For &forItem, const Do &doItem); - template - void appendChildren(const Tuple &tuple, QList *children) { - constexpr auto TupleSize = std::tuple_size_v; - if constexpr (TupleSize > 0) { - // static_assert(workflowPolicyCount() <= 1, "Too many workflow policies in one group."); - children->append(std::get(tuple)); - if constexpr (N + 1 < TupleSize) - appendChildren(tuple, children); - } - } + Loop m_loop; }; -class TASKING_EXPORT Forever final : public For +class When; + +class TASKING_EXPORT Do final { public: - Forever(const QList &children) : For(LoopForever(), children) {} - Forever(std::initializer_list children) : For(LoopForever(), children) {} + explicit Do(const GroupItems &children) : m_children(children) {} + explicit Do(std::initializer_list 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 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) return {}; // User passed {} for the done handler. - static constexpr bool isDoneResultType = std::is_same_v; + static constexpr bool isDoneResultType = std::is_same_v, DoneResult>; // R, B, V, T, D stands for: Done[R]esult, [B]ool, [V]oid, [T]ask, [D]oneWith static constexpr bool isRTD = isInvocable(); static constexpr bool isRT = isInvocable(); @@ -619,6 +643,21 @@ private: } }; +template +class SimpleTaskAdapter final : public TaskAdapter +{ +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 +using SimpleCustomTask = CustomTask>; + 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 &future); - static DoneWith runBlocking(const Group &recipe, - std::chrono::milliseconds timeout = std::chrono::milliseconds::max()); - static DoneWith runBlocking(const Group &recipe, const QFuture &future, - std::chrono::milliseconds timeout = std::chrono::milliseconds::max()); + static DoneWith runBlocking(const Group &recipe); + static DoneWith runBlocking(const Group &recipe, const QFuture &future); int asyncCount() const; int taskCount() const; @@ -710,6 +747,9 @@ private: using TaskTreeTask = CustomTask; using TimeoutTask = CustomTask; +TASKING_EXPORT ExecutableItem timeoutTask(const std::chrono::milliseconds &timeout, + DoneResult result = DoneResult::Error); + } // namespace Tasking QT_END_NAMESPACE diff --git a/src/assets/downloader/tasking/tcpsocket.cpp b/src/assets/downloader/tasking/tcpsocket.cpp index 20cd8b8e2b8..19aab8fc714 100644 --- a/src/assets/downloader/tasking/tcpsocket.cpp +++ b/src/assets/downloader/tasking/tcpsocket.cpp @@ -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 diff --git a/src/assets/downloader/tasking/tcpsocket.h b/src/assets/downloader/tasking/tcpsocket.h index 9e17d703c9e..ec0069bf130 100644 --- a/src/assets/downloader/tasking/tcpsocket.h +++ b/src/assets/downloader/tasking/tcpsocket.h @@ -56,14 +56,7 @@ private: QAbstractSocket::SocketError m_error = QAbstractSocket::UnknownSocketError; }; -class TASKING_EXPORT TcpSocketTaskAdapter final : public TaskAdapter -{ -public: - TcpSocketTaskAdapter(); - void start() final; -}; - -using TcpSocketTask = CustomTask; +using TcpSocketTask = SimpleCustomTask; } // namespace Tasking