Short live QCoreApplication::instanceExists()

This is a thread-safe version of

    QCoreApplication::instance() != nullptr

for Qt 6.x, because QCoreApplication::self is not atomic and thus
reading it from outside the main thread could be a data race.

That's not to say it always is: if by construction the code can only run
in the main thread or while QCoreApplication definitely exists, that's
safe. Therefore, this commit focuses on places that are meant to be used
in multi-threaded environment (ruling out most of QtGui and QtWidgets)
or where the code was going to dereference the returned pointer anyway.

Change-Id: I6fc556c5fe5cbe0b5902fffdfb6b3bb345b0ee50
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Thiago Macieira 2025-02-09 11:13:19 -08:00 committed by Marc Mutz
parent 8b75f4735b
commit d09c6c1046
12 changed files with 33 additions and 22 deletions

View File

@ -62,14 +62,14 @@ QLibrarySettings::QLibrarySettings() : paths(false), reloadOnQAppAvailable(false
QSettings *QLibrarySettings::configuration() QSettings *QLibrarySettings::configuration()
{ {
if (reloadOnQAppAvailable && QCoreApplication::instance() != nullptr) if (reloadOnQAppAvailable && QCoreApplication::instanceExists())
load(); load();
return settings.get(); return settings.get();
} }
bool QLibrarySettings::havePaths() bool QLibrarySettings::havePaths()
{ {
if (reloadOnQAppAvailable && QCoreApplication::instance() != nullptr) if (reloadOnQAppAvailable && QCoreApplication::instanceExists())
load(); load();
return paths; return paths;
} }
@ -78,7 +78,7 @@ void QLibrarySettings::load()
{ {
// If we get any settings here, those won't change when the application shows up. // If we get any settings here, those won't change when the application shows up.
settings = findConfiguration(); settings = findConfiguration();
reloadOnQAppAvailable = !settings && !QCoreApplication::instance(); reloadOnQAppAvailable = !settings && !QCoreApplication::instanceExists();
if (settings) { if (settings) {
// This code needs to be in the regular library, as otherwise a qt.conf that // This code needs to be in the regular library, as otherwise a qt.conf that
@ -121,7 +121,7 @@ static std::unique_ptr<QSettings> findConfiguration()
} }
} }
#endif #endif
if (QCoreApplication::instance()) { if (QCoreApplication::instanceExists()) {
QString pwd = QCoreApplication::applicationDirPath(); QString pwd = QCoreApplication::applicationDirPath();
qtconfig = pwd + u"/qt" QT_STRINGIFY(QT_VERSION_MAJOR) ".conf"_s; qtconfig = pwd + u"/qt" QT_STRINGIFY(QT_VERSION_MAJOR) ".conf"_s;
if (QFile::exists(qtconfig)) if (QFile::exists(qtconfig))
@ -270,7 +270,7 @@ QVersionNumber QLibraryInfo::version() noexcept
static QString prefixFromAppDirHelper() static QString prefixFromAppDirHelper()
{ {
if (QCoreApplication::instance()) { if (QCoreApplication::instanceExists()) {
#ifdef Q_OS_DARWIN #ifdef Q_OS_DARWIN
CFBundleRef bundleRef = CFBundleGetMainBundle(); CFBundleRef bundleRef = CFBundleGetMainBundle();
if (bundleRef) { if (bundleRef) {

View File

@ -144,6 +144,21 @@ Q_CONSTINIT QCoreApplication *QCoreApplication::self = nullptr;
Q_CONSTINIT static QBasicAtomicPointer<QCoreApplication> g_self = nullptr; Q_CONSTINIT static QBasicAtomicPointer<QCoreApplication> g_self = nullptr;
# undef qApp # undef qApp
# define qApp g_self.loadRelaxed() # define qApp g_self.loadRelaxed()
/*!
\internal
This function is a Qt 6 thread-safe (no data races) version of:
\code
QCoreApplication::instance() != nullptr
\endcode
We may remove it in Qt 7.0 because the above will be thread-safe.
*/
bool QCoreApplication::instanceExists() noexcept
{
return qApp != nullptr;
}
#endif #endif
#if !defined(Q_OS_WIN) || defined(QT_BOOTSTRAPPED) #if !defined(Q_OS_WIN) || defined(QT_BOOTSTRAPPED)
@ -276,7 +291,7 @@ void qAddPreRoutine(QtStartUpFunction p)
return; return;
if (preRoutinesCalled) { if (preRoutinesCalled) {
Q_ASSERT(QCoreApplication::instance()); Q_ASSERT(qApp);
p(); p();
} }
@ -467,11 +482,6 @@ QCoreApplicationPrivate::~QCoreApplicationPrivate()
#endif #endif
} }
bool QCoreApplicationPrivate::isAlive() noexcept
{
return qApp != nullptr;
}
#ifndef QT_NO_QOBJECT #ifndef QT_NO_QOBJECT
void QCoreApplicationPrivate::cleanupThreadData() void QCoreApplicationPrivate::cleanupThreadData()

View File

@ -96,8 +96,10 @@ public:
#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
static QCoreApplication *instance() noexcept { return self.loadRelaxed(); } static QCoreApplication *instance() noexcept { return self.loadRelaxed(); }
static bool instanceExists() noexcept { return instance() != nullptr; }
#else #else
static QCoreApplication *instance() noexcept { return self; } static QCoreApplication *instance() noexcept { return self; }
static bool instanceExists() noexcept;
#endif #endif
#ifndef QT_NO_QOBJECT #ifndef QT_NO_QOBJECT

View File

@ -61,7 +61,6 @@ public:
#endif #endif
~QCoreApplicationPrivate(); ~QCoreApplicationPrivate();
static bool isAlive() noexcept;
void init(); void init();
QString appName() const; QString appName() const;

View File

@ -329,7 +329,7 @@ QEvent::QEvent(Type type)
QEvent::~QEvent() QEvent::~QEvent()
{ {
if (m_posted && QCoreApplication::instance()) if (m_posted && QCoreApplication::instanceExists())
QCoreApplicationPrivate::removePostedEvent(this); QCoreApplicationPrivate::removePostedEvent(this);
} }

View File

@ -68,7 +68,7 @@ QEventLoop::QEventLoop(QObject *parent)
{ {
Q_D(QEventLoop); Q_D(QEventLoop);
QThreadData *threadData = d->threadData.loadRelaxed(); QThreadData *threadData = d->threadData.loadRelaxed();
if (!QCoreApplication::instance() && threadData->requiresCoreApplication) { if (!QCoreApplication::instanceExists() && threadData->requiresCoreApplication) {
qWarning("QEventLoop: Cannot be used without QCoreApplication"); qWarning("QEventLoop: Cannot be used without QCoreApplication");
} else { } else {
threadData->ensureEventDispatcher(); threadData->ensureEventDispatcher();

View File

@ -397,7 +397,7 @@ QTranslator::QTranslator(QObject * parent)
QTranslator::~QTranslator() QTranslator::~QTranslator()
{ {
if (QCoreApplication::instance()) if (QCoreApplication::instanceExists())
QCoreApplication::removeTranslator(this); QCoreApplication::removeTranslator(this);
Q_D(QTranslator); Q_D(QTranslator);
d->clear(); d->clear();

View File

@ -157,7 +157,7 @@ void QThreadStorageData::finish(void **p)
locker.unlock(); locker.unlock();
if (!destructor) { if (!destructor) {
if (QCoreApplicationPrivate::isAlive()) if (QCoreApplication::instanceExists())
qWarning("QThreadStorage: entry %d destroyed before end of thread %p", qWarning("QThreadStorage: entry %d destroyed before end of thread %p",
i, QThread::currentThread()); i, QThread::currentThread());
continue; continue;

View File

@ -559,7 +559,7 @@ bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg)
// run it through the spy filters (if any) before the regular processing: // run it through the spy filters (if any) before the regular processing:
// a) if it's a local message, we're in the caller's thread, so invoke the filter directly // a) if it's a local message, we're in the caller's thread, so invoke the filter directly
// b) if it's an external message, post to the main thread // b) if it's an external message, post to the main thread
if (Q_UNLIKELY(qDBusSpyHookList.exists()) && qApp) { if (Q_UNLIKELY(qDBusSpyHookList.exists()) && QCoreApplication::instanceExists()) {
if (isLocal) { if (isLocal) {
Q_ASSERT(QThread::currentThread() != thread()); Q_ASSERT(QThread::currentThread() != thread());
qDBusDebug() << this << "invoking message spies directly"; qDBusDebug() << this << "invoking message spies directly";

View File

@ -48,7 +48,7 @@ QT_WARNING_DISABLE_MSVC(4723)
static bool qt_pixmap_thread_test() static bool qt_pixmap_thread_test()
{ {
if (Q_UNLIKELY(!QCoreApplication::instance())) { if (!QCoreApplication::instanceExists()) {
qFatal("QPixmap: Must construct a QGuiApplication before a QPixmap"); qFatal("QPixmap: Must construct a QGuiApplication before a QPixmap");
return false; return false;
} }

View File

@ -776,7 +776,7 @@ void QDnsLookup::lookup()
Q_D(QDnsLookup); Q_D(QDnsLookup);
d->isFinished = false; d->isFinished = false;
d->reply = QDnsLookupReply(); d->reply = QDnsLookupReply();
if (!QCoreApplication::instance()) { if (!QCoreApplication::instanceExists()) {
// NOT qCWarning because this isn't a result of the lookup // NOT qCWarning because this isn't a result of the lookup
qWarning("QDnsLookup requires a QCoreApplication"); qWarning("QDnsLookup requires a QCoreApplication");
return; return;

View File

@ -23,12 +23,12 @@ Q_STATIC_LOGGING_CATEGORY(lcSqlDb, "qt.sql.qsqldatabase")
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
#define CHECK_QCOREAPPLICATION \ #define CHECK_QCOREAPPLICATION \
if (Q_UNLIKELY(!QCoreApplication::instance())) { \ if (Q_UNLIKELY(!QCoreApplication::instanceExists())) { \
qCWarning(lcSqlDb, "QSqlDatabase requires a QCoreApplication"); \ qCWarning(lcSqlDb, "QSqlDatabase requires a QCoreApplication"); \
return; \ return; \
} }
#define CHECK_QCOREAPPLICATION_RETVAL \ #define CHECK_QCOREAPPLICATION_RETVAL \
if (Q_UNLIKELY(!QCoreApplication::instance())) { \ if (Q_UNLIKELY(!QCoreApplication::instanceExists())) { \
qCWarning(lcSqlDb, "QSqlDatabase requires a QCoreApplication"); \ qCWarning(lcSqlDb, "QSqlDatabase requires a QCoreApplication"); \
return {}; \ return {}; \
} }
@ -661,7 +661,7 @@ void QSqlDatabasePrivate::init(const QString &type)
qCWarning(lcSqlDb, "QSqlDatabase: %ls driver not loaded", qUtf16Printable(type)); qCWarning(lcSqlDb, "QSqlDatabase: %ls driver not loaded", qUtf16Printable(type));
qCWarning(lcSqlDb, "QSqlDatabase: available drivers: %ls", qCWarning(lcSqlDb, "QSqlDatabase: available drivers: %ls",
qUtf16Printable(QSqlDatabase::drivers().join(u' '))); qUtf16Printable(QSqlDatabase::drivers().join(u' ')));
if (QCoreApplication::instance() == nullptr) if (!QCoreApplication::instanceExists())
qCWarning(lcSqlDb, "QSqlDatabase: an instance of QCoreApplication is required for loading driver plugins"); qCWarning(lcSqlDb, "QSqlDatabase: an instance of QCoreApplication is required for loading driver plugins");
driver = shared_null()->driver; driver = shared_null()->driver;
} }