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
parent
1a8b7eb1d4
commit
f7fd57075b
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# Generated from kernel.pro.
|
||||
|
||||
add_subdirectory(qapplicationstatic)
|
||||
add_subdirectory(qcoreapplication)
|
||||
add_subdirectory(qdeadlinetimer)
|
||||
add_subdirectory(qelapsedtimer)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
#####################################################################
|
||||
## tst_qapplicationstatic Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qapplicationstatic
|
||||
SOURCES
|
||||
tst_qapplicationstatic.cpp
|
||||
)
|
||||
|
|
@ -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"
|
||||
Loading…
Reference in New Issue