1 //===-- A simple equivalent of std::atomic ----------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_LIBC_SRC_SUPPORT_CPP_ATOMIC_H
10 #define LLVM_LIBC_SRC_SUPPORT_CPP_ATOMIC_H
11 
12 #include "TypeTraits.h"
13 
14 namespace __llvm_libc {
15 namespace cpp {
16 
17 enum class MemoryOrder : int {
18   RELAXED = __ATOMIC_RELAXED,
19   CONSUME = __ATOMIC_CONSUME,
20   ACQUIRE = __ATOMIC_ACQUIRE,
21   RELEASE = __ATOMIC_RELEASE,
22   ACQ_REL = __ATOMIC_ACQ_REL,
23   SEQ_CST = __ATOMIC_SEQ_CST
24 };
25 
26 template <typename T> struct Atomic {
27   // For now, we will restrict to only arithmetic types.
28   static_assert(IsArithmetic<T>::Value, "Only arithmetic types can be atomic.");
29 
30 private:
31   // The value stored should be appropriately aligned so that
32   // hardware instructions used to perform atomic operations work
33   // correctly.
34   static constexpr int ALIGNMENT = sizeof(T) > alignof(T) ? sizeof(T)
35                                                           : alignof(T);
36 
37 public:
38   using value_type = T;
39 
40   // We keep the internal value public so that it can be addressable.
41   // This is useful in places like the Linux futex operations where
42   // we need pointers to the memory of the atomic values. Load and store
43   // operations should be performed using the atomic methods however.
44   alignas(ALIGNMENT) value_type val;
45 
46   constexpr Atomic() = default;
47 
48   // Intializes the value without using atomic operations.
AtomicAtomic49   constexpr Atomic(value_type v) : val(v) {}
50 
51   Atomic(const Atomic &) = delete;
52   Atomic &operator=(const Atomic &) = delete;
53 
54   // Atomic load
TAtomic55   operator T() { return __atomic_load_n(&val, int(MemoryOrder::SEQ_CST)); }
56 
57   T load(MemoryOrder mem_ord = MemoryOrder::SEQ_CST) {
58     return __atomic_load_n(&val, int(mem_ord));
59   }
60 
61   // Atomic store
62   T operator=(T rhs) {
63     __atomic_store_n(&val, rhs, int(MemoryOrder::SEQ_CST));
64     return rhs;
65   }
66 
67   void store(T rhs, MemoryOrder mem_ord = MemoryOrder::SEQ_CST) {
68     __atomic_store_n(&val, rhs, int(mem_ord));
69   }
70 
71   // Atomic compare exchange
72   bool compare_exchange_strong(T &expected, T desired,
73                                MemoryOrder mem_ord = MemoryOrder::SEQ_CST) {
74     return __atomic_compare_exchange_n(&val, &expected, desired, false,
75                                        int(mem_ord), int(mem_ord));
76   }
77 
78   T exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST) {
79     return __atomic_exchange_n(&val, desired, int(mem_ord));
80   }
81 
82   T fetch_add(T increment, MemoryOrder mem_ord = MemoryOrder::SEQ_CST) {
83     return __atomic_fetch_add(&val, increment, int(mem_ord));
84   }
85 
86   T fetch_sub(T decrement, MemoryOrder mem_ord = MemoryOrder::SEQ_CST) {
87     return __atomic_fetch_sub(&val, decrement, int(mem_ord));
88   }
89 
90   // Set the value without using an atomic operation. This is useful
91   // in initializing atomic values without a constructor.
setAtomic92   void set(T rhs) { val = rhs; }
93 };
94 
95 } // namespace cpp
96 } // namespace __llvm_libc
97 
98 #endif // LLVM_LIBC_SRC_SUPPORT_CPP_ATOMIC_H
99