Rewrite Q_{GLOBAL,APPLICATION}_STATIC with C++17 goodies
Especially static inline variables. This greatly reduces the amount of code that existed in macros, moving them to templates. Additionally, this removes one level of indirection from Q_APPLICATION_STATIC by removing the std::unique_ptr. We now directly manage the object's storage. Change-Id: I2cffe62afda945079b63fffd16bcc825cc04334e Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
parent
fbbcd109f5
commit
81a31beeb2
@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 Intel Corporation.
|
** Copyright (C) 2021 Intel Corporation.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the QtCore module of the Qt Toolkit.
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
@ -44,6 +44,7 @@
|
|||||||
|
|
||||||
#include <QtCore/qatomic.h>
|
#include <QtCore/qatomic.h>
|
||||||
|
|
||||||
|
#include <atomic> // for bootstrapped (no thread) builds
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
@ -55,92 +56,92 @@ enum GuardValues {
|
|||||||
Uninitialized = 0,
|
Uninitialized = 0,
|
||||||
Initializing = 1
|
Initializing = 1
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
#define Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \
|
template <typename QGS> struct Holder
|
||||||
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
|
|
||||||
// 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
|
|
||||||
|
|
||||||
#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; \
|
|
||||||
}
|
|
||||||
|
|
||||||
// this class must be POD, unless the compiler supports thread-safe statics
|
|
||||||
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
|
|
||||||
struct QGlobalStatic
|
|
||||||
{
|
{
|
||||||
typedef T Type;
|
using Type = typename QGS::QGS_Type;
|
||||||
|
using PlainType = std::remove_cv_t<Type>;
|
||||||
|
|
||||||
bool isDestroyed() const { return guard.loadRelaxed() <= QtGlobalStatic::Destroyed; }
|
static constexpr bool ConstructionIsNoexcept = noexcept(QGS::innerFunction(nullptr));
|
||||||
bool exists() const { return guard.loadRelaxed() == QtGlobalStatic::Initialized; }
|
std::aligned_union_t<1, PlainType> storage;
|
||||||
|
static inline QBasicAtomicInteger<qint8> guard = { QtGlobalStatic::Uninitialized };
|
||||||
|
|
||||||
|
Holder() noexcept(ConstructionIsNoexcept)
|
||||||
|
{
|
||||||
|
QGS::innerFunction(pointer());
|
||||||
|
guard.storeRelaxed(QtGlobalStatic::Initialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Holder()
|
||||||
|
{
|
||||||
|
guard.storeRelaxed(QtGlobalStatic::Destroyed);
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire); // avoid mixing stores to guard and *pointer()
|
||||||
|
pointer()->~PlainType();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlainType *pointer() noexcept
|
||||||
|
{
|
||||||
|
return reinterpret_cast<PlainType *>(&storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DISABLE_COPY_MOVE(Holder)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Holder> struct QGlobalStatic
|
||||||
|
{
|
||||||
|
using Type = typename Holder::Type;
|
||||||
|
|
||||||
|
bool isDestroyed() const { return guardValue() <= QtGlobalStatic::Destroyed; }
|
||||||
|
bool exists() const { return guardValue() == QtGlobalStatic::Initialized; }
|
||||||
operator Type *()
|
operator Type *()
|
||||||
{
|
{
|
||||||
if (isDestroyed())
|
if (isDestroyed())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return innerFunction();
|
return instance();
|
||||||
}
|
}
|
||||||
Type *operator()()
|
Type *operator()()
|
||||||
{
|
{
|
||||||
if (isDestroyed())
|
if (isDestroyed())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return innerFunction();
|
return instance();
|
||||||
}
|
}
|
||||||
Type *operator->()
|
Type *operator->()
|
||||||
{
|
{
|
||||||
Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC",
|
Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC",
|
||||||
"The global static was used after being destroyed");
|
"The global static was used after being destroyed");
|
||||||
return innerFunction();
|
return instance();
|
||||||
}
|
}
|
||||||
Type &operator*()
|
Type &operator*()
|
||||||
{
|
{
|
||||||
Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC",
|
Q_ASSERT_X(!isDestroyed(), "Q_GLOBAL_STATIC",
|
||||||
"The global static was used after being destroyed");
|
"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) \
|
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
|
||||||
QT_WARNING_PUSH \
|
namespace { struct Q_QGS_ ## NAME { \
|
||||||
QT_WARNING_DISABLE_CLANG("-Wunevaluated-expression") \
|
typedef TYPE QGS_Type; \
|
||||||
namespace { namespace Q_QGS_ ## NAME { \
|
static void innerFunction(void *pointer) \
|
||||||
typedef TYPE Type; \
|
noexcept(noexcept(std::remove_cv_t<QGS_Type> ARGS)) \
|
||||||
QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
|
{ \
|
||||||
Q_GLOBAL_STATIC_INTERNAL(ARGS) \
|
new (pointer) QGS_Type ARGS; \
|
||||||
} } \
|
} \
|
||||||
static QGlobalStatic<TYPE, \
|
}; } \
|
||||||
Q_QGS_ ## NAME::innerFunction, \
|
static QGlobalStatic<QtGlobalStatic::Holder<Q_QGS_ ## NAME>> NAME; \
|
||||||
Q_QGS_ ## NAME::guard> NAME; \
|
/**/
|
||||||
QT_WARNING_POP
|
|
||||||
|
|
||||||
#define Q_GLOBAL_STATIC(TYPE, NAME) \
|
#define Q_GLOBAL_STATIC(TYPE, NAME) \
|
||||||
Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())
|
Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ())
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 Intel Corporation.
|
** Copyright (C) 2021 Intel Corporation.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the documentation of the Qt Toolkit.
|
** This file is part of the documentation of the Qt Toolkit.
|
||||||
@ -195,11 +195,11 @@
|
|||||||
\omit
|
\omit
|
||||||
\section1 Compatibility with Qt 4 and Qt 5.0
|
\section1 Compatibility with Qt 4 and Qt 5.0
|
||||||
|
|
||||||
This macro, in its current form and behavior, was introduced in Qt 5.1.
|
This macro, in its current behavior, was introduced in Qt 5.1. Prior to
|
||||||
Prior to that version, Qt had another macro with the same name that was
|
that version, Qt had another macro with the same name that was private API.
|
||||||
private API. This section is not meant to document how to use
|
This section is not meant to document how to use Q_GLOBAL_STATIC in those
|
||||||
Q_GLOBAL_STATIC in those versions, but instead to serve as a porting guide
|
versions, but instead to serve as a porting guide for Qt code that used
|
||||||
for Qt code that used those macros.
|
those macros.
|
||||||
|
|
||||||
The Qt 4 Q_GLOBAL_STATIC macro differed in behavior in the following ways:
|
The Qt 4 Q_GLOBAL_STATIC macro differed in behavior in the following ways:
|
||||||
|
|
||||||
@ -218,24 +218,38 @@
|
|||||||
|
|
||||||
\section1 Implementation Details
|
\section1 Implementation Details
|
||||||
|
|
||||||
Q_GLOBAL_STATIC is implemented by creating a QBasicAtomicInt called the \c
|
Q_GLOBAL_STATIC is implemented by creating a type called \c Q_QGS_NAME
|
||||||
guard and a free, inline function called \c innerFunction. The guard
|
where \c NAME is the name of the variable the user passed to the macro,
|
||||||
variable is initialized to value 0 (chosen so that the guard can be placed
|
inside an unnamed namespace, and a \c static variable of \l QGlobalStatic
|
||||||
in the .bss section of the binary file), which denotes that construction
|
type, named \c NAME. The use of unnamed namespaces forces the compiler to emit
|
||||||
has not yet taken place (uninitialized). The inner function is implemented
|
non-exported symbols, often local to the translation unit, and this
|
||||||
by the helper macro Q_GLOBAL_STATIC_INTERNAL.
|
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
|
The "QGS" type is a \c struct containing a \c typedef to \a TYPE and a
|
||||||
parameters to QGlobalStatic, along with the type \a Type. Both should also
|
static member function that receives a pointer to storage suitable to hold
|
||||||
have static linkage or be placed inside an anonymous namespace, so that the
|
an instance of \a TYPE. It will initialize the storage using a placement \c
|
||||||
visibility of Q_GLOBAL_STATIC is that of a global static. To permit
|
new and the variadic arguments.
|
||||||
multiple Q_GLOBAL_STATIC per translation unit, the guard variable and the
|
|
||||||
inner function must have unique names, which can be accomplished by
|
The majority of the work is done by the public \l QGlobalStatic class and
|
||||||
concatenating with \a VariableName or by placing them in a namespace that
|
the private \c QtGlobalStatic::Holder class. The \c Holder class has a
|
||||||
has \a VariableName as part of the name. To simplify and improve
|
non-static, trivial member of suitable size and alignment to hold \a TYPE
|
||||||
readability on Q_GLOBAL_STATIC_INTERNAL, we chose the namespace solution.
|
(a \c{std::aligned_union_t} or a \c{std::aligned_storage_t}). The
|
||||||
It's also required for C++98 builds, since static symbols cannot be used as
|
constructor calls the "QGS" type's static member function with a pointer to
|
||||||
template parameters.
|
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:
|
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
|
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.
|
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
|
\endomit
|
||||||
|
|
||||||
\sa Q_GLOBAL_STATIC_WITH_ARGS(), QGlobalStatic
|
\sa Q_GLOBAL_STATIC_WITH_ARGS(), QGlobalStatic
|
||||||
@ -356,7 +342,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> bool QGlobalStatic<T, innerFunction, guard>::isDestroyed() const
|
\fn template <typename Holder> bool QGlobalStatic<Holder>::isDestroyed() const
|
||||||
|
|
||||||
This function returns \c true if the global static object has already
|
This function returns \c true if the global static object has already
|
||||||
completed destruction (that is, if the destructor for the type has already
|
completed destruction (that is, if the destructor for the type has already
|
||||||
@ -386,7 +372,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> bool QGlobalStatic<T, innerFunction, guard>::exists() const
|
\fn template <typename Holder> bool QGlobalStatic<Holder>::exists() const
|
||||||
|
|
||||||
This function returns \c true if the global static object has already
|
This function returns \c true if the global static object has already
|
||||||
completed initialization (that is, if the constructor for the type has
|
completed initialization (that is, if the constructor for the type has
|
||||||
@ -436,7 +422,7 @@
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
\keyword qglobalstatic-operator-type-ptr
|
\keyword qglobalstatic-operator-type-ptr
|
||||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> QGlobalStatic<T, innerFunction, guard>::operator Type*()
|
\fn template <typename Holder> QGlobalStatic<Holder>::operator Type*()
|
||||||
|
|
||||||
This function returns the address of the contents of this global static. If
|
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
|
the contents have not yet been created, they will be created thread-safely
|
||||||
@ -469,7 +455,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> Type *QGlobalStatic<T, innerFunction, guard>::operator()()
|
\fn template <typename Holder> Type *QGlobalStatic<Holder>::operator()()
|
||||||
\deprecated
|
\deprecated
|
||||||
|
|
||||||
This function returns the address of the contents of this global static. If
|
This function returns the address of the contents of this global static. If
|
||||||
@ -485,7 +471,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> Type *QGlobalStatic<T, innerFunction, guard>::operator->()
|
\fn template <typename Holder> Type *QGlobalStatic<Holder>::operator->()
|
||||||
|
|
||||||
This function returns the address of the contents of this global static. If
|
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
|
the contents have not yet been created, they will be created thread-safely
|
||||||
@ -498,7 +484,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard> Type &QGlobalStatic<T, innerFunction, guard>::operator*()
|
\fn template <typename Holder> Type &QGlobalStatic<Holder>::operator*()
|
||||||
|
|
||||||
This function returns a reference to the contents of this global static. If
|
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
|
the contents have not yet been created, they will be created thread-safely
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2021 The Qt Company Ltd.
|
** Copyright (C) 2021 The Qt Company Ltd.
|
||||||
|
** Copyright (C) 2021 Intel Corporation.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the QtCore module of the Qt Toolkit.
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
@ -41,58 +42,75 @@
|
|||||||
#define QAPPLICATIONSTATIC_H
|
#define QAPPLICATIONSTATIC_H
|
||||||
|
|
||||||
#include <QtCore/QMutex>
|
#include <QtCore/QMutex>
|
||||||
|
#include <QtCore/qcoreapplication.h>
|
||||||
#include <QtCore/qglobalstatic.h>
|
#include <QtCore/qglobalstatic.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
#define Q_APPLICATION_STATIC_INTERNAL_HOLDER(ARGS) \
|
namespace QtGlobalStatic {
|
||||||
Q_GLOBAL_STATIC_INTERNAL_HOLDER(ARGS) \
|
template <typename QAS> struct ApplicationHolder
|
||||||
struct LifecycleHolder : public Holder \
|
{
|
||||||
{ \
|
using Type = typename QAS::QAS_Type;
|
||||||
LifecycleHolder() { connectLifecycle(); } \
|
using PlainType = std::remove_cv_t<Type>;
|
||||||
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) \
|
static inline std::aligned_union_t<1, PlainType> storage;
|
||||||
Q_GLOBAL_STATIC_INTERNAL_DECORATION BaseType *innerFunction() \
|
static inline QBasicAtomicInteger<qint8> guard = { QtGlobalStatic::Uninitialized };
|
||||||
{ \
|
static inline QBasicMutex mutex {};
|
||||||
Q_APPLICATION_STATIC_INTERNAL_HOLDER(ARGS) \
|
|
||||||
static LifecycleHolder holder; \
|
static constexpr bool MutexLockIsNoexcept = noexcept(mutex.lock());
|
||||||
return holder.get(); \
|
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<PlainType *>(&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) \
|
#define Q_APPLICATION_STATIC_WITH_ARGS(TYPE, NAME, ARGS) \
|
||||||
QT_WARNING_PUSH \
|
namespace { struct Q_QAS_ ## NAME { \
|
||||||
QT_WARNING_DISABLE_CLANG("-Wunevaluated-expression") \
|
typedef TYPE QAS_Type; \
|
||||||
namespace { namespace Q_QAS_ ## NAME { \
|
static void innerFunction(void *pointer) \
|
||||||
typedef TYPE BaseType; \
|
noexcept(noexcept(std::remove_cv_t<QAS_Type> ARGS)) \
|
||||||
typedef std::unique_ptr<BaseType> Type; \
|
{ \
|
||||||
QBasicAtomicInt guard = Q_BASIC_ATOMIC_INITIALIZER(QtGlobalStatic::Uninitialized); \
|
new (pointer) QAS_Type ARGS; \
|
||||||
QBasicMutex theMutex; \
|
} \
|
||||||
Q_APPLICATION_STATIC_INTERNAL((new BaseType ARGS)) \
|
}; } \
|
||||||
} } \
|
static QGlobalStatic<QtGlobalStatic::ApplicationHolder<Q_QAS_ ## NAME>> NAME;\
|
||||||
static QGlobalStatic<TYPE, \
|
/**/
|
||||||
Q_QAS_ ## NAME::innerFunction, \
|
|
||||||
Q_QAS_ ## NAME::guard> NAME; \
|
|
||||||
QT_WARNING_POP
|
|
||||||
|
|
||||||
#define Q_APPLICATION_STATIC(TYPE, NAME) \
|
#define Q_APPLICATION_STATIC(TYPE, NAME) \
|
||||||
Q_APPLICATION_STATIC_WITH_ARGS(TYPE, NAME, ())
|
Q_APPLICATION_STATIC_WITH_ARGS(TYPE, NAME, ())
|
||||||
|
@ -47,8 +47,10 @@
|
|||||||
type will be recreated when it's accessed again once a new QCoreApplication
|
type will be recreated when it's accessed again once a new QCoreApplication
|
||||||
has been created.
|
has been created.
|
||||||
|
|
||||||
Since the value is bound to the QCoreApplication it should only ever be accessed,
|
Since the value is bound to the QCoreApplication, it should only ever be
|
||||||
if there is a valid QCoreApplication::instance().
|
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,
|
The typical use of this macro is as follows, in a global context (that is,
|
||||||
outside of any function bodies):
|
outside of any function bodies):
|
||||||
@ -61,6 +63,42 @@
|
|||||||
this macro behaves identically to Q_GLOBAL_STATIC(). Please see that macro's
|
this macro behaves identically to Q_GLOBAL_STATIC(). Please see that macro's
|
||||||
documentation for more information.
|
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
|
\sa Q_APPLICATION_STATIC_WITH_ARGS(), QGlobalStatic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ void tst_QGlobalStatic::exception()
|
|||||||
exceptionCaught = true;
|
exceptionCaught = true;
|
||||||
}
|
}
|
||||||
QVERIFY(exceptionCaught);
|
QVERIFY(exceptionCaught);
|
||||||
QCOMPARE(Q_QGS_throwingGS::guard.loadRelaxed(), 0);
|
QCOMPARE(QtGlobalStatic::Holder<Q_QGS_throwingGS>::guard.loadRelaxed(), 0);
|
||||||
QVERIFY(!throwingGS.exists());
|
QVERIFY(!throwingGS.exists());
|
||||||
QVERIFY(!throwingGS.isDestroyed());
|
QVERIFY(!throwingGS.isDestroyed());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user