forked from qt-creator/qt-creator
TaskTree: Detect a misconfigured recipe of a nested task tree
Change-Id: I6652336023ac111cde5334e655f5dd972977b07f Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -1291,6 +1291,12 @@ const void *Loop::valuePtr() const
|
|||||||
|
|
||||||
using StoragePtr = void *;
|
using StoragePtr = void *;
|
||||||
|
|
||||||
|
static QString s_activeStorageWarning =
|
||||||
|
"The referenced storage is not reachable in the running tree. "
|
||||||
|
"A nullptr will be returned which might lead to a crash in the calling code. "
|
||||||
|
"It is possible that no storage was added to the tree, "
|
||||||
|
"or the storage is not reachable from where it is referenced.";
|
||||||
|
|
||||||
class StorageThreadData
|
class StorageThreadData
|
||||||
{
|
{
|
||||||
Q_DISABLE_COPY_MOVE(StorageThreadData)
|
Q_DISABLE_COPY_MOVE(StorageThreadData)
|
||||||
@@ -1299,7 +1305,7 @@ public:
|
|||||||
StorageThreadData() = default;
|
StorageThreadData() = default;
|
||||||
void pushStorage(StoragePtr storagePtr)
|
void pushStorage(StoragePtr storagePtr)
|
||||||
{
|
{
|
||||||
m_activeStorageStack.push_back(storagePtr);
|
m_activeStorageStack.push_back({storagePtr, activeTaskTree()});
|
||||||
}
|
}
|
||||||
void popStorage()
|
void popStorage()
|
||||||
{
|
{
|
||||||
@@ -1308,16 +1314,16 @@ public:
|
|||||||
}
|
}
|
||||||
StoragePtr activeStorage() const
|
StoragePtr activeStorage() const
|
||||||
{
|
{
|
||||||
QT_ASSERT(m_activeStorageStack.size(), qWarning(
|
QT_ASSERT(m_activeStorageStack.size(),
|
||||||
"The referenced storage is not reachable in the running tree. "
|
qWarning().noquote() << s_activeStorageWarning; return nullptr);
|
||||||
"A nullptr will be returned which might lead to a crash in the calling code. "
|
const QPair<StoragePtr, TaskTree *> &top = m_activeStorageStack.last();
|
||||||
"It is possible that no storage was added to the tree, "
|
QT_ASSERT(top.second == activeTaskTree(),
|
||||||
"or the storage is not reachable from where it is referenced."); return nullptr);
|
qWarning().noquote() << s_activeStorageWarning; return nullptr);
|
||||||
return m_activeStorageStack.last();
|
return top.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<StoragePtr> m_activeStorageStack;
|
QList<QPair<StoragePtr, TaskTree *>> m_activeStorageStack;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StorageData
|
class StorageData
|
||||||
|
@@ -118,6 +118,7 @@ private slots:
|
|||||||
void storageOperators();
|
void storageOperators();
|
||||||
void storageDestructor();
|
void storageDestructor();
|
||||||
void storageZeroInitialization();
|
void storageZeroInitialization();
|
||||||
|
void nestedBrokenStorage();
|
||||||
void restart();
|
void restart();
|
||||||
void destructorOfTaskEmittingDone();
|
void destructorOfTaskEmittingDone();
|
||||||
};
|
};
|
||||||
@@ -3452,6 +3453,55 @@ void tst_Tasking::storageZeroInitialization()
|
|||||||
QCOMPARE(defaultValue, 0);
|
QCOMPARE(defaultValue, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test ensures that when a missing storage object inside the nested task tree is accessed
|
||||||
|
// directly from the outer task tree's handler, containing the same storage object, then we
|
||||||
|
// detect this misconfigured recipe, issue a warning, and return nullptr for the storage
|
||||||
|
// being accessed (instead of returning parent's task tree's storage instance).
|
||||||
|
// This test should also trigger a runtime assert that we are accessing the nullptr storage.
|
||||||
|
void tst_Tasking::nestedBrokenStorage()
|
||||||
|
{
|
||||||
|
int *outerStorage = nullptr;
|
||||||
|
int *innerStorage1 = nullptr;
|
||||||
|
int *innerStorage2 = nullptr;
|
||||||
|
const Storage<int> storage;
|
||||||
|
|
||||||
|
const auto onOuterSync = [storage, &outerStorage, &innerStorage1, &innerStorage2] {
|
||||||
|
outerStorage = &*storage;
|
||||||
|
|
||||||
|
const auto onInnerSync1 = [storage, &innerStorage1] {
|
||||||
|
innerStorage1 = &*storage; // Triggers the runtime assert on purpose.
|
||||||
|
};
|
||||||
|
const auto onInnerSync2 = [storage, &innerStorage2] {
|
||||||
|
innerStorage2 = &*storage; // Storage is accessible in currently running tree - all OK.
|
||||||
|
};
|
||||||
|
|
||||||
|
const Group innerRecipe {
|
||||||
|
Group {
|
||||||
|
// Broken subrecipe, the storage wasn't placed inside the recipe.
|
||||||
|
// storage,
|
||||||
|
Sync(onInnerSync1)
|
||||||
|
},
|
||||||
|
Group {
|
||||||
|
storage, // Subrecipe OK, another instance for the nested storage will be created.
|
||||||
|
Sync(onInnerSync2)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TaskTree::runBlocking(innerRecipe);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Group outerRecipe {
|
||||||
|
storage,
|
||||||
|
Sync(onOuterSync)
|
||||||
|
};
|
||||||
|
|
||||||
|
TaskTree::runBlocking(outerRecipe);
|
||||||
|
QVERIFY(outerStorage != nullptr);
|
||||||
|
QCOMPARE(innerStorage1, nullptr);
|
||||||
|
QVERIFY(innerStorage2 != nullptr);
|
||||||
|
QVERIFY(innerStorage2 != outerStorage);
|
||||||
|
}
|
||||||
|
|
||||||
void tst_Tasking::restart()
|
void tst_Tasking::restart()
|
||||||
{
|
{
|
||||||
TaskTree taskTree({TestTask([](TaskObject &taskObject) { taskObject = 1000ms; })});
|
TaskTree taskTree({TestTask([](TaskObject &taskObject) { taskObject = 1000ms; })});
|
||||||
|
Reference in New Issue
Block a user