1 | // Copyright (C) 2021 The Qt Company Ltd. |
---|---|
2 | // Copyright (C) 2021 Intel Corporation. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #ifndef QAPPLICATIONSTATIC_H |
6 | #define QAPPLICATIONSTATIC_H |
7 | |
8 | #include <QtCore/QMutex> |
9 | #include <QtCore/qcoreapplication.h> |
10 | #include <QtCore/qglobalstatic.h> |
11 | |
12 | #include <new> |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | namespace QtGlobalStatic { |
17 | template <typename QAS> struct ApplicationHolder |
18 | { |
19 | using Type = typename QAS::QAS_Type; |
20 | using PlainType = std::remove_cv_t<Type>; |
21 | |
22 | Q_CONSTINIT static inline struct { alignas(Type) unsigned char data[sizeof(Type)]; } storage = {}; |
23 | Q_CONSTINIT static inline QBasicAtomicInteger<qint8> guard = { QtGlobalStatic::Uninitialized }; |
24 | Q_CONSTINIT static inline QBasicMutex mutex {}; |
25 | |
26 | static constexpr bool MutexLockIsNoexcept = noexcept(mutex.lock()); |
27 | static constexpr bool ConstructionIsNoexcept = noexcept(QAS::innerFunction(nullptr)); |
28 | |
29 | ApplicationHolder() = default; |
30 | Q_DISABLE_COPY_MOVE(ApplicationHolder) |
31 | ~ApplicationHolder() |
32 | { |
33 | if (guard.loadAcquire() == QtGlobalStatic::Initialized) { |
34 | // No mutex! Up to external code to ensure no race happens. |
35 | guard.storeRelease(newValue: QtGlobalStatic::Destroyed); |
36 | realPointer()->~PlainType(); |
37 | } |
38 | } |
39 | |
40 | static PlainType *realPointer() |
41 | { |
42 | return std::launder(reinterpret_cast<PlainType *>(&storage)); |
43 | } |
44 | |
45 | // called from QGlobalStatic::instance() |
46 | PlainType *pointer() noexcept(MutexLockIsNoexcept && ConstructionIsNoexcept) |
47 | { |
48 | if (guard.loadAcquire() == QtGlobalStatic::Initialized) |
49 | return realPointer(); |
50 | QMutexLocker locker(&mutex); |
51 | if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { |
52 | QAS::innerFunction(&storage); |
53 | QObject::connect(QCoreApplication::instance(), &QObject::destroyed, reset); |
54 | guard.storeRelease(newValue: QtGlobalStatic::Initialized); |
55 | } |
56 | return realPointer(); |
57 | } |
58 | |
59 | static void reset() |
60 | { |
61 | // we only synchronize using the mutex here, not the guard |
62 | QMutexLocker locker(&mutex); |
63 | realPointer()->~PlainType(); |
64 | guard.storeRelaxed(newValue: QtGlobalStatic::Uninitialized); |
65 | } |
66 | }; |
67 | } // namespace QtGlobalStatic |
68 | |
69 | #define Q_APPLICATION_STATIC(TYPE, NAME, ...) \ |
70 | namespace { struct Q_QAS_ ## NAME { \ |
71 | typedef TYPE QAS_Type; \ |
72 | static void innerFunction(void *pointer) \ |
73 | noexcept(noexcept(std::remove_cv_t<QAS_Type>(__VA_ARGS__))) \ |
74 | { \ |
75 | new (pointer) QAS_Type(__VA_ARGS__); \ |
76 | } \ |
77 | }; } \ |
78 | static QGlobalStatic<QtGlobalStatic::ApplicationHolder<Q_QAS_ ## NAME>> NAME;\ |
79 | /**/ |
80 | |
81 | QT_END_NAMESPACE |
82 | |
83 | #endif // QAPPLICATIONSTATIC_H |
84 |