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()
{
if (reloadOnQAppAvailable && QCoreApplication::instance() != nullptr)
if (reloadOnQAppAvailable && QCoreApplication::instanceExists())
load();
return settings.get();
}
bool QLibrarySettings::havePaths()
{
if (reloadOnQAppAvailable && QCoreApplication::instance() != nullptr)
if (reloadOnQAppAvailable && QCoreApplication::instanceExists())
load();
return paths;
}
@ -78,7 +78,7 @@ void QLibrarySettings::load()
{
// If we get any settings here, those won't change when the application shows up.
settings = findConfiguration();
reloadOnQAppAvailable = !settings && !QCoreApplication::instance();
reloadOnQAppAvailable = !settings && !QCoreApplication::instanceExists();
if (settings) {
// 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
if (QCoreApplication::instance()) {
if (QCoreApplication::instanceExists()) {
QString pwd = QCoreApplication::applicationDirPath();
qtconfig = pwd + u"/qt" QT_STRINGIFY(QT_VERSION_MAJOR) ".conf"_s;
if (QFile::exists(qtconfig))
@ -270,7 +270,7 @@ QVersionNumber QLibraryInfo::version() noexcept
static QString prefixFromAppDirHelper()
{
if (QCoreApplication::instance()) {
if (QCoreApplication::instanceExists()) {
#ifdef Q_OS_DARWIN
CFBundleRef bundleRef = CFBundleGetMainBundle();
if (bundleRef) {

View File

@ -144,6 +144,21 @@ Q_CONSTINIT QCoreApplication *QCoreApplication::self = nullptr;
Q_CONSTINIT static QBasicAtomicPointer<QCoreApplication> g_self = nullptr;
# undef qApp
# 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
#if !defined(Q_OS_WIN) || defined(QT_BOOTSTRAPPED)
@ -276,7 +291,7 @@ void qAddPreRoutine(QtStartUpFunction p)
return;
if (preRoutinesCalled) {
Q_ASSERT(QCoreApplication::instance());
Q_ASSERT(qApp);
p();
}
@ -467,11 +482,6 @@ QCoreApplicationPrivate::~QCoreApplicationPrivate()
#endif
}
bool QCoreApplicationPrivate::isAlive() noexcept
{
return qApp != nullptr;
}
#ifndef QT_NO_QOBJECT
void QCoreApplicationPrivate::cleanupThreadData()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -157,7 +157,7 @@ void QThreadStorageData::finish(void **p)
locker.unlock();
if (!destructor) {
if (QCoreApplicationPrivate::isAlive())
if (QCoreApplication::instanceExists())
qWarning("QThreadStorage: entry %d destroyed before end of thread %p",
i, QThread::currentThread());
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:
// 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
if (Q_UNLIKELY(qDBusSpyHookList.exists()) && qApp) {
if (Q_UNLIKELY(qDBusSpyHookList.exists()) && QCoreApplication::instanceExists()) {
if (isLocal) {
Q_ASSERT(QThread::currentThread() != thread());
qDBusDebug() << this << "invoking message spies directly";

View File

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

View File

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

View File

@ -23,12 +23,12 @@ Q_STATIC_LOGGING_CATEGORY(lcSqlDb, "qt.sql.qsqldatabase")
using namespace Qt::StringLiterals;
#define CHECK_QCOREAPPLICATION \
if (Q_UNLIKELY(!QCoreApplication::instance())) { \
if (Q_UNLIKELY(!QCoreApplication::instanceExists())) { \
qCWarning(lcSqlDb, "QSqlDatabase requires a QCoreApplication"); \
return; \
}
#define CHECK_QCOREAPPLICATION_RETVAL \
if (Q_UNLIKELY(!QCoreApplication::instance())) { \
if (Q_UNLIKELY(!QCoreApplication::instanceExists())) { \
qCWarning(lcSqlDb, "QSqlDatabase requires a QCoreApplication"); \
return {}; \
}
@ -661,7 +661,7 @@ void QSqlDatabasePrivate::init(const QString &type)
qCWarning(lcSqlDb, "QSqlDatabase: %ls driver not loaded", qUtf16Printable(type));
qCWarning(lcSqlDb, "QSqlDatabase: available drivers: %ls",
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");
driver = shared_null()->driver;
}