-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathatomic.hpp
More file actions
138 lines (117 loc) · 5.06 KB
/
atomic.hpp
File metadata and controls
138 lines (117 loc) · 5.06 KB
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
131
132
133
134
135
136
137
138
#pragma once
#include <conc/atomic.hpp>
#include <atomic>
#include <type_traits>
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if __cplusplus >= 202002L
#define CPP20(...) __VA_ARGS__
#else
#define CPP20(...)
#endif
namespace stdx {
inline namespace v1 {
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
template <typename T> class atomic {
static_assert(std::is_trivially_copyable_v<T> and
std::is_copy_constructible_v<T> and
std::is_move_constructible_v<T> and
std::is_copy_assignable_v<T> and
std::is_move_assignable_v<T>,
"Atomic values must be trivially copyable, copy "
"constructible and copy assignable");
using elem_t = ::atomic::atomic_type_t<T>;
constexpr static auto alignment = ::atomic::alignment_of<T>;
static_assert(std::is_trivially_copyable_v<T>,
"value_type of atomic<T> must be trivially_copyable");
static_assert(std::is_trivially_copyable_v<elem_t>,
"::atomic::atomic_type_t specialization result must be "
"trivially_copyable");
static_assert(sizeof(elem_t) >= sizeof(T),
"::atomic::atomic_type_t specialization result must be at "
"least as big as T");
static_assert(alignof(elem_t) >= alignof(T),
"::atomic::atomic_type_t specialization result must be "
"alignment-compatible with T");
static_assert(alignof(elem_t) <= alignment,
"::atomic::atomic_type_t specialization result must be "
"alignment-compatible with alignment_of<T>");
alignas(alignment) elem_t value;
public:
using value_type = T;
constexpr atomic() CPP20(requires std::is_default_constructible_v<elem_t>)
: value{} {}
constexpr explicit atomic(T t) : value{static_cast<elem_t>(t)} {}
atomic(atomic const &) = delete;
auto operator=(atomic const &) -> atomic & = delete;
[[nodiscard]] auto
load(std::memory_order mo = std::memory_order_seq_cst) const -> T {
return static_cast<T>(::atomic::load(value, mo));
}
void store(T t, std::memory_order mo = std::memory_order_seq_cst) {
::atomic::store(value, static_cast<elem_t>(t), mo);
}
// NOLINTNEXTLINE(google-explicit-constructor)
[[nodiscard]] operator T() const { return load(); }
// NOLINTNEXTLINE(misc-unconventional-assign-operator)
auto operator=(T t) -> T {
store(t);
return t;
}
[[nodiscard]] auto
exchange(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
return ::atomic::exchange(value, static_cast<elem_t>(t), mo);
}
auto fetch_add(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t + t; }, "T must support operator+(x, y)"));
return ::atomic::fetch_add(value, static_cast<elem_t>(t), mo);
}
auto fetch_sub(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t - t; }, "T must support operator-(x, y)"));
return ::atomic::fetch_sub(value, static_cast<elem_t>(t), mo);
}
auto operator+=(T t) -> T { return fetch_add(t) + t; }
auto operator-=(T t) -> T { return fetch_sub(t) - t; }
auto operator++() -> T {
CPP20(static_assert(
requires(T t) { ++t; }, "T must support operator++()"));
return ::atomic::fetch_add(value, 1) + 1;
}
[[nodiscard]] auto operator++(int) -> T {
CPP20(static_assert(
requires(T t) { t++; }, "T must support operator++(int)"));
return ::atomic::fetch_add(value, 1);
}
auto operator--() -> T {
CPP20(static_assert(
requires(T t) { --t; }, "T must support operator--()"));
return ::atomic::fetch_sub(value, 1) - 1;
}
[[nodiscard]] auto operator--(int) -> T {
CPP20(static_assert(
requires(T t) { t--; }, "T must support operator--(int)"));
return ::atomic::fetch_sub(value, 1);
}
auto fetch_and(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t & t; }, "T must support operator&(x, y)"));
return ::atomic::fetch_and(value, static_cast<elem_t>(t), mo);
}
auto fetch_or(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t | t; }, "T must support operator|(x, y)"));
return ::atomic::fetch_or(value, static_cast<elem_t>(t), mo);
}
auto fetch_xor(T t, std::memory_order mo = std::memory_order_seq_cst) -> T {
CPP20(static_assert(
requires { t ^ t; }, "T must support operator^(x, y)"));
return ::atomic::fetch_xor(value, static_cast<elem_t>(t), mo);
}
auto operator&=(T t) -> T { return fetch_and(t) & t; }
auto operator|=(T t) -> T { return fetch_or(t) | t; }
auto operator^=(T t) -> T { return fetch_xor(t) ^ t; }
};
} // namespace v1
} // namespace stdx
// NOLINTEND(cppcoreguidelines-macro-usage)