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 | #if 0 |
15 | #pragma qt_class(QApplicationStatic) |
16 | #endif |
17 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | namespace QtGlobalStatic { |
21 | template <typename QAS> struct ApplicationHolder |
22 | { |
23 | using Type = typename QAS::QAS_Type; |
24 | using PlainType = std::remove_cv_t<Type>; |
25 | |
26 | Q_CONSTINIT static inline struct { alignas(Type) unsigned char data[sizeof(Type)]; } storage = {}; |
27 | Q_CONSTINIT static inline QBasicAtomicInteger<qint8> guard = { QtGlobalStatic::Uninitialized }; |
28 | Q_CONSTINIT static inline QBasicMutex mutex {}; |
29 | |
30 | static constexpr bool MutexLockIsNoexcept = noexcept(mutex.lock()); |
31 | static constexpr bool ConstructionIsNoexcept = noexcept(QAS::innerFunction(nullptr)); |
32 | |
33 | ApplicationHolder() = default; |
34 | Q_DISABLE_COPY_MOVE(ApplicationHolder) |
35 | ~ApplicationHolder() |
36 | { |
37 | if (guard.loadAcquire() == QtGlobalStatic::Initialized) { |
38 | // No mutex! Up to external code to ensure no race happens. |
39 | guard.storeRelease(newValue: QtGlobalStatic::Destroyed); |
40 | realPointer()->~PlainType(); |
41 | } |
42 | } |
43 | |
44 | static PlainType *realPointer() |
45 | { |
46 | return std::launder(reinterpret_cast<PlainType *>(&storage)); |
47 | } |
48 | |
49 | // called from QGlobalStatic::instance() |
50 | PlainType *pointer() noexcept(MutexLockIsNoexcept && ConstructionIsNoexcept) |
51 | { |
52 | if (guard.loadAcquire() == QtGlobalStatic::Initialized) |
53 | return realPointer(); |
54 | QMutexLocker locker(&mutex); |
55 | if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { |
56 | QAS::innerFunction(&storage); |
57 | const auto *app = QCoreApplication::instance(); |
58 | Q_ASSERT_X(app, Q_FUNC_INFO, |
59 | "The application static was used without a QCoreApplication instance" ); |
60 | QObject::connect(app, &QObject::destroyed, app, reset, Qt::DirectConnection); |
61 | guard.storeRelease(newValue: QtGlobalStatic::Initialized); |
62 | } |
63 | return realPointer(); |
64 | } |
65 | |
66 | static void reset() |
67 | { |
68 | // we only synchronize using the mutex here, not the guard |
69 | QMutexLocker locker(&mutex); |
70 | realPointer()->~PlainType(); |
71 | guard.storeRelaxed(newValue: QtGlobalStatic::Uninitialized); |
72 | } |
73 | }; |
74 | } // namespace QtGlobalStatic |
75 | |
76 | #define Q_APPLICATION_STATIC(TYPE, NAME, ...) \ |
77 | namespace { struct Q_QAS_ ## NAME { \ |
78 | typedef TYPE QAS_Type; \ |
79 | static void innerFunction(void *pointer) \ |
80 | noexcept(noexcept(std::remove_cv_t<QAS_Type>(__VA_ARGS__))) \ |
81 | { \ |
82 | new (pointer) QAS_Type(__VA_ARGS__); \ |
83 | } \ |
84 | }; } \ |
85 | static QGlobalStatic<QtGlobalStatic::ApplicationHolder<Q_QAS_ ## NAME>> NAME;\ |
86 | /**/ |
87 | |
88 | QT_END_NAMESPACE |
89 | |
90 | #endif // QAPPLICATIONSTATIC_H |
91 | |