From b9394b48c1b42e0ef834315ca4f89161a5c12ae7 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 27 Mar 2023 22:19:40 -0700 Subject: 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. Pick-to: 6.5 Change-Id: Idd5e1bb52be047d7b4fffffd17507d9e6ef08743 Reviewed-by: Marc Mutz Reviewed-by: Volker Hilsheimer --- .../qcoreapplication/tst_qcoreapplication.cpp | 40 ++++++++++++++++++++-- .../kernel/qcoreapplication/tst_qcoreapplication.h | 1 + 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp index 46d52c2340..edba67bf46 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp @@ -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(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) diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h index 0894b64f33..91d9324db4 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.h @@ -44,6 +44,7 @@ private slots: #if QT_CONFIG(library) void addRemoveLibPaths(); #endif + void theMainThread(); }; #endif // TST_QCOREAPPLICATION_H -- cgit v1.2.3