diff options
-rw-r--r-- | src/corelib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/corelib/global/qglobalstatic.h | 38 | ||||
-rw-r--r-- | src/corelib/kernel/qapplicationstatic.h | 102 | ||||
-rw-r--r-- | src/corelib/kernel/qapplicationstatic.qdoc | 98 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo.cpp | 20 | ||||
-rw-r--r-- | src/network/kernel/qhostinfo_unix.cpp | 21 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt | 8 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qapplicationstatic/tst_qapplicationstatic.cpp | 65 |
9 files changed, 303 insertions, 51 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index bae11298bc..2e785471d6 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -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 diff --git a/src/corelib/global/qglobalstatic.h b/src/corelib/global/qglobalstatic.h index 49cebe1e61..6816721b32 100644 --- a/src/corelib/global/qglobalstatic.h +++ b/src/corelib/global/qglobalstatic.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 diff --git a/src/corelib/kernel/qapplicationstatic.h b/src/corelib/kernel/qapplicationstatic.h new file mode 100644 index 0000000000..e7f381a70c --- /dev/null +++ b/src/corelib/kernel/qapplicationstatic.h @@ -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 diff --git a/src/corelib/kernel/qapplicationstatic.qdoc b/src/corelib/kernel/qapplicationstatic.qdoc new file mode 100644 index 0000000000..4d203a78ae --- /dev/null +++ b/src/corelib/kernel/qapplicationstatic.qdoc @@ -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 +*/ diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp index fdf2844447..6f508fb59b 100644 --- a/src/network/kernel/qhostinfo.cpp +++ b/src/network/kernel/qhostinfo.cpp @@ -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) } diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp index 9b0a2ee669..3ccf8b217b 100644 --- a/src/network/kernel/qhostinfo_unix.cpp +++ b/src/network/kernel/qhostinfo_unix.cpp @@ -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) { diff --git a/tests/auto/corelib/kernel/CMakeLists.txt b/tests/auto/corelib/kernel/CMakeLists.txt index 39f16bc970..18f20bfadc 100644 --- a/tests/auto/corelib/kernel/CMakeLists.txt +++ b/tests/auto/corelib/kernel/CMakeLists.txt @@ -1,5 +1,6 @@ # Generated from kernel.pro. +add_subdirectory(qapplicationstatic) add_subdirectory(qcoreapplication) add_subdirectory(qdeadlinetimer) add_subdirectory(qelapsedtimer) diff --git a/tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt b/tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt new file mode 100644 index 0000000000..0890113c55 --- /dev/null +++ b/tests/auto/corelib/kernel/qapplicationstatic/CMakeLists.txt @@ -0,0 +1,8 @@ +##################################################################### +## tst_qapplicationstatic Test: +##################################################################### + +qt_internal_add_test(tst_qapplicationstatic + SOURCES + tst_qapplicationstatic.cpp + ) diff --git a/tests/auto/corelib/kernel/qapplicationstatic/tst_qapplicationstatic.cpp b/tests/auto/corelib/kernel/qapplicationstatic/tst_qapplicationstatic.cpp new file mode 100644 index 0000000000..9972e050db --- /dev/null +++ b/tests/auto/corelib/kernel/qapplicationstatic/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" |