diff --git a/src/corelib/global/qglobalstatic.h b/src/corelib/global/qglobalstatic.h index 6816721b32d..01e06e52345 100644 --- a/src/corelib/global/qglobalstatic.h +++ b/src/corelib/global/qglobalstatic.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2021 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -44,6 +44,7 @@ #include +#include // for bootstrapped (no thread) builds #include QT_BEGIN_NAMESPACE @@ -55,92 +56,92 @@ enum GuardValues { Uninitialized = 0, Initializing = 1 }; -} -#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 ARGS)) \ - : value ARGS \ - { \ - guard.storeRelaxed(QtGlobalStatic::Initialized); \ - } \ - }; +template struct Holder +{ + using Type = typename QGS::QGS_Type; + using PlainType = std::remove_cv_t; -#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 -// symbols (this affects the IA-64 C++ ABI, so OS X and Linux only) -# define Q_GLOBAL_STATIC_INTERNAL_DECORATION Q_DECL_HIDDEN -#else -# define Q_GLOBAL_STATIC_INTERNAL_DECORATION Q_DECL_HIDDEN inline -#endif + static constexpr bool ConstructionIsNoexcept = noexcept(QGS::innerFunction(nullptr)); + std::aligned_union_t<1, PlainType> storage; + static inline QBasicAtomicInteger guard = { QtGlobalStatic::Uninitialized }; -#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \ - Q_GLOBAL_STATIC_INTERNAL_DECORATION Type *innerFunction() \ - { \ - Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \ - static Holder holder; \ - return &holder.value; \ + Holder() noexcept(ConstructionIsNoexcept) + { + QGS::innerFunction(pointer()); + guard.storeRelaxed(QtGlobalStatic::Initialized); } -// this class must be POD, unless the compiler supports thread-safe statics -template -struct QGlobalStatic -{ - typedef T Type; + ~Holder() + { + guard.storeRelaxed(QtGlobalStatic::Destroyed); + std::atomic_thread_fence(std::memory_order_acquire); // avoid mixing stores to guard and *pointer() + pointer()->~PlainType(); + } - bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; } - bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; } + PlainType *pointer() noexcept + { + return reinterpret_cast(&storage); + } + + Q_DISABLE_COPY_MOVE(Holder) +}; +} + +template struct QGlobalStatic +{ + using Type = typename Holder::Type; + + bool isDestroyed() const { return guardValue() <= QtGlobalStatic::Destroyed; } + bool exists() const { return guardValue() == QtGlobalStatic::Initialized; } operator Type *() { if (isDestroyed()) return nullptr; - return innerFunction(); + return instance(); } Type *operator()() { if (isDestroyed()) return nullptr; - return innerFunction(); + return instance(); } Type *operator->() { Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed"); - return innerFunction(); + return instance(); } Type &operator*() { Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC", "The global static was used after being destroyed"); - return *innerFunction(); + return *instance(); + } + +protected: + static Type *instance() noexcept(Holder::ConstructionIsNoexcept) + { + static Holder holder; + return holder.pointer(); + } + static QtGlobalStatic::GuardValues guardValue() + { + return QtGlobalStatic::GuardValues(Holder::guard.loadAcquire()); } }; #define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \ - QT_WARNING_PUSH \ - QT_WARNING_DISABLE_CLANG("-Wunevaluated-expression") \ - namespace { namespace Q_QGS_ ## NAME { \ - typedef TYPE Type; \ - QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \ - Q_GLOBAL_STATIC_INTERNAL(ARGS) \ - } } \ - static QGlobalStatic NAME; \ - QT_WARNING_POP + namespace { struct Q_QGS_ ## NAME { \ + typedef TYPE QGS_Type; \ + static void innerFunction(void *pointer) \ + noexcept(noexcept(std::remove_cv_t ARGS)) \ + { \ + new (pointer) QGS_Type ARGS; \ + } \ + }; } \ + static QGlobalStatic> NAME; \ + /**/ #define Q_GLOBAL_STATIC(TYPE, NAME) \ Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ()) diff --git a/src/corelib/global/qglobalstatic.qdoc b/src/corelib/global/qglobalstatic.qdoc index e7935d5a9b9..5aea8fed064 100644 --- a/src/corelib/global/qglobalstatic.qdoc +++ b/src/corelib/global/qglobalstatic.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2021 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the documentation of the Qt Toolkit. @@ -195,11 +195,11 @@ \omit \section1 Compatibility with Qt 4 and Qt 5.0 - This macro, in its current form and behavior, was introduced in Qt 5.1. - Prior to that version, Qt had another macro with the same name that was - private API. This section is not meant to document how to use - Q_GLOBAL_STATIC in those versions, but instead to serve as a porting guide - for Qt code that used those macros. + This macro, in its current behavior, was introduced in Qt 5.1. Prior to + that version, Qt had another macro with the same name that was private API. + This section is not meant to document how to use Q_GLOBAL_STATIC in those + versions, but instead to serve as a porting guide for Qt code that used + those macros. The Qt 4 Q_GLOBAL_STATIC macro differed in behavior in the following ways: @@ -218,24 +218,38 @@ \section1 Implementation Details - Q_GLOBAL_STATIC is implemented by creating a QBasicAtomicInt called the \c - guard and a free, inline function called \c innerFunction. The guard - variable is initialized to value 0 (chosen so that the guard can be placed - in the .bss section of the binary file), which denotes that construction - has not yet taken place (uninitialized). The inner function is implemented - by the helper macro Q_GLOBAL_STATIC_INTERNAL. + Q_GLOBAL_STATIC is implemented by creating a type called \c Q_QGS_NAME + where \c NAME is the name of the variable the user passed to the macro, + inside an unnamed namespace, and a \c static variable of \l QGlobalStatic + type, named \c NAME. The use of unnamed namespaces forces the compiler to emit + non-exported symbols, often local to the translation unit, and this + propagates to the \l QGlobalStatic template instantiation that uses such + types. Additionally, because the type is used only for one variable, + there's effectively no difference between static and non-static data + members. - Both the guard variable and the inner function are passed as template - parameters to QGlobalStatic, along with the type \a Type. Both should also - have static linkage or be placed inside an anonymous namespace, so that the - visibility of Q_GLOBAL_STATIC is that of a global static. To permit - multiple Q_GLOBAL_STATIC per translation unit, the guard variable and the - inner function must have unique names, which can be accomplished by - concatenating with \a VariableName or by placing them in a namespace that - has \a VariableName as part of the name. To simplify and improve - readability on Q_GLOBAL_STATIC_INTERNAL, we chose the namespace solution. - It's also required for C++98 builds, since static symbols cannot be used as - template parameters. + The "QGS" type is a \c struct containing a \c typedef to \a TYPE and a + static member function that receives a pointer to storage suitable to hold + an instance of \a TYPE. It will initialize the storage using a placement \c + new and the variadic arguments. + + The majority of the work is done by the public \l QGlobalStatic class and + the private \c QtGlobalStatic::Holder class. The \c Holder class has a + non-static, trivial member of suitable size and alignment to hold \a TYPE + (a \c{std::aligned_union_t} or a \c{std::aligned_storage_t}). The + constructor calls the "QGS" type's static member function with a pointer to + this location so it can be initialized and the destructor calls the type's + destructor. The \c{Holder} type is therefore neither trivially + constructible nor trivially destructible. It is used as a function-local \c + static so its initialization is thread-safe due to C++11's requirement that + such variables be thread-safely initialized. + + Additionally, both the constructor and destructor modify a guard variable + after construction and before destruction, respectively. The guard variable + is implemented as a \c {static inline} member instead of a non-static + member so the compiler and linker are free to place this variable in memory + far from the actual object. This way, if we wanted to, we could mark it + aligned-to-cacheline in the future to prevent false sharing. The guard variable can assume the following values: @@ -257,34 +271,6 @@ operate solely on the guard variable: the former returns \c true if the guard is negative, whereas the latter returns \c true only if it is -2. - The Q_GLOBAL_STATIC_INTERNAL macro implements the actual construction and - destruction. There are two implementations of it: one for compilers that - support thread-safe initialization of function-local statics and one for - compilers that don't. Thread-safe initialization is required by C++11 in - [stmt.decl], but as of the time of this writing, only compilers based on - the IA-64 C++ ABI implemented it properly. The implementation requiring - thread-safe initialization is also used on the Qt bootstrapped tools, which - disable the "thread" feature. - - The implementation requiring thread-safe initialization from the compiler - is the simplest: it creates the \a Type object as a function-local static - and returns its address. The actual object is actually inside a holder - structure so holder's destructor can set the guard variable to the value -2 - (destroyed) when the type has finished destruction. Since we need to set - the guard \b after the destruction has finished, this code needs to be in a - base struct's destructor. And it only sets to -2 (destroyed) if it finds - the guard at -1 (initialized): this is done to ensure that the guard isn't - set to -2 in the event the type's constructor threw an exception. A holder - structure is used to avoid creating two statics, which the ABI might - require duplicating the thread-safe control structures for. - - The other implementation is similar to Qt 4's Q_GLOBAL_STATIC, but unlike - that one, it uses a \l QBasicMutex to provide locking. It is also more - efficient memory-wise. It use a simple double-checked locking of the mutex - and then creates the contents on the heap. After that, it creates a - function-local structure called "Cleanup", whose destructor will be run at - program exit and will actually destroy the contents. - \endomit \sa Q_GLOBAL_STATIC_WITH_ARGS(), QGlobalStatic @@ -356,7 +342,7 @@ */ /*! - \fn template bool QGlobalStatic::isDestroyed() const + \fn template bool QGlobalStatic::isDestroyed() const This function returns \c true if the global static object has already completed destruction (that is, if the destructor for the type has already @@ -386,7 +372,7 @@ */ /*! - \fn template bool QGlobalStatic::exists() const + \fn template bool QGlobalStatic::exists() const This function returns \c true if the global static object has already completed initialization (that is, if the constructor for the type has @@ -436,7 +422,7 @@ /*! \keyword qglobalstatic-operator-type-ptr - \fn template QGlobalStatic::operator Type*() + \fn template QGlobalStatic::operator Type*() This function returns the address of the contents of this global static. If the contents have not yet been created, they will be created thread-safely @@ -469,7 +455,7 @@ */ /*! - \fn template Type *QGlobalStatic::operator()() + \fn template Type *QGlobalStatic::operator()() \deprecated This function returns the address of the contents of this global static. If @@ -485,7 +471,7 @@ */ /*! - \fn template Type *QGlobalStatic::operator->() + \fn template Type *QGlobalStatic::operator->() This function returns the address of the contents of this global static. If the contents have not yet been created, they will be created thread-safely @@ -498,7 +484,7 @@ */ /*! - \fn template Type &QGlobalStatic::operator*() + \fn template Type &QGlobalStatic::operator*() This function returns a reference to the contents of this global static. If the contents have not yet been created, they will be created thread-safely diff --git a/src/corelib/kernel/qapplicationstatic.h b/src/corelib/kernel/qapplicationstatic.h index 33567596e66..f34bd806a20 100644 --- a/src/corelib/kernel/qapplicationstatic.h +++ b/src/corelib/kernel/qapplicationstatic.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2021 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -41,58 +42,75 @@ #define QAPPLICATIONSTATIC_H #include +#include #include 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); \ - }); \ - } \ - }; +namespace QtGlobalStatic { +template struct ApplicationHolder +{ + using Type = typename QAS::QAS_Type; + using PlainType = std::remove_cv_t; -#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(); \ + static inline std::aligned_union_t<1, PlainType> storage; + static inline QBasicAtomicInteger guard = { QtGlobalStatic::Uninitialized }; + static inline QBasicMutex mutex {}; + + static constexpr bool MutexLockIsNoexcept = noexcept(mutex.lock()); + static constexpr bool ConstructionIsNoexcept = noexcept(QAS::innerFunction(nullptr)); + + ApplicationHolder() = default; + Q_DISABLE_COPY_MOVE(ApplicationHolder) + ~ApplicationHolder() + { + if (guard.loadRelaxed() == QtGlobalStatic::Initialized) { + guard.storeRelease(QtGlobalStatic::Destroyed); + realPointer()->~PlainType(); + } } + static PlainType *realPointer() + { + return reinterpret_cast(&storage); + } + + // called from QGlobalStatic::instance() + PlainType *pointer() noexcept(MutexLockIsNoexcept && ConstructionIsNoexcept) + { + if (guard.loadRelaxed() == QtGlobalStatic::Initialized) + return realPointer(); + QMutexLocker locker(&mutex); + if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { + QAS::innerFunction(realPointer()); + QObject::connect(QCoreApplication::instance(), &QObject::destroyed, reset); + guard.storeRelaxed(QtGlobalStatic::Initialized); + } + return realPointer(); + } + + static void reset() + { + if (guard.loadRelaxed() == QtGlobalStatic::Initialized) { + QMutexLocker locker(&mutex); + realPointer()->~PlainType(); + guard.storeRelaxed(QtGlobalStatic::Uninitialized); + } + } +}; +} // namespace QtGlobalStatic + #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 Type; \ - QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \ - QBasicMutex theMutex; \ - Q_APPLICATION_STATIC_INTERNAL((new BaseType ARGS)) \ - } } \ - static QGlobalStatic NAME; \ - QT_WARNING_POP + namespace { struct Q_QAS_ ## NAME { \ + typedef TYPE QAS_Type; \ + static void innerFunction(void *pointer) \ + noexcept(noexcept(std::remove_cv_t ARGS)) \ + { \ + new (pointer) QAS_Type ARGS; \ + } \ + }; } \ + static QGlobalStatic> NAME;\ + /**/ #define Q_APPLICATION_STATIC(TYPE, NAME) \ Q_APPLICATION_STATIC_WITH_ARGS(TYPE, NAME, ()) diff --git a/src/corelib/kernel/qapplicationstatic.qdoc b/src/corelib/kernel/qapplicationstatic.qdoc index 4d203a78aec..5903cdc20bf 100644 --- a/src/corelib/kernel/qapplicationstatic.qdoc +++ b/src/corelib/kernel/qapplicationstatic.qdoc @@ -47,8 +47,10 @@ 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(). + Since the value is bound to the QCoreApplication, it should only ever be + accessed if there is a valid QCoreApplication::instance(). Accessing this + object before QCoreApplication is created or after it's destroyed will + produce warnings and may have unpredictable behavior. The typical use of this macro is as follows, in a global context (that is, outside of any function bodies): @@ -61,6 +63,42 @@ this macro behaves identically to Q_GLOBAL_STATIC(). Please see that macro's documentation for more information. + \omit + \section1 Implementation details + See \l Q_GLOBAL_STATIC implementation details for an introduction. + + Q_APPLICATION_STATIC uses the same \l QGlobalStatic public class that + Q_GLOBAL_STATIC does, but instead uses a QtGlobalStatic::ApplicationHolder + template class as the template parameter. The differences to + QtGlobalStatic::Holder are: + + \list + \li The ApplicationHolder class is empty. Unlike Holder, the storage is + provided as a \c {static inline} member, simply so that the static + member reset() function can access it without having to save the + pointer in a lambda. + + \li The ApplicationHolder constructor is trivial; initialization of the + type is instead deferred to the \c pointer() function. This means the + C++11 thread-safe initialization of statics does not protect the + object. + + \li Instead, ApplicationHolder provides a mutex (implemented as a \c + {static inline} member of type \l QBasicMutex) and locks it before + constructing or destructing the object. + + \li After constructing the object, it will QObject::connect() the + QCoreApplication::destroyed() signal to a function that will in turn + destroy the object. + + \li The destructor will destroy the object if the application is + exiting without first destroying the QCoreApplication object (i.e., a + call to \c ::exit) or this Q_APPLICATION_STATIC is part of a plugin + that is being unloaded. + \endlist + + \endomit + \sa Q_APPLICATION_STATIC_WITH_ARGS(), QGlobalStatic */ diff --git a/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp b/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp index 626c3757dab..d45333e435c 100644 --- a/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp +++ b/tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp @@ -148,7 +148,7 @@ void tst_QGlobalStatic::exception() exceptionCaught = true; } QVERIFY(exceptionCaught); - QCOMPARE(Q_QGS_throwingGS::guard.loadRelaxed(), 0); + QCOMPARE(QtGlobalStatic::Holder::guard.loadRelaxed(), 0); QVERIFY(!throwingGS.exists()); QVERIFY(!throwingGS.isDestroyed()); }