summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qatomicscopedvaluerollback.h
blob: 41e919a3c63fee74f6861b23bc8082490d263235 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QATOMICSCOPEDVALUEROLLBACK_H
#define QATOMICSCOPEDVALUEROLLBACK_H

#include <QtCore/qassert.h>
#include <QtCore/qatomic.h>
#include <QtCore/qcompilerdetection.h>
#include <QtCore/qtclasshelpermacros.h>
#include <QtCore/qtconfigmacros.h>

#include <atomic>

QT_BEGIN_NAMESPACE

template <typename T>
class QAtomicScopedValueRollback
{
    std::atomic<T> &m_atomic;
    T m_value;
    std::memory_order m_mo;

    Q_DISABLE_COPY_MOVE(QAtomicScopedValueRollback)

    static constexpr std::memory_order store_part(std::memory_order mo) noexcept
    {
        switch (mo) {
        case std::memory_order_relaxed:
        case std::memory_order_consume:
        case std::memory_order_acquire: return std::memory_order_relaxed;
        case std::memory_order_release:
        case std::memory_order_acq_rel: return std::memory_order_release;
        case std::memory_order_seq_cst: return std::memory_order_seq_cst;
        }
        // GCC 8.x does not treat __builtin_unreachable() as constexpr
#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
        // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8
        Q_UNREACHABLE();
#endif
        return std::memory_order_seq_cst;
    }

    static constexpr std::memory_order load_part(std::memory_order mo) noexcept
    {
        switch (mo) {
        case std::memory_order_relaxed:
        case std::memory_order_release: return std::memory_order_relaxed;
        case std::memory_order_consume: return std::memory_order_consume;
        case std::memory_order_acquire:
        case std::memory_order_acq_rel: return std::memory_order_acquire;
        case std::memory_order_seq_cst: return std::memory_order_seq_cst;
        }
        // GCC 8.x does not treat __builtin_unreachable() as constexpr
#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
        // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8
        Q_UNREACHABLE();
#endif
        return std::memory_order_seq_cst;
    }
public:
    //
    // std::atomic:
    //
    Q_NODISCARD_CTOR
    explicit constexpr
    QAtomicScopedValueRollback(std::atomic<T> &var,
                               std::memory_order mo = std::memory_order_seq_cst)
        : m_atomic(var), m_value(var.load(load_part(mo))), m_mo(mo) {}

    Q_NODISCARD_CTOR
    explicit constexpr
    QAtomicScopedValueRollback(std::atomic<T> &var, T value,
                               std::memory_order mo = std::memory_order_seq_cst)
        : m_atomic(var), m_value(var.exchange(value, mo)), m_mo(mo) {}

    //
    // Q(Basic)AtomicInteger:
    //
    Q_NODISCARD_CTOR
    explicit constexpr
    QAtomicScopedValueRollback(QBasicAtomicInteger<T> &var,
                               std::memory_order mo = std::memory_order_seq_cst)
        : QAtomicScopedValueRollback(var._q_value, mo) {}

    Q_NODISCARD_CTOR
    explicit constexpr
    QAtomicScopedValueRollback(QBasicAtomicInteger<T> &var, T value,
                               std::memory_order mo = std::memory_order_seq_cst)
        : QAtomicScopedValueRollback(var._q_value, value, mo) {}

    //
    // Q(Basic)AtomicPointer:
    //
    Q_NODISCARD_CTOR
    explicit constexpr
    QAtomicScopedValueRollback(QBasicAtomicPointer<std::remove_pointer_t<T>> &var,
                               std::memory_order mo = std::memory_order_seq_cst)
        : QAtomicScopedValueRollback(var._q_value, mo) {}

    Q_NODISCARD_CTOR
    explicit constexpr
    QAtomicScopedValueRollback(QBasicAtomicPointer<std::remove_pointer_t<T>> &var, T value,
                               std::memory_order mo = std::memory_order_seq_cst)
        : QAtomicScopedValueRollback(var._q_value, value, mo) {}

#if __cpp_constexpr >= 201907L
    constexpr
#endif
    ~QAtomicScopedValueRollback()
    {
        m_atomic.store(m_value, store_part(m_mo));
    }

    constexpr void commit()
    {
        m_value = m_atomic.load(load_part(m_mo));
    }
};

template <typename T>
QAtomicScopedValueRollback(QBasicAtomicPointer<T> &)
    -> QAtomicScopedValueRollback<T*>;
template <typename T>
QAtomicScopedValueRollback(QBasicAtomicPointer<T> &, std::memory_order)
    -> QAtomicScopedValueRollback<T*>;

QT_END_NAMESPACE

#endif // QATOMICASCOPEDVALUEROLLBACK_H