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 <hjk@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Jarek Kobus
2022-12-05 11:20:57 +01:00
parent dfdeb4d630
commit 7fc9933969
3 changed files with 102 additions and 21 deletions

View File

@@ -227,6 +227,11 @@ public:
QTC_ASSERT(m_root, return); QTC_ASSERT(m_root, return);
m_progressValue = 0; m_progressValue = 0;
emitStartedAndProgress(); 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(); m_root->start();
} }
void stop() { void stop() {
@@ -278,12 +283,36 @@ public:
} }
return addedStorages; 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; TaskTree *q = nullptr;
std::unique_ptr<TaskNode> m_root = nullptr;
Guard m_guard; Guard m_guard;
int m_progressValue = 0; int m_progressValue = 0;
QSet<TreeStorageBase> m_storages; QSet<TreeStorageBase> m_storages;
QHash<TreeStorageBase, StorageHandler> m_storageHandlers;
std::unique_ptr<TaskNode> m_root = nullptr; // Keep me last in order to destruct first
}; };
TaskContainer::TaskContainer(TaskTreePrivate *taskTreePrivate, TaskContainer *parentContainer, TaskContainer::TaskContainer(TaskTreePrivate *taskTreePrivate, TaskContainer *parentContainer,
@@ -481,27 +510,29 @@ void TaskContainer::updateSuccessBit(bool success)
void TaskContainer::createStorages() void TaskContainer::createStorages()
{ {
// TODO: Don't create new storage for already created storages with the same shared pointer.
QTC_CHECK(m_storageIdList.isEmpty()); QTC_CHECK(m_storageIdList.isEmpty());
for (int i = 0; i < m_storageList.size(); ++i) for (int i = 0; i < m_storageList.size(); ++i) {
m_storageIdList << m_storageList[i].createStorage(); TreeStorageBase storage = m_storageList[i];
const int storageId = storage.createStorage();
m_storageIdList << storageId;
m_taskTreePrivate->callSetupHandler(storage, storageId);
}
} }
void TaskContainer::deleteStorages() void TaskContainer::deleteStorages()
{ {
// TODO: Do the opposite for (int i = 0; i < m_storageIdList.size(); ++i) { // iterate in reverse order?
TreeStorageBase storage = m_storageList[i];
for (int i = 0; i < m_storageIdList.size(); ++i) // iterate in reverse order? const int storageId = m_storageIdList.value(i);
m_storageList[i].deleteStorage(m_storageIdList.value(i)); m_taskTreePrivate->callDoneHandler(storage, storageId);
storage.deleteStorage(storageId);
}
m_storageIdList.clear(); m_storageIdList.clear();
} }
void TaskContainer::activateStorages() void TaskContainer::activateStorages()
{ {
// TODO: check if the same shared storage was already activated. Don't activate recursively.
if (m_parentContainer) if (m_parentContainer)
m_parentContainer->activateStorages(); m_parentContainer->activateStorages();
@@ -511,8 +542,6 @@ void TaskContainer::activateStorages()
void TaskContainer::deactivateStorages() void TaskContainer::deactivateStorages()
{ {
// TODO: Do the opposite
for (int i = 0; i < m_storageList.size(); ++i) // iterate in reverse order? for (int i = 0; i < m_storageList.size(); ++i) // iterate in reverse order?
m_storageList[i].activateStorage(0); m_storageList[i].activateStorage(0);
@@ -691,6 +720,27 @@ int TaskTree::progressValue() const
return d->m_progressValue; 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() TaskTreeAdapter::TaskTreeAdapter()
{ {
connect(task(), &TaskTree::done, this, [this] { emit done(true); }); connect(task(), &TaskTree::done, this, [this] { emit done(true); });

View File

@@ -13,6 +13,8 @@ namespace Utils {
class TaskContainer; class TaskContainer;
class TaskTreePrivate;
namespace Tasking { namespace Tasking {
class QTCREATOR_UTILS_EXPORT TaskInterface : public QObject class QTCREATOR_UTILS_EXPORT TaskInterface : public QObject
@@ -59,6 +61,7 @@ private:
}; };
QSharedPointer<StorageData> m_storageData; QSharedPointer<StorageData> m_storageData;
friend TaskContainer; friend TaskContainer;
friend TaskTreePrivate;
}; };
inline size_t qHash(const TreeStorageBase &storage, uint seed = 0) inline size_t qHash(const TreeStorageBase &storage, uint seed = 0)
@@ -312,10 +315,22 @@ public:
void start(); void start();
void stop(); void stop();
bool isRunning() const; bool isRunning() const;
int taskCount() const; int taskCount() const;
int progressMaximum() const { return taskCount(); } int progressMaximum() const { return taskCount(); }
int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded int progressValue() const; // all finished / skipped / stopped tasks, groups itself excluded
template <typename StorageStruct, typename StorageHandler>
void onStorageSetup(const Tasking::TreeStorage<StorageStruct> &storage,
const StorageHandler &handler) {
setupStorageHandler(storage, wrapHandler<StorageStruct>(handler), {});
}
template <typename StorageStruct, typename StorageHandler>
void onStorageDone(const Tasking::TreeStorage<StorageStruct> &storage,
const StorageHandler &handler) {
setupStorageHandler(storage, {}, wrapHandler<StorageStruct>(handler));
}
signals: signals:
void started(); void started();
void done(); void done();
@@ -323,6 +338,21 @@ signals:
void progressValueChanged(int value); // updated whenever task finished / skipped / stopped void progressValueChanged(int value); // updated whenever task finished / skipped / stopped
private: private:
using StorageVoidHandler = std::function<void(void *)>;
void setupStorageHandler(const Tasking::TreeStorageBase &storage,
StorageVoidHandler setupHandler,
StorageVoidHandler doneHandler);
template <typename StorageStruct>
StorageVoidHandler wrapHandler(const std::function<void(StorageStruct *)> &handler) {
if (!handler)
return {};
return [handler](void *voidStruct) {
StorageStruct *storageStruct = static_cast<StorageStruct *>(voidStruct);
handler(storageStruct);
};
}
friend class TaskTreePrivate;
TaskTreePrivate *d; TaskTreePrivate *d;
}; };

View File

@@ -513,14 +513,13 @@ private:
}; };
int CustomStorage::s_count = 0; int CustomStorage::s_count = 0;
static Log s_log;
void tst_TaskTree::storage_data() void tst_TaskTree::storage_data()
{ {
using namespace std::placeholders; using namespace std::placeholders;
QTest::addColumn<Group>("root"); QTest::addColumn<Group>("root");
QTest::addColumn<TreeStorage<CustomStorage>>("storageLog"); QTest::addColumn<TreeStorage<CustomStorage>>("storage");
QTest::addColumn<Log>("expectedLog"); QTest::addColumn<Log>("expectedLog");
QTest::addColumn<bool>("runningAfterStart"); QTest::addColumn<bool>("runningAfterStart");
QTest::addColumn<bool>("success"); QTest::addColumn<bool>("success");
@@ -548,7 +547,6 @@ void tst_TaskTree::storage_data()
}; };
const auto rootDone = [storageLog] { const auto rootDone = [storageLog] {
storageLog->m_log.append({-1, Handler::GroupDone}); storageLog->m_log.append({-1, Handler::GroupDone});
s_log = storageLog->m_log;
}; };
const Log expectedLog{{1, Handler::GroupSetup}, const Log expectedLog{{1, Handler::GroupSetup},
@@ -597,18 +595,21 @@ void tst_TaskTree::storage_data()
void tst_TaskTree::storage() void tst_TaskTree::storage()
{ {
QFETCH(Group, root); QFETCH(Group, root);
QFETCH(TreeStorage<CustomStorage>, storageLog); QFETCH(TreeStorage<CustomStorage>, storage);
QFETCH(Log, expectedLog); QFETCH(Log, expectedLog);
QFETCH(bool, runningAfterStart); QFETCH(bool, runningAfterStart);
QFETCH(bool, success); QFETCH(bool, success);
s_log.clear(); QVERIFY(storage.isValid());
QVERIFY(storageLog.isValid());
QCOMPARE(CustomStorage::instanceCount(), 0); QCOMPARE(CustomStorage::instanceCount(), 0);
Log actualLog;
QEventLoop eventLoop; QEventLoop eventLoop;
TaskTree processTree(root); TaskTree processTree(root);
auto collectLog = [&actualLog](CustomStorage *storage){
actualLog = storage->m_log;
};
processTree.onStorageDone(storage, collectLog);
int doneCount = 0; int doneCount = 0;
int errorCount = 0; int errorCount = 0;
connect(&processTree, &TaskTree::done, this, [&doneCount, &eventLoop] { ++doneCount; eventLoop.quit(); }); connect(&processTree, &TaskTree::done, this, [&doneCount, &eventLoop] { ++doneCount; eventLoop.quit(); });
@@ -625,7 +626,7 @@ void tst_TaskTree::storage()
eventLoop.exec(); eventLoop.exec();
QVERIFY(!processTree.isRunning()); QVERIFY(!processTree.isRunning());
QCOMPARE(s_log, expectedLog); QCOMPARE(actualLog, expectedLog);
QCOMPARE(CustomStorage::instanceCount(), 0); QCOMPARE(CustomStorage::instanceCount(), 0);
const int expectedDoneCount = success ? 1 : 0; const int expectedDoneCount = success ? 1 : 0;