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 10 #include <atomic> 11 #include <type_traits> 12 13 #include "src/base/atomicops.h" 14 #include "src/base/macros.h" 15 16 namespace v8 { 17 namespace base { 18 19 // Deprecated. Use std::atomic<T> for new code. 20 // Flag using T atomically. Also accepts void* as T. 21 template <typename T> 22 class AtomicValue { 23 public: AtomicValue()24 AtomicValue() : value_(0) {} 25 AtomicValue(T initial)26 explicit AtomicValue(T initial) 27 : value_(cast_helper<T>::to_storage_type(initial)) {} 28 Value()29 V8_INLINE T Value() const { 30 return cast_helper<T>::to_return_type(base::Acquire_Load(&value_)); 31 } 32 SetValue(T new_value)33 V8_INLINE void SetValue(T new_value) { 34 base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value)); 35 } 36 37 private: 38 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 39 40 template <typename S> 41 struct cast_helper { to_storage_typecast_helper42 static base::AtomicWord to_storage_type(S value) { 43 return static_cast<base::AtomicWord>(value); 44 } to_return_typecast_helper45 static S to_return_type(base::AtomicWord value) { 46 return static_cast<S>(value); 47 } 48 }; 49 50 template <typename S> 51 struct cast_helper<S*> { 52 static base::AtomicWord to_storage_type(S* value) { 53 return reinterpret_cast<base::AtomicWord>(value); 54 } 55 static S* to_return_type(base::AtomicWord value) { 56 return reinterpret_cast<S*>(value); 57 } 58 }; 59 60 base::AtomicWord value_; 61 }; 62 63 // Provides atomic operations for a values stored at some address. 64 template <typename TAtomicStorageType> 65 class AsAtomicImpl { 66 public: 67 using AtomicStorageType = TAtomicStorageType; 68 69 template <typename T> 70 static T SeqCst_Load(T* addr) { 71 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 72 return cast_helper<T>::to_return_type( 73 base::SeqCst_Load(to_storage_addr(addr))); 74 } 75 76 template <typename T> 77 static T Acquire_Load(T* addr) { 78 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 79 return cast_helper<T>::to_return_type( 80 base::Acquire_Load(to_storage_addr(addr))); 81 } 82 83 template <typename T> 84 static T Relaxed_Load(T* addr) { 85 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 86 return cast_helper<T>::to_return_type( 87 base::Relaxed_Load(to_storage_addr(addr))); 88 } 89 90 template <typename T> 91 static void SeqCst_Store(T* addr, 92 typename std::remove_reference<T>::type new_value) { 93 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 94 base::SeqCst_Store(to_storage_addr(addr), 95 cast_helper<T>::to_storage_type(new_value)); 96 } 97 98 template <typename T> 99 static void Release_Store(T* addr, 100 typename std::remove_reference<T>::type new_value) { 101 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 102 base::Release_Store(to_storage_addr(addr), 103 cast_helper<T>::to_storage_type(new_value)); 104 } 105 106 template <typename T> 107 static void Relaxed_Store(T* addr, 108 typename std::remove_reference<T>::type new_value) { 109 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 110 base::Relaxed_Store(to_storage_addr(addr), 111 cast_helper<T>::to_storage_type(new_value)); 112 } 113 114 template <typename T> 115 static T SeqCst_Swap(T* addr, 116 typename std::remove_reference<T>::type new_value) { 117 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 118 return base::SeqCst_AtomicExchange( 119 to_storage_addr(addr), cast_helper<T>::to_storage_type(new_value)); 120 } 121 122 template <typename T> 123 static T Release_CompareAndSwap( 124 T* addr, typename std::remove_reference<T>::type old_value, 125 typename std::remove_reference<T>::type new_value) { 126 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 127 return cast_helper<T>::to_return_type(base::Release_CompareAndSwap( 128 to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value), 129 cast_helper<T>::to_storage_type(new_value))); 130 } 131 132 template <typename T> 133 static T Relaxed_CompareAndSwap( 134 T* addr, typename std::remove_reference<T>::type old_value, 135 typename std::remove_reference<T>::type new_value) { 136 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 137 return cast_helper<T>::to_return_type(base::Relaxed_CompareAndSwap( 138 to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value), 139 cast_helper<T>::to_storage_type(new_value))); 140 } 141 142 template <typename T> 143 static T AcquireRelease_CompareAndSwap( 144 T* addr, typename std::remove_reference<T>::type old_value, 145 typename std::remove_reference<T>::type new_value) { 146 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 147 return cast_helper<T>::to_return_type(base::AcquireRelease_CompareAndSwap( 148 to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value), 149 cast_helper<T>::to_storage_type(new_value))); 150 } 151 152 // Atomically sets bits selected by the mask to the given value. 153 // Returns false if the bits are already set as needed. 154 template <typename T> 155 static bool SetBits(T* addr, T bits, T mask) { 156 STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType)); 157 DCHECK_EQ(bits & ~mask, static_cast<T>(0)); 158 T old_value = Relaxed_Load(addr); 159 T new_value, old_value_before_cas; 160 do { 161 if ((old_value & mask) == bits) return false; 162 new_value = (old_value & ~mask) | bits; 163 old_value_before_cas = old_value; 164 old_value = Release_CompareAndSwap(addr, old_value, new_value); 165 } while (old_value != old_value_before_cas); 166 return true; 167 } 168 169 private: 170 template <typename U> 171 struct cast_helper { 172 static AtomicStorageType to_storage_type(U value) { 173 return static_cast<AtomicStorageType>(value); 174 } 175 static U to_return_type(AtomicStorageType value) { 176 return static_cast<U>(value); 177 } 178 }; 179 180 template <typename U> 181 struct cast_helper<U*> { 182 static AtomicStorageType to_storage_type(U* value) { 183 return reinterpret_cast<AtomicStorageType>(value); 184 } 185 static U* to_return_type(AtomicStorageType value) { 186 return reinterpret_cast<U*>(value); 187 } 188 }; 189 190 template <typename T> 191 static AtomicStorageType* to_storage_addr(T* value) { 192 return reinterpret_cast<AtomicStorageType*>(value); 193 } 194 template <typename T> 195 static const AtomicStorageType* to_storage_addr(const T* value) { 196 return reinterpret_cast<const AtomicStorageType*>(value); 197 } 198 }; 199 200 using AsAtomic8 = AsAtomicImpl<base::Atomic8>; 201 using AsAtomic16 = AsAtomicImpl<base::Atomic16>; 202 using AsAtomic32 = AsAtomicImpl<base::Atomic32>; 203 using AsAtomicWord = AsAtomicImpl<base::AtomicWord>; 204 205 template <int Width> 206 struct AtomicTypeFromByteWidth {}; 207 template <> 208 struct AtomicTypeFromByteWidth<1> { 209 using type = base::Atomic8; 210 }; 211 template <> 212 struct AtomicTypeFromByteWidth<2> { 213 using type = base::Atomic16; 214 }; 215 template <> 216 struct AtomicTypeFromByteWidth<4> { 217 using type = base::Atomic32; 218 }; 219 #if V8_HOST_ARCH_64_BIT 220 template <> 221 struct AtomicTypeFromByteWidth<8> { 222 using type = base::Atomic64; 223 }; 224 #endif 225 226 // This is similar to AsAtomicWord but it explicitly deletes functionality 227 // provided atomic access to bit representation of stored values. 228 template <typename TAtomicStorageType> 229 class AsAtomicPointerImpl : public AsAtomicImpl<TAtomicStorageType> { 230 public: 231 template <typename T> 232 static bool SetBits(T* addr, T bits, T mask) = delete; 233 }; 234 235 using AsAtomicPointer = AsAtomicPointerImpl<base::AtomicWord>; 236 237 template <typename T, 238 typename = typename std::enable_if<std::is_unsigned<T>::value>::type> 239 inline void CheckedIncrement( 240 std::atomic<T>* number, T amount, 241 std::memory_order order = std::memory_order_seq_cst) { 242 const T old = number->fetch_add(amount, order); 243 DCHECK_GE(old + amount, old); 244 USE(old); 245 } 246 247 template <typename T, 248 typename = typename std::enable_if<std::is_unsigned<T>::value>::type> 249 inline void CheckedDecrement( 250 std::atomic<T>* number, T amount, 251 std::memory_order order = std::memory_order_seq_cst) { 252 const T old = number->fetch_sub(amount, order); 253 DCHECK_GE(old, amount); 254 USE(old); 255 } 256 257 template <typename T> 258 V8_INLINE std::atomic<T>* AsAtomicPtr(T* t) { 259 STATIC_ASSERT(sizeof(T) == sizeof(std::atomic<T>)); 260 STATIC_ASSERT(alignof(T) >= alignof(std::atomic<T>)); 261 return reinterpret_cast<std::atomic<T>*>(t); 262 } 263 264 template <typename T> 265 V8_INLINE const std::atomic<T>* AsAtomicPtr(const T* t) { 266 STATIC_ASSERT(sizeof(T) == sizeof(std::atomic<T>)); 267 STATIC_ASSERT(alignof(T) >= alignof(std::atomic<T>)); 268 return reinterpret_cast<const std::atomic<T>*>(t); 269 } 270 271 } // namespace base 272 } // namespace v8 273 274 #endif // V8_BASE_ATOMIC_UTILS_H_ 275