tst_QCoreApplication: ensure that theMainThread has expected states

The expected states are:
- nothing sets theMainThread before main()
- theMainThread is reset when the last QObject (the QCoreApplication in
  the test) is destroyed

The GUI version of this test appears to leak a lot of QObjects. By the
time this function runs, theMainThread's QThreadData still has a
refcount of 66 on Linux/XCB. The Windows non-GUI version also
failed. Neither situation was investigated to see why objects are
getting leaked.

Change-Id: Idd5e1bb52be047d7b4fffffd17507d9e6ef08743
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit b9394b48c1b42e0ef834315ca4f89161a5c12ae7)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Thiago Macieira 2023-03-27 22:19:40 -07:00 committed by Qt Cherry-pick Bot
parent 17be1c566f
commit a94f99adcc
2 changed files with 38 additions and 3 deletions

View File

@ -1034,16 +1034,50 @@ void tst_QCoreApplication::addRemoveLibPaths()
}
#endif
static bool theMainThreadIsSet()
{
// QCoreApplicationPrivate::mainThread() has a Q_ASSERT we'd trigger
return QCoreApplicationPrivate::theMainThread.loadRelaxed() != nullptr;
}
static bool theMainThreadWasUnset = !theMainThreadIsSet(); // global static
void tst_QCoreApplication::theMainThread()
{
QVERIFY2(theMainThreadWasUnset, "Something set the theMainThread before main()");
QVERIFY(theMainThreadIsSet()); // we have at LEAST one QObject alive: tst_QCoreApplication
int argc = 1;
char *argv[] = { const_cast<char*>(QTest::currentAppName()) };
TestApplication app(argc, argv);
QVERIFY(QCoreApplicationPrivate::theMainThread.loadRelaxed());
QCOMPARE(QCoreApplicationPrivate::theMainThread.loadRelaxed(), thread());
QCOMPARE(app.thread(), thread());
QCOMPARE(app.thread(), QThread::currentThread());
}
static void createQObjectOnDestruction()
{
// Make sure that we can create a QObject after the last QObject has been
// destroyed (especially after QCoreApplication has).
//
// Make sure that we can create a QObject (and thus have an associated
// QThread) after the last QObject has been destroyed (especially after
// QCoreApplication has).
#if !defined(QT_QGUIAPPLICATIONTEST) && !defined(Q_OS_WIN)
// QCoreApplicationData's global static destructor has run and cleaned up
// the QAdoptedThrad.
if (theMainThreadIsSet())
qFatal("theMainThreadIsSet() returned true; some QObject must have leaked");
#endif
// Before the fixes, this would cause a dangling pointer dereference. If
// the problem comes back, it's possible that the following causes no
// effect.
QObject obj;
obj.thread()->setProperty("testing", 1);
if (!theMainThreadIsSet())
qFatal("theMainThreadIsSet() returned false");
// because we created a QObject after QCoreApplicationData was destroyed,
// the QAdoptedThread won't get cleaned up
}
Q_DESTRUCTOR_FUNCTION(createQObjectOnDestruction)

View File

@ -44,6 +44,7 @@ private slots:
#if QT_CONFIG(library)
void addRemoveLibPaths();
#endif
void theMainThread();
};
#endif // TST_QCOREAPPLICATION_H