1 // Copyright 2015 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_BASE_ATOMIC_UTILS_H_ 6 #define V8_BASE_ATOMIC_UTILS_H_ 7 8 #include <limits.h> 9 #include <type_traits> 10 11 #include "src/base/atomicops.h" 12 #include "src/base/macros.h" 13 14 namespace v8 { 15 namespace base { 16 17 // Deprecated. Use std::atomic<T> for new code. 18 // Flag using T atomically. Also accepts void* as T. 19 template <typename T> 20 class AtomicValue { 21 public: AtomicValue()22 AtomicValue() : value_(0) {} 23 AtomicValue(T initial)24 explicit AtomicValue(T initial) 25 : value_(cast_helper<T>::to_storage_type(initial)) {} 26 Value()27 V8_INLINE T Value() const { 28 return cast_helper<T>::to_return_type(base::Acquire_Load(&value_)); 29 } 30 SetValue(T new_value)31 V8_INLINE void SetValue(T new_value) { 32 base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value)); 33 } 34 35 private: 36 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 37 38 template <typename S> 39 struct cast_helper { to_storage_typecast_helper40 static base::AtomicWord to_storage_type(S value) { 41 return static_cast<base::AtomicWord>(value); 42 } to_return_typecast_helper43 static S to_return_type(base::AtomicWord value) { 44 return static_cast<S>(value); 45 } 46 }; 47 48 template <typename S> 49 struct cast_helper<S*> { 50 static base::AtomicWord to_storage_type(S* value) { 51 return reinterpret_cast<base::AtomicWord>(value); 52 } 53 static S* to_return_type(base::AtomicWord value) { 54 return reinterpret_cast<S*>(value); 55 } 56 }; 57 58 base::AtomicWord value_; 59 }; 60 61 // Provides atomic operations for a values stored at some address. 62 template <typename TAtomicStorageType> 63 class AsAtomicImpl { 64 public: 65 using AtomicStorageType = TAtomicStorageType; 66 67 template <typename T> 68 static T Acquire_Load(T* addr) { 69 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 70 return cast_helper<T>::to_return_type( 71 base::Acquire_Load(to_storage_addr(addr))); 72 } 73 74 template <typename T> 75 static T Relaxed_Load(T* addr) { 76 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 77 return cast_helper<T>::to_return_type( 78 base::Relaxed_Load(to_storage_addr(addr))); 79 } 80 81 template <typename T> 82 static void Release_Store(T* addr, 83 typename std::remove_reference<T>::type new_value) { 84 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 85 base::Release_Store(to_storage_addr(addr), 86 cast_helper<T>::to_storage_type(new_value)); 87 } 88 89 template <typename T> 90 static void Relaxed_Store(T* addr, 91 typename std::remove_reference<T>::type new_value) { 92 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 93 base::Relaxed_Store(to_storage_addr(addr), 94 cast_helper<T>::to_storage_type(new_value)); 95 } 96 97 template <typename T> 98 static T Release_CompareAndSwap( 99 T* addr, typename std::remove_reference<T>::type old_value, 100 typename std::remove_reference<T>::type new_value) { 101 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 102 return cast_helper<T>::to_return_type(base::Release_CompareAndSwap( 103 to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value), 104 cast_helper<T>::to_storage_type(new_value))); 105 } 106 107 template <typename T> 108 static T Relaxed_CompareAndSwap( 109 T* addr, typename std::remove_reference<T>::type old_value, 110 typename std::remove_reference<T>::type new_value) { 111 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 112 return cast_helper<T>::to_return_type(base::Relaxed_CompareAndSwap( 113 to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value), 114 cast_helper<T>::to_storage_type(new_value))); 115 } 116 117 template <typename T> 118 static T AcquireRelease_CompareAndSwap( 119 T* addr, typename std::remove_reference<T>::type old_value, 120 typename std::remove_reference<T>::type new_value) { 121 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 122 return cast_helper<T>::to_return_type(base::AcquireRelease_CompareAndSwap( 123 to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value), 124 cast_helper<T>::to_storage_type(new_value))); 125 } 126 127 // Atomically sets bits selected by the mask to the given value. 128 // Returns false if the bits are already set as needed. 129 template <typename T> 130 static bool SetBits(T* addr, T bits, T mask) { 131 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 132 DCHECK_EQ(bits & ~mask, static_cast<T>(0)); 133 T old_value = Relaxed_Load(addr); 134 T new_value, old_value_before_cas; 135 do { 136 if ((old_value & mask) == bits) return false; 137 new_value = (old_value & ~mask) | bits; 138 old_value_before_cas = old_value; 139 old_value = Release_CompareAndSwap(addr, old_value, new_value); 140 } while (old_value != old_value_before_cas); 141 return true; 142 } 143 144 private: 145 template <typename U> 146 struct cast_helper { 147 static AtomicStorageType to_storage_type(U value) { 148 return static_cast<AtomicStorageType>(value); 149 } 150 static U to_return_type(AtomicStorageType value) { 151 return static_cast<U>(value); 152 } 153 }; 154 155 template <typename U> 156 struct cast_helper<U*> { 157 static AtomicStorageType to_storage_type(U* value) { 158 return reinterpret_cast<AtomicStorageType>(value); 159 } 160 static U* to_return_type(AtomicStorageType value) { 161 return reinterpret_cast<U*>(value); 162 } 163 }; 164 165 template <typename T> 166 static AtomicStorageType* to_storage_addr(T* value) { 167 return reinterpret_cast<AtomicStorageType*>(value); 168 } 169 template <typename T> 170 static const AtomicStorageType* to_storage_addr(const T* value) { 171 return reinterpret_cast<const AtomicStorageType*>(value); 172 } 173 }; 174 175 using AsAtomic8 = AsAtomicImpl<base::Atomic8>; 176 using AsAtomic32 = AsAtomicImpl<base::Atomic32>; 177 using AsAtomicWord = AsAtomicImpl<base::AtomicWord>; 178 179 // This is similar to AsAtomicWord but it explicitly deletes functionality 180 // provided atomic access to bit representation of stored values. 181 template <typename TAtomicStorageType> 182 class AsAtomicPointerImpl : public AsAtomicImpl<TAtomicStorageType> { 183 public: 184 template <typename T> 185 static bool SetBits(T* addr, T bits, T mask) = delete; 186 }; 187 188 using AsAtomicPointer = AsAtomicPointerImpl<base::AtomicWord>; 189 190 template <typename T, 191 typename = typename std::enable_if<std::is_unsigned<T>::value>::type> 192 inline void CheckedIncrement(std::atomic<T>* number, T amount) { 193 const T old = number->fetch_add(amount); 194 DCHECK_GE(old + amount, old); 195 USE(old); 196 } 197 198 template <typename T, 199 typename = typename std::enable_if<std::is_unsigned<T>::value>::type> 200 inline void CheckedDecrement(std::atomic<T>* number, T amount) { 201 const T old = number->fetch_sub(amount); 202 DCHECK_GE(old, amount); 203 USE(old); 204 } 205 206 template <typename T> 207 V8_INLINE std::atomic<T>* AsAtomicPtr(T* t) { 208 return reinterpret_cast<std::atomic<T>*>(t); 209 } 210 211 template <typename T> 212 V8_INLINE const std::atomic<T>* AsAtomicPtr(const T* t) { 213 return reinterpret_cast<const std::atomic<T>*>(t); 214 } 215 216 } // namespace base 217 } // namespace v8 218 219 #endif // V8_BASE_ATOMIC_UTILS_H_ 220