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