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;