From 7fc99339697405fe1abfe6cc5cfd2d8bf195ae95 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 5 Dec 2022 11:20:57 +0100 Subject: [PATCH] TaskTree: Add a possibility to setup storage handlers Storage handlers make it possible to pass initial data into task tree and make it possible to retrieve the output data from task tree after the task tree finished. Change-Id: I0bcc12f5e552f55c1d5d89b81521ae9cb7b8b124 Reviewed-by: hjk Reviewed-by: --- src/libs/utils/tasktree.cpp | 76 ++++++++++++++++++---- src/libs/utils/tasktree.h | 30 +++++++++ tests/auto/utils/tasktree/tst_tasktree.cpp | 17 ++--- 3 files changed, 102 insertions(+), 21 deletions(-) diff --git a/src/libs/utils/tasktree.cpp b/src/libs/utils/tasktree.cpp index f1a4146c9da..62db76982f4 100644 --- a/src/libs/utils/tasktree.cpp +++ b/src/libs/utils/tasktree.cpp @@ -227,6 +227,11 @@ public: QTC_ASSERT(m_root, return); m_progressValue = 0; emitStartedAndProgress(); + // TODO: check storage handlers for not existing storages in tree + for (auto it = m_storageHandlers.cbegin(); it != m_storageHandlers.cend(); ++it) { + QTC_ASSERT(m_storages.contains(it.key()), qWarning("The registered storage doesn't " + "exist in task tree. Its handlers will never be called.")); + } m_root->start(); } void stop() { @@ -278,12 +283,36 @@ public: } return addedStorages; } + void callSetupHandler(TreeStorageBase storage, int storageId) { + callStorageHandler(storage, storageId, &StorageHandler::m_setupHandler); + } + void callDoneHandler(TreeStorageBase storage, int storageId) { + callStorageHandler(storage, storageId, &StorageHandler::m_doneHandler); + } + struct StorageHandler { + TaskTree::StorageVoidHandler m_setupHandler = {}; + TaskTree::StorageVoidHandler m_doneHandler = {}; + }; + typedef TaskTree::StorageVoidHandler StorageHandler::*HandlerPtr; // ptr to class member + void callStorageHandler(TreeStorageBase storage, int storageId, HandlerPtr ptr) + { + const auto it = m_storageHandlers.constFind(storage); + if (it == m_storageHandlers.constEnd()) + return; + GuardLocker locker(m_guard); + const StorageHandler storageHandler = *it; + storage.activateStorage(storageId); + if (storageHandler.*ptr) + (storageHandler.*ptr)(storage.activeStorageVoid()); + storage.activateStorage(0); + } TaskTree *q = nullptr; - std::unique_ptr m_root = nullptr; Guard m_guard; int m_progressValue = 0; QSet m_storages; + QHash m_storageHandlers; + std::unique_ptr m_root = nullptr; // Keep me last in order to destruct first }; TaskContainer::TaskContainer(TaskTreePrivate *taskTreePrivate, TaskContainer *parentContainer, @@ -481,27 +510,29 @@ void TaskContainer::updateSuccessBit(bool success) void TaskContainer::createStorages() { - // TODO: Don't create new storage for already created storages with the same shared pointer. QTC_CHECK(m_storageIdList.isEmpty()); - for (int i = 0; i < m_storageList.size(); ++i) - m_storageIdList << m_storageList[i].createStorage(); + for (int i = 0; i < m_storageList.size(); ++i) { + TreeStorageBase storage = m_storageList[i]; + const int storageId = storage.createStorage(); + m_storageIdList << storageId; + m_taskTreePrivate->callSetupHandler(storage, storageId); + } } void TaskContainer::deleteStorages() { - // TODO: Do the opposite - - for (int i = 0; i < m_storageIdList.size(); ++i) // iterate in reverse order? - m_storageList[i].deleteStorage(m_storageIdList.value(i)); - + for (int i = 0; i < m_storageIdList.size(); ++i) { // iterate in reverse order? + TreeStorageBase storage = m_storageList[i]; + const int storageId = m_storageIdList.value(i); + m_taskTreePrivate->callDoneHandler(storage, storageId); + storage.deleteStorage(storageId); + } m_storageIdList.clear(); } void TaskContainer::activateStorages() { - // TODO: check if the same shared storage was already activated. Don't activate recursively. - if (m_parentContainer) m_parentContainer->activateStorages(); @@ -511,8 +542,6 @@ void TaskContainer::activateStorages() void TaskContainer::deactivateStorages() { - // TODO: Do the opposite - for (int i = 0; i < m_storageList.size(); ++i) // iterate in reverse order? m_storageList[i].activateStorage(0); @@ -691,6 +720,27 @@ int TaskTree::progressValue() const return d->m_progressValue; } +void TaskTree::setupStorageHandler(const Tasking::TreeStorageBase &storage, + StorageVoidHandler setupHandler, + StorageVoidHandler doneHandler) +{ + auto it = d->m_storageHandlers.find(storage); + if (it == d->m_storageHandlers.end()) { + d->m_storageHandlers.insert(storage, {setupHandler, doneHandler}); + return; + } + if (setupHandler) { + QTC_ASSERT(!it->m_setupHandler, + qWarning("The storage has its setup handler defined, overriding...")); + it->m_setupHandler = setupHandler; + } + if (doneHandler) { + QTC_ASSERT(!it->m_doneHandler, + qWarning("The storage has its done handler defined, overriding...")); + it->m_doneHandler = doneHandler; + } +} + TaskTreeAdapter::TaskTreeAdapter() { connect(task(), &TaskTree::done, this, [this] { emit done(true); }); diff --git a/src/libs/utils/tasktree.h b/src/libs/utils/tasktree.h index f780ce9f176..10b18e2a078 100644 --- a/src/libs/utils/tasktree.h +++ b/src/libs/utils/tasktree.h @@ -13,6 +13,8 @@ namespace Utils { class TaskContainer; +class TaskTreePrivate; + namespace Tasking { class QTCREATOR_UTILS_EXPORT TaskInterface : public QObject @@ -59,6 +61,7 @@ private: }; QSharedPointer m_storageData; friend TaskContainer; + friend TaskTreePrivate; }; inline size_t qHash(const TreeStorageBase &storage, uint seed = 0) @@ -312,10 +315,22 @@ public: void start(); void stop(); bool isRunning() const; + int taskCount() const; int progressMaximum() const { return taskCount(); } int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded + template + void onStorageSetup(const Tasking::TreeStorage &storage, + const StorageHandler &handler) { + setupStorageHandler(storage, wrapHandler(handler), {}); + } + template + void onStorageDone(const Tasking::TreeStorage &storage, + const StorageHandler &handler) { + setupStorageHandler(storage, {}, wrapHandler(handler)); + } + signals: void started(); void done(); @@ -323,6 +338,21 @@ signals: void progressValueChanged(int value); // updated whenever task finished / skipped / stopped private: + using StorageVoidHandler = std::function; + void setupStorageHandler(const Tasking::TreeStorageBase &storage, + StorageVoidHandler setupHandler, + StorageVoidHandler doneHandler); + template + StorageVoidHandler wrapHandler(const std::function &handler) { + if (!handler) + return {}; + return [handler](void *voidStruct) { + StorageStruct *storageStruct = static_cast(voidStruct); + handler(storageStruct); + }; + } + + friend class TaskTreePrivate; TaskTreePrivate *d; }; diff --git a/tests/auto/utils/tasktree/tst_tasktree.cpp b/tests/auto/utils/tasktree/tst_tasktree.cpp index 6a8f2454c27..5f6c969cd27 100644 --- a/tests/auto/utils/tasktree/tst_tasktree.cpp +++ b/tests/auto/utils/tasktree/tst_tasktree.cpp @@ -513,14 +513,13 @@ private: }; int CustomStorage::s_count = 0; -static Log s_log; void tst_TaskTree::storage_data() { using namespace std::placeholders; QTest::addColumn("root"); - QTest::addColumn>("storageLog"); + QTest::addColumn>("storage"); QTest::addColumn("expectedLog"); QTest::addColumn("runningAfterStart"); QTest::addColumn("success"); @@ -548,7 +547,6 @@ void tst_TaskTree::storage_data() }; const auto rootDone = [storageLog] { storageLog->m_log.append({-1, Handler::GroupDone}); - s_log = storageLog->m_log; }; const Log expectedLog{{1, Handler::GroupSetup}, @@ -597,18 +595,21 @@ void tst_TaskTree::storage_data() void tst_TaskTree::storage() { QFETCH(Group, root); - QFETCH(TreeStorage, storageLog); + QFETCH(TreeStorage, storage); QFETCH(Log, expectedLog); QFETCH(bool, runningAfterStart); QFETCH(bool, success); - s_log.clear(); - - QVERIFY(storageLog.isValid()); + QVERIFY(storage.isValid()); QCOMPARE(CustomStorage::instanceCount(), 0); + Log actualLog; QEventLoop eventLoop; TaskTree processTree(root); + auto collectLog = [&actualLog](CustomStorage *storage){ + actualLog = storage->m_log; + }; + processTree.onStorageDone(storage, collectLog); int doneCount = 0; int errorCount = 0; connect(&processTree, &TaskTree::done, this, [&doneCount, &eventLoop] { ++doneCount; eventLoop.quit(); }); @@ -625,7 +626,7 @@ void tst_TaskTree::storage() eventLoop.exec(); QVERIFY(!processTree.isRunning()); - QCOMPARE(s_log, expectedLog); + QCOMPARE(actualLog, expectedLog); QCOMPARE(CustomStorage::instanceCount(), 0); const int expectedDoneCount = success ? 1 : 0;