| 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 |
