Introduce Q_APPLICATION_STATIC

QObjects must be deleted if the QCoreApplication is being destroyed.
This was previously done by implementing custom code in qtbase and
other modules. So unify it and introduce a Q_APPLICATION_STATIC,
based on the Q_GLOBAL_STATIC, which centralises the logic.

Since we still have a few remaining living QObjects,
this comes in handy to fix those as well.

Task-number: QTBUG-84234
Change-Id: I3040a2280ff56291f2b1c39948c06a23597865c4
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Mike Achtelik 2021-10-18 13:35:15 +02:00
parent 1a8b7eb1d4
commit f7fd57075b
9 changed files with 303 additions and 51 deletions

View File

@ -99,6 +99,7 @@ qt_internal_add_module(Core
io/qurlrecode.cpp
kernel/qabstracteventdispatcher.cpp kernel/qabstracteventdispatcher.h kernel/qabstracteventdispatcher_p.h
kernel/qabstractnativeeventfilter.cpp kernel/qabstractnativeeventfilter.h
kernel/qapplicationstatic.h
kernel/qassociativeiterable.cpp kernel/qassociativeiterable.h
kernel/qbasictimer.cpp kernel/qbasictimer.h
kernel/qbindingstorage.h

View File

@ -57,6 +57,27 @@ enum GuardValues {
};
}
#define Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \
struct HolderBase \
{ \
HolderBase() = default; \
~HolderBase() noexcept \
{ \
if (guard.loadRelaxed() == QtGlobalStatic::Initialized) \
guard.storeRelaxed(QtGlobalStatic::Destroyed); \
} \
Q_DISABLE_COPY_MOVE(HolderBase) \
}; \
struct Holder : public HolderBase \
{ \
Type value; \
Holder() noexcept(noexcept(typename std::remove_cv<Type>::type ARGS)) \
: value ARGS \
{ \
guard.storeRelaxed(QtGlobalStatic::Initialized); \
} \
};
#if defined(Q_OS_UNIX) && defined(Q_CC_INTEL)
// Work around Intel issue ID 6000058488:
// local statics inside an inline function inside an anonymous namespace are global
@ -69,24 +90,11 @@ enum GuardValues {
#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \
Q_GLOBAL_STATIC_INTERNAL_DECORATION Type *innerFunction() \
{ \
struct HolderBase { \
HolderBase() = default; \
~HolderBase() noexcept \
{ if (guard.loadRelaxed() == QtGlobalStatic::Initialized) \
guard.storeRelaxed(QtGlobalStatic::Destroyed); } \
Q_DISABLE_COPY_MOVE(HolderBase) \
}; \
static struct Holder : public HolderBase { \
Type value; \
Holder() \
noexcept(noexcept(typename std::remove_cv<Type>::type ARGS)) \
: value ARGS \
{ guard.storeRelaxed(QtGlobalStatic::Initialized); } \
} holder; \
Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \
static Holder holder; \
return &holder.value; \
}
// this class must be POD, unless the compiler supports thread-safe statics
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
struct QGlobalStatic

View File

@ -0,0 +1,102 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QAPPLICATIONSTATIC_H
#define QAPPLICATIONSTATIC_H
#include <QMutex>
#include <QtCore/qglobalstatic.h>
QT_BEGIN_NAMESPACE
#define Q_APPLICATION_STATIC_INTERNAL_HOLDER(ARGS) \
Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \
struct LifecycleHolder : public Holder \
{ \
LifecycleHolder() { connectLifecycle(); } \
BaseType *get() \
{ \
const QMutexLocker locker(&theMutex); \
if (value.get() == nullptr \
&& guard.loadRelaxed() == QtGlobalStatic::Initialized) { \
value.reset(ARGS); \
connectLifecycle(); \
} \
return value.get(); \
} \
void connectLifecycle() \
{ \
Q_ASSERT(QCoreApplication::instance()); \
QObject::connect(QCoreApplication::instance(), \
&QCoreApplication::destroyed, [this] { \
const QMutexLocker locker(&theMutex); \
value.reset(nullptr); \
}); \
} \
};
#define Q_APPLICATION_STATIC_INTERNAL(ARGS) \
Q_GLOBAL_STATIC_INTERNAL_DECORATION BaseType *innerFunction() \
{ \
Q_APPLICATION_STATIC_INTERNAL_HOLDER(ARGS) \
static LifecycleHolder holder; \
return holder.get(); \
}
#define Q_APPLICATION_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
QT_WARNING_PUSH \
QT_WARNING_DISABLE_CLANG("-Wunevaluated-expression") \
namespace { namespace Q_QAS_ ## NAME { \
typedef TYPE BaseType; \
typedef std::unique_ptr<BaseType> Type; \
QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
QBasicMutex theMutex; \
Q_APPLICATION_STATIC_INTERNAL((new BaseType ARGS)) \
} } \
static QGlobalStatic<TYPE, \
Q_QAS_ ## NAME::innerFunction, \
Q_QAS_ ## NAME::guard> NAME; \
QT_WARNING_POP
#define Q_APPLICATION_STATIC(TYPE, NAME) \
Q_APPLICATION_STATIC_WITH_ARGS(TYPE, NAME, ())
QT_END_NAMESPACE
#endif // QAPPLICATIONSTATIC_H

View File

@ -0,0 +1,98 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\macro Q_APPLICATION_STATIC(Type, VariableName)
\since 6.3
\relates QGlobalStatic
This macro extends Q_GLOBAL_STATIC and creates a global and static object
of type \l QGlobalStatic, of name \a VariableName and that behaves as a
pointer to \a Type, where the actual lifetime of the type is bound to the
QCoreApplication. The object created by Q_APPLICATION_STATIC initializes
itself on the first use, which means that it will not increase the application
or the library's load time. Additionally, the object is initialized in a
thread-safe manner on all platforms.
In contrast to Q_GLOBAL_STATIC where the type is only meant to be destroyed at
program exit, here the actual lifetime of the type is bound to the lifetime of
the QCoreApplication. This makes it ideal to store semi-static QObjects, which
should also be destroyed once the QCoreApplication is destroyed. This means the
type will get deleted once the QCoreApplication emits the destroyed signal.
However, as long as the actual holder is still in the initialized state, the
type will be recreated when it's accessed again once a new QCoreApplication
has been created.
Since the value is bound to the QCoreApplication it should only ever be accessed,
if there is a valid QCoreApplication::instance().
The typical use of this macro is as follows, in a global context (that is,
outside of any function bodies):
\code
Q_APPLICATION_STATIC(MyQObjectType, staticType)
\endcode
Aside from the value also being bound to the lifetime of the QCoreApplication,
this macro behaves identically to Q_GLOBAL_STATIC(). Please see that macro's
documentation for more information.
\sa Q_APPLICATION_STATIC_WITH_ARGS(), QGlobalStatic
*/
/*!
\macro Q_APPLICATION_STATIC_WITH_ARGS(Type, VariableName, Arguments)
\since 6.3
\relates QGlobalStatic
Creates a global and static object of type \l QGlobalStatic, of name \a
VariableName, initialized by the arguments \a Arguments and that behaves as
a pointer to \a Type, where the actual lifetime of the type is bound to the
QCoreApplication. The object created by Q_APPLICATION_STATIC_WITH_ARGS
initializes itself on the first use, which means that it will not increase
the application or the library's load time. Additionally, the object is
initialized in a thread-safe manner on all platforms.
Since the value is bound to the QCoreApplication it should only ever be accessed,
if there is a valid QCoreApplication::instance().
The typical use of this macro is as follows, in a global context (that is,
outside of any function bodies):
\code
Q_APPLICATION_STATIC_WITH_ARGS(MyQObjectType, staticType, (42, "Hello", "World"))
\endcode
The \a Arguments macro parameter must always include the parentheses or, if
C++11 uniform initialization is allowed, the braces.
Aside from the actual initialization of the contents with the supplied
arguments, this macro behaves identically to Q_APPLICATION_STATIC(). Please
see that macro's documentation for more information.
\sa Q_APPLICATION_STATIC(), QGlobalStatic
*/

View File

@ -43,6 +43,7 @@
#include "qhostinfo_p.h"
#include <qplatformdefs.h>
#include "QtCore/qapplicationstatic.h"
#include "QtCore/qscopedpointer.h"
#include <qabstracteventdispatcher.h>
#include <qcoreapplication.h>
@ -99,24 +100,7 @@ std::pair<OutputIt1, OutputIt2> separate_if(InputIt first, InputIt last, OutputI
return std::make_pair(dest1, dest2);
}
QHostInfoLookupManager* theHostInfoLookupManager()
{
static QHostInfoLookupManager* theManager = nullptr;
static QBasicMutex theMutex;
const QMutexLocker locker(&theMutex);
if (theManager == nullptr) {
theManager = new QHostInfoLookupManager();
Q_ASSERT(QCoreApplication::instance());
QObject::connect(QCoreApplication::instance(), &QCoreApplication::destroyed, [] {
const QMutexLocker locker(&theMutex);
delete theManager;
theManager = nullptr;
});
}
return theManager;
}
Q_APPLICATION_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
}

View File

@ -53,6 +53,8 @@
#include <qfile.h>
#include <private/qnet_unix_p.h>
#include "QtCore/qapplicationstatic.h"
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
@ -157,24 +159,7 @@ LibResolv::LibResolv()
}
}
LibResolv* libResolv()
{
static LibResolv* theLibResolv = nullptr;
static QBasicMutex theMutex;
const QMutexLocker locker(&theMutex);
if (theLibResolv == nullptr) {
theLibResolv = new LibResolv();
Q_ASSERT(QCoreApplication::instance());
QObject::connect(QCoreApplication::instance(), &QCoreApplication::destroyed, [] {
const QMutexLocker locker(&theMutex);
delete theLibResolv;
theLibResolv = nullptr;
});
}
return theLibResolv;
}
Q_APPLICATION_STATIC(LibResolv, libResolv)
static void resolveLibrary(LibResolvFeature f)
{

View File

@ -1,5 +1,6 @@
# Generated from kernel.pro.
add_subdirectory(qapplicationstatic)
add_subdirectory(qcoreapplication)
add_subdirectory(qdeadlinetimer)
add_subdirectory(qelapsedtimer)

View File

@ -0,0 +1,8 @@
#####################################################################
## tst_qapplicationstatic Test:
#####################################################################
qt_internal_add_test(tst_qapplicationstatic
SOURCES
tst_qapplicationstatic.cpp
)

View File

@ -0,0 +1,65 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QTest>
#include <QPointer>
#include "QtCore/qapplicationstatic.h"
Q_APPLICATION_STATIC(QObject, tstObject)
class tst_qapplicationstatic : public QObject
{
Q_OBJECT
private slots:
void testCreateMultipleApplications() const;
};
void tst_qapplicationstatic::testCreateMultipleApplications() const
{
for (int i = 0; i < 5; i++) {
int argc = 1;
char *argv[] = { (char *)"tst_qapplicationstatic" };
auto app = new QCoreApplication(argc, argv);
QVERIFY(tstObject);
QPointer<QObject> tstObjectPointer(tstObject);
QVERIFY(tstObjectPointer.get());
QVERIFY2(tstObject->objectName().isEmpty(), "Got QObject from previous iteration, not correctly recreated");
tstObject->setObjectName(QStringLiteral("tstObject"));
QVERIFY(!tstObject->objectName().isEmpty());
delete app;
QVERIFY2(!tstObjectPointer.get(), "QObject wasn't destroyed on QCoreApplication destruction");
}
}
QTEST_APPLESS_MAIN(tst_qapplicationstatic)
#include "tst_qapplicationstatic.moc"