-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfetch_max.hpp
129 lines (115 loc) · 3.98 KB
/
fetch_max.hpp
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
#pragma once
#include <algorithm>
#include <atomic>
#include <cstdint>
/**
* @brief enum for different implementation types
*/
enum class type_e : std::size_t { strong = 0, weak, smart, hardware, faster };
/**
* @brief templated fetch_max implementation, with varying semantics
*
* @tparam mpl_e implementation type
*/
template <type_e> struct atomic_fetch_max;
inline constexpr std::memory_order drop_release(std::memory_order m) noexcept {
return (m == std::memory_order_release ? std::memory_order_relaxed
: ((m == std::memory_order_acq_rel ||
m == std::memory_order_seq_cst)
? std::memory_order_acquire
: m));
}
template <> struct atomic_fetch_max<type_e::strong> final {
template <typename T>
auto operator()(std::atomic<T> *pv, typename std::atomic<T>::value_type v,
std::memory_order m) const noexcept -> T {
using std::max;
auto t = pv->load(drop_release(m));
while (!pv->compare_exchange_weak(t, max(v, t), m, drop_release(m)))
;
return t;
}
};
template <> struct atomic_fetch_max<type_e::weak> final {
template <typename T>
auto operator()(std::atomic<T> *pv, typename std::atomic<T>::value_type v,
std::memory_order m) const noexcept -> T {
using std::max;
auto t = pv->load(drop_release(m));
while (max(v, t) != t || (m == std::memory_order_release || //
m == std::memory_order_acq_rel || //
m == std::memory_order_seq_cst)) {
if (pv->compare_exchange_weak(t, v, m, drop_release(m))) {
break;
}
}
return t;
}
};
template <> struct atomic_fetch_max<type_e::smart> final {
template <typename T>
auto operator()(std::atomic<T> *pv, typename std::atomic<T>::value_type v,
std::memory_order m) const noexcept -> T {
using std::max;
auto const mr = drop_release(m);
auto t = (mr != m) ? pv->fetch_add(0, m) : pv->load(mr);
while (max(v, t) != t) {
if (pv->compare_exchange_weak(t, v, m, mr))
return t;
}
return t;
}
};
template <> struct atomic_fetch_max<type_e::hardware> final {
#if defined(__ARM_ARCH_8A)
auto operator()(std::atomic<int> *pv, int v,
std::memory_order m) const noexcept -> int {
using namespace std;
int x;
if (m == memory_order_relaxed)
__asm__ __volatile__("ldsmax %w1,%w0,[%2]" : "=r"(x) : "r"(v), "r"(pv) :);
else if (m == memory_order_release)
__asm__ __volatile__("ldsmaxl %w1,%w0,[%2]"
: "=r"(x)
: "r"(v), "r"(pv)
:);
else if (m == memory_order_acquire || m == memory_order_consume)
__asm__ __volatile__("ldsmaxa %w1,%w0,[%2]"
: "=r"(x)
: "r"(v), "r"(pv)
:);
else if (m == memory_order_acq_rel || m == memory_order_seq_cst)
__asm__ __volatile__("ldsmaxal %w1,%w0,[%2]"
: "=r"(x)
: "r"(v), "r"(pv)
:);
else {
__builtin_unreachable();
}
return x;
}
#else
auto operator()(std::atomic<int> *, int, std::memory_order) const noexcept
-> int {
__builtin_unreachable();
}
#endif
};
template <> struct atomic_fetch_max<type_e::faster> final {
auto operator()(std::atomic<int> *pv, int v,
std::memory_order m) const noexcept -> int {
using namespace std;
using std::max;
if (m == std::memory_order_release || //
m == std::memory_order_acq_rel || //
m == std::memory_order_seq_cst) {
return atomic_fetch_max<type_e::hardware>{}(pv, v, m);
} else {
auto t = pv->load(drop_release(m));
if (max(t, v) != t) {
return atomic_fetch_max<type_e::hardware>{}(pv, v, m);
}
return t;
}
}
};