1 | // Copyright (C) 2022 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QATOMICSCOPEDVALUEROLLBACK_H |
5 | #define QATOMICSCOPEDVALUEROLLBACK_H |
6 | |
7 | #include <QtCore/qassert.h> |
8 | #include <QtCore/qatomic.h> |
9 | #include <QtCore/qcompilerdetection.h> |
10 | #include <QtCore/qtclasshelpermacros.h> |
11 | #include <QtCore/qtconfigmacros.h> |
12 | |
13 | #include <atomic> |
14 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | template <typename T> |
18 | class QAtomicScopedValueRollback |
19 | { |
20 | std::atomic<T> &m_atomic; |
21 | T m_value; |
22 | std::memory_order m_mo; |
23 | |
24 | Q_DISABLE_COPY_MOVE(QAtomicScopedValueRollback) |
25 | |
26 | static constexpr std::memory_order store_part(std::memory_order mo) noexcept |
27 | { |
28 | switch (mo) { |
29 | case std::memory_order_relaxed: |
30 | case std::memory_order_consume: |
31 | case std::memory_order_acquire: return std::memory_order_relaxed; |
32 | case std::memory_order_release: |
33 | case std::memory_order_acq_rel: return std::memory_order_release; |
34 | case std::memory_order_seq_cst: return std::memory_order_seq_cst; |
35 | } |
36 | // GCC 8.x does not treat __builtin_unreachable() as constexpr |
37 | #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900) |
38 | // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8 |
39 | Q_UNREACHABLE(); |
40 | #endif |
41 | return std::memory_order_seq_cst; |
42 | } |
43 | |
44 | static constexpr std::memory_order load_part(std::memory_order mo) noexcept |
45 | { |
46 | switch (mo) { |
47 | case std::memory_order_relaxed: |
48 | case std::memory_order_release: return std::memory_order_relaxed; |
49 | case std::memory_order_consume: return std::memory_order_consume; |
50 | case std::memory_order_acquire: |
51 | case std::memory_order_acq_rel: return std::memory_order_acquire; |
52 | case std::memory_order_seq_cst: return std::memory_order_seq_cst; |
53 | } |
54 | // GCC 8.x does not treat __builtin_unreachable() as constexpr |
55 | #if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900) |
56 | // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8 |
57 | Q_UNREACHABLE(); |
58 | #endif |
59 | return std::memory_order_seq_cst; |
60 | } |
61 | public: |
62 | // |
63 | // std::atomic: |
64 | // |
65 | Q_NODISCARD_CTOR |
66 | explicit constexpr |
67 | QAtomicScopedValueRollback(std::atomic<T> &var, |
68 | std::memory_order mo = std::memory_order_seq_cst) |
69 | : m_atomic(var), m_value(var.load(load_part(mo))), m_mo(mo) {} |
70 | |
71 | Q_NODISCARD_CTOR |
72 | explicit constexpr |
73 | QAtomicScopedValueRollback(std::atomic<T> &var, T value, |
74 | std::memory_order mo = std::memory_order_seq_cst) |
75 | : m_atomic(var), m_value(var.exchange(value, mo)), m_mo(mo) {} |
76 | |
77 | // |
78 | // Q(Basic)AtomicInteger: |
79 | // |
80 | Q_NODISCARD_CTOR |
81 | explicit constexpr |
82 | QAtomicScopedValueRollback(QBasicAtomicInteger<T> &var, |
83 | std::memory_order mo = std::memory_order_seq_cst) |
84 | : QAtomicScopedValueRollback(var._q_value, mo) {} |
85 | |
86 | Q_NODISCARD_CTOR |
87 | explicit constexpr |
88 | QAtomicScopedValueRollback(QBasicAtomicInteger<T> &var, T value, |
89 | std::memory_order mo = std::memory_order_seq_cst) |
90 | : QAtomicScopedValueRollback(var._q_value, value, mo) {} |
91 | |
92 | // |
93 | // Q(Basic)AtomicPointer: |
94 | // |
95 | Q_NODISCARD_CTOR |
96 | explicit constexpr |
97 | QAtomicScopedValueRollback(QBasicAtomicPointer<std::remove_pointer_t<T>> &var, |
98 | std::memory_order mo = std::memory_order_seq_cst) |
99 | : QAtomicScopedValueRollback(var._q_value, mo) {} |
100 | |
101 | Q_NODISCARD_CTOR |
102 | explicit constexpr |
103 | QAtomicScopedValueRollback(QBasicAtomicPointer<std::remove_pointer_t<T>> &var, T value, |
104 | std::memory_order mo = std::memory_order_seq_cst) |
105 | : QAtomicScopedValueRollback(var._q_value, value, mo) {} |
106 | |
107 | ~QAtomicScopedValueRollback() |
108 | { |
109 | m_atomic.store(m_value, store_part(mo: m_mo)); |
110 | } |
111 | |
112 | void commit() |
113 | { |
114 | m_value = m_atomic.load(load_part(mo: m_mo)); |
115 | } |
116 | }; |
117 | |
118 | template <typename T> |
119 | QAtomicScopedValueRollback(QBasicAtomicPointer<T> &) |
120 | -> QAtomicScopedValueRollback<T*>; |
121 | template <typename T> |
122 | QAtomicScopedValueRollback(QBasicAtomicPointer<T> &, std::memory_order) |
123 | -> QAtomicScopedValueRollback<T*>; |
124 | |
125 | QT_END_NAMESPACE |
126 | |
127 | #endif // QATOMICASCOPEDVALUEROLLBACK_H |
128 |
Definitions
Learn Advanced QML with KDAB
Find out more