libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
atomic.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <atomic>
14#include <type_traits>
15#include <promeki/namespace.h>
16
17PROMEKI_NAMESPACE_BEGIN
18
28enum class MemoryOrder {
29 Relaxed,
30 Consume,
31 Acquire,
32 Release,
33 AcqRel,
34 SeqCst
35};
36
43inline constexpr std::memory_order toStdMemoryOrder(MemoryOrder mo) {
44 switch (mo) {
45 case MemoryOrder::Relaxed: return std::memory_order_relaxed;
46 case MemoryOrder::Consume: return std::memory_order_consume;
47 case MemoryOrder::Acquire: return std::memory_order_acquire;
48 case MemoryOrder::Release: return std::memory_order_release;
49 case MemoryOrder::AcqRel: return std::memory_order_acq_rel;
50 case MemoryOrder::SeqCst: return std::memory_order_seq_cst;
51 }
52 return std::memory_order_seq_cst;
53}
54
65inline void atomicThreadFence(MemoryOrder mo) {
66 std::atomic_thread_fence(toStdMemoryOrder(mo));
67}
68
105template <typename T> class Atomic {
106 static_assert(std::is_trivially_copyable_v<T>,
107 "Atomic<T> requires T to be trivially copyable (std::atomic requirement)");
108
109 public:
111 using ValueType = T;
112
122 explicit Atomic() : _value(T{}) {}
123
125 explicit Atomic(T val) : _value(val) {}
126
128 ~Atomic() = default;
129
130 Atomic(const Atomic &) = delete;
131 Atomic &operator=(const Atomic &) = delete;
132 Atomic(Atomic &&) = delete;
133 Atomic &operator=(Atomic &&) = delete;
134
135 // --------------------------------------------------------
136 // Default-order accessors (preserved API).
137 // --------------------------------------------------------
138
143 T value() const { return _value.load(std::memory_order_acquire); }
144
149 void setValue(T val) { _value.store(val, std::memory_order_release); }
150
170 bool compareAndSwap(T &expected, T desired) {
171 return _value.compare_exchange_strong(expected, desired, std::memory_order_acq_rel,
172 std::memory_order_acquire);
173 }
174
180 T exchange(T desired) { return _value.exchange(desired, std::memory_order_acq_rel); }
181
182 // --------------------------------------------------------
183 // Explicit-order accessors.
184 //
185 // Defaults follow the C++ convention: SeqCst on
186 // load / store / exchange / compare-exchange (the
187 // strongest, safest choice when callers don't think
188 // hard about it), AcqRel on read-modify-write
189 // (fetch_*) since RMW with seq_cst is a meaningful
190 // extra cost on most ISAs and AcqRel is what a
191 // refcount-style operation actually needs. Mirrored
192 // identically by @ref AtomicRef.
193 // --------------------------------------------------------
194
196 T load(MemoryOrder mo = MemoryOrder::SeqCst) const {
197 return _value.load(toStdMemoryOrder(mo));
198 }
199
201 void store(T val, MemoryOrder mo = MemoryOrder::SeqCst) {
202 _value.store(val, toStdMemoryOrder(mo));
203 }
204
206 T exchange(T desired, MemoryOrder mo) {
207 return _value.exchange(desired, toStdMemoryOrder(mo));
208 }
209
211 T fetchAndAdd(T val, MemoryOrder mo = MemoryOrder::AcqRel) {
212 return _value.fetch_add(val, toStdMemoryOrder(mo));
213 }
214
216 T fetchAndSub(T val, MemoryOrder mo = MemoryOrder::AcqRel) {
217 return _value.fetch_sub(val, toStdMemoryOrder(mo));
218 }
219
221 T fetchAndAnd(T val, MemoryOrder mo = MemoryOrder::AcqRel) {
222 return _value.fetch_and(val, toStdMemoryOrder(mo));
223 }
224
226 T fetchAndOr(T val, MemoryOrder mo = MemoryOrder::AcqRel) {
227 return _value.fetch_or(val, toStdMemoryOrder(mo));
228 }
229
231 T fetchAndXor(T val, MemoryOrder mo = MemoryOrder::AcqRel) {
232 return _value.fetch_xor(val, toStdMemoryOrder(mo));
233 }
234
240 bool compareExchangeStrong(T &expected, T desired, MemoryOrder success, MemoryOrder failure) {
241 return _value.compare_exchange_strong(expected, desired, toStdMemoryOrder(success),
242 toStdMemoryOrder(failure));
243 }
244
246 bool compareExchangeStrong(T &expected, T desired, MemoryOrder mo = MemoryOrder::SeqCst) {
247 return _value.compare_exchange_strong(expected, desired, toStdMemoryOrder(mo));
248 }
249
257 bool compareExchangeWeak(T &expected, T desired, MemoryOrder success, MemoryOrder failure) {
258 return _value.compare_exchange_weak(expected, desired, toStdMemoryOrder(success),
259 toStdMemoryOrder(failure));
260 }
261
263 bool compareExchangeWeak(T &expected, T desired, MemoryOrder mo = MemoryOrder::SeqCst) {
264 return _value.compare_exchange_weak(expected, desired, toStdMemoryOrder(mo));
265 }
266
267 // --------------------------------------------------------
268 // Convenience: implicit conversion to T (SeqCst load).
269 // --------------------------------------------------------
270
276 T operator++() { return _value.fetch_add(1, std::memory_order_acq_rel) + 1; }
277
283 T operator++(int) { return _value.fetch_add(1, std::memory_order_acq_rel); }
284
290 T operator--() { return _value.fetch_sub(1, std::memory_order_acq_rel) - 1; }
291
297 T operator--(int) { return _value.fetch_sub(1, std::memory_order_acq_rel); }
298
299 private:
300 std::atomic<T> _value;
301};
302
339template <typename T> class AtomicRef {
340 static_assert(std::is_trivially_copyable_v<T>,
341 "AtomicRef<T> requires T to be trivially copyable (std::atomic_ref requirement)");
342
343 public:
345 using ValueType = T;
346
354 explicit AtomicRef(T &obj) : _ref(obj) {}
355
356 AtomicRef(const AtomicRef &) = default;
357 AtomicRef &operator=(const AtomicRef &) = delete;
358 AtomicRef(AtomicRef &&) = default;
359 AtomicRef &operator=(AtomicRef &&) = delete;
360
362 T load(MemoryOrder mo = MemoryOrder::SeqCst) const {
363 return _ref.load(toStdMemoryOrder(mo));
364 }
365
367 void store(T val, MemoryOrder mo = MemoryOrder::SeqCst) const {
368 _ref.store(val, toStdMemoryOrder(mo));
369 }
370
372 T exchange(T desired, MemoryOrder mo = MemoryOrder::SeqCst) const {
373 return _ref.exchange(desired, toStdMemoryOrder(mo));
374 }
375
377 T fetchAndAdd(T val, MemoryOrder mo = MemoryOrder::AcqRel) const {
378 return _ref.fetch_add(val, toStdMemoryOrder(mo));
379 }
380
382 T fetchAndSub(T val, MemoryOrder mo = MemoryOrder::AcqRel) const {
383 return _ref.fetch_sub(val, toStdMemoryOrder(mo));
384 }
385
391 bool compareExchangeStrong(T &expected, T desired, MemoryOrder success, MemoryOrder failure) const {
392 return _ref.compare_exchange_strong(expected, desired, toStdMemoryOrder(success),
393 toStdMemoryOrder(failure));
394 }
395
397 bool compareExchangeStrong(T &expected, T desired, MemoryOrder mo = MemoryOrder::SeqCst) const {
398 return _ref.compare_exchange_strong(expected, desired, toStdMemoryOrder(mo));
399 }
400
406 bool compareExchangeWeak(T &expected, T desired, MemoryOrder success, MemoryOrder failure) const {
407 return _ref.compare_exchange_weak(expected, desired, toStdMemoryOrder(success),
408 toStdMemoryOrder(failure));
409 }
410
412 bool compareExchangeWeak(T &expected, T desired, MemoryOrder mo = MemoryOrder::SeqCst) const {
413 return _ref.compare_exchange_weak(expected, desired, toStdMemoryOrder(mo));
414 }
415
416 private:
417 std::atomic_ref<T> _ref;
418};
419
453class AtomicFlag {
454 public:
456 AtomicFlag() noexcept = default;
457
459 ~AtomicFlag() = default;
460
461 AtomicFlag(const AtomicFlag &) = delete;
462 AtomicFlag &operator=(const AtomicFlag &) = delete;
463 AtomicFlag(AtomicFlag &&) = delete;
464 AtomicFlag &operator=(AtomicFlag &&) = delete;
465
477 bool testAndSet(MemoryOrder mo = MemoryOrder::Acquire) noexcept {
478 return _flag.test_and_set(toStdMemoryOrder(mo));
479 }
480
487 void clear(MemoryOrder mo = MemoryOrder::Release) noexcept {
488 _flag.clear(toStdMemoryOrder(mo));
489 }
490
497 bool test(MemoryOrder mo = MemoryOrder::Acquire) const noexcept {
498 return _flag.test(toStdMemoryOrder(mo));
499 }
500
501 private:
502 std::atomic_flag _flag = ATOMIC_FLAG_INIT;
503};
504
505PROMEKI_NAMESPACE_END
506
507#endif // PROMEKI_ENABLE_CORE