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 TrySetValue(T old_value,T new_value)31 V8_INLINE bool TrySetValue(T old_value, T new_value) { 32 return base::Release_CompareAndSwap( 33 &value_, cast_helper<T>::to_storage_type(old_value), 34 cast_helper<T>::to_storage_type(new_value)) == 35 cast_helper<T>::to_storage_type(old_value); 36 } 37 SetBits(T bits,T mask)38 V8_INLINE void SetBits(T bits, T mask) { 39 DCHECK_EQ(bits & ~mask, static_cast<T>(0)); 40 T old_value; 41 T new_value; 42 do { 43 old_value = Value(); 44 new_value = (old_value & ~mask) | bits; 45 } while (!TrySetValue(old_value, new_value)); 46 } 47 SetBit(int bit)48 V8_INLINE void SetBit(int bit) { 49 SetBits(static_cast<T>(1) << bit, static_cast<T>(1) << bit); 50 } 51 ClearBit(int bit)52 V8_INLINE void ClearBit(int bit) { SetBits(0, 1 << bit); } 53 SetValue(T new_value)54 V8_INLINE void SetValue(T new_value) { 55 base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value)); 56 } 57 58 private: 59 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 60 61 template <typename S> 62 struct cast_helper { to_storage_typecast_helper63 static base::AtomicWord to_storage_type(S value) { 64 return static_cast<base::AtomicWord>(value); 65 } to_return_typecast_helper66 static S to_return_type(base::AtomicWord value) { 67 return static_cast<S>(value); 68 } 69 }; 70 71 template <typename S> 72 struct cast_helper<S*> { 73 static base::AtomicWord to_storage_type(S* value) { 74 return reinterpret_cast<base::AtomicWord>(value); 75 } 76 static S* to_return_type(base::AtomicWord value) { 77 return reinterpret_cast<S*>(value); 78 } 79 }; 80 81 base::AtomicWord value_; 82 }; 83 84 class AsAtomic32 { 85 public: 86 template <typename T> 87 static T Acquire_Load(T* addr) { 88 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32)); 89 return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr))); 90 } 91 92 template <typename T> 93 static T Relaxed_Load(T* addr) { 94 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32)); 95 return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr))); 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(base::Atomic32)); 102 base::Release_Store(to_storage_addr(addr), to_storage_type(new_value)); 103 } 104 105 template <typename T> 106 static void Relaxed_Store(T* addr, 107 typename std::remove_reference<T>::type new_value) { 108 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32)); 109 base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value)); 110 } 111 112 template <typename T> 113 static T Release_CompareAndSwap( 114 T* addr, typename std::remove_reference<T>::type old_value, 115 typename std::remove_reference<T>::type new_value) { 116 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32)); 117 return to_return_type<T>(base::Release_CompareAndSwap( 118 to_storage_addr(addr), to_storage_type(old_value), 119 to_storage_type(new_value))); 120 } 121 122 // Atomically sets bits selected by the mask to the given value. 123 // Returns false if the bits are already set as needed. 124 template <typename T> 125 static bool SetBits(T* addr, T bits, T mask) { 126 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32)); 127 DCHECK_EQ(bits & ~mask, static_cast<T>(0)); 128 T old_value; 129 T new_value; 130 do { 131 old_value = Relaxed_Load(addr); 132 if ((old_value & mask) == bits) return false; 133 new_value = (old_value & ~mask) | bits; 134 } while (Release_CompareAndSwap(addr, old_value, new_value) != old_value); 135 return true; 136 } 137 138 private: 139 template <typename T> 140 static base::Atomic32 to_storage_type(T value) { 141 return static_cast<base::Atomic32>(value); 142 } 143 template <typename T> 144 static T to_return_type(base::Atomic32 value) { 145 return static_cast<T>(value); 146 } 147 template <typename T> 148 static base::Atomic32* to_storage_addr(T* value) { 149 return reinterpret_cast<base::Atomic32*>(value); 150 } 151 template <typename T> 152 static const base::Atomic32* to_storage_addr(const T* value) { 153 return reinterpret_cast<const base::Atomic32*>(value); 154 } 155 }; 156 157 class AsAtomicWord { 158 public: 159 template <typename T> 160 static T Acquire_Load(T* addr) { 161 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 162 return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr))); 163 } 164 165 template <typename T> 166 static T Relaxed_Load(T* addr) { 167 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 168 return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr))); 169 } 170 171 template <typename T> 172 static void Release_Store(T* addr, 173 typename std::remove_reference<T>::type new_value) { 174 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 175 base::Release_Store(to_storage_addr(addr), to_storage_type(new_value)); 176 } 177 178 template <typename T> 179 static void Relaxed_Store(T* addr, 180 typename std::remove_reference<T>::type new_value) { 181 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 182 base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value)); 183 } 184 185 template <typename T> 186 static T Release_CompareAndSwap( 187 T* addr, typename std::remove_reference<T>::type old_value, 188 typename std::remove_reference<T>::type new_value) { 189 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 190 return to_return_type<T>(base::Release_CompareAndSwap( 191 to_storage_addr(addr), to_storage_type(old_value), 192 to_storage_type(new_value))); 193 } 194 195 // Atomically sets bits selected by the mask to the given value. 196 // Returns false if the bits are already set as needed. 197 template <typename T> 198 static bool SetBits(T* addr, T bits, T mask) { 199 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 200 DCHECK_EQ(bits & ~mask, static_cast<T>(0)); 201 T old_value; 202 T new_value; 203 do { 204 old_value = Relaxed_Load(addr); 205 if ((old_value & mask) == bits) return false; 206 new_value = (old_value & ~mask) | bits; 207 } while (Release_CompareAndSwap(addr, old_value, new_value) != old_value); 208 return true; 209 } 210 211 private: 212 template <typename T> 213 static base::AtomicWord to_storage_type(T value) { 214 return static_cast<base::AtomicWord>(value); 215 } 216 template <typename T> 217 static T to_return_type(base::AtomicWord value) { 218 return static_cast<T>(value); 219 } 220 template <typename T> 221 static base::AtomicWord* to_storage_addr(T* value) { 222 return reinterpret_cast<base::AtomicWord*>(value); 223 } 224 template <typename T> 225 static const base::AtomicWord* to_storage_addr(const T* value) { 226 return reinterpret_cast<const base::AtomicWord*>(value); 227 } 228 }; 229 230 class AsAtomic8 { 231 public: 232 template <typename T> 233 static T Acquire_Load(T* addr) { 234 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8)); 235 return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr))); 236 } 237 238 template <typename T> 239 static T Relaxed_Load(T* addr) { 240 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8)); 241 return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr))); 242 } 243 244 template <typename T> 245 static void Release_Store(T* addr, 246 typename std::remove_reference<T>::type new_value) { 247 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8)); 248 base::Release_Store(to_storage_addr(addr), to_storage_type(new_value)); 249 } 250 251 template <typename T> 252 static void Relaxed_Store(T* addr, 253 typename std::remove_reference<T>::type new_value) { 254 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8)); 255 base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value)); 256 } 257 258 template <typename T> 259 static T Release_CompareAndSwap( 260 T* addr, typename std::remove_reference<T>::type old_value, 261 typename std::remove_reference<T>::type new_value) { 262 STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8)); 263 return to_return_type<T>(base::Release_CompareAndSwap( 264 to_storage_addr(addr), to_storage_type(old_value), 265 to_storage_type(new_value))); 266 } 267 268 private: 269 template <typename T> 270 static base::Atomic8 to_storage_type(T value) { 271 return static_cast<base::Atomic8>(value); 272 } 273 template <typename T> 274 static T to_return_type(base::Atomic8 value) { 275 return static_cast<T>(value); 276 } 277 template <typename T> 278 static base::Atomic8* to_storage_addr(T* value) { 279 return reinterpret_cast<base::Atomic8*>(value); 280 } 281 template <typename T> 282 static const base::Atomic8* to_storage_addr(const T* value) { 283 return reinterpret_cast<const base::Atomic8*>(value); 284 } 285 }; 286 287 class AsAtomicPointer { 288 public: 289 template <typename T> 290 static T Acquire_Load(T* addr) { 291 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 292 return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr))); 293 } 294 295 template <typename T> 296 static T Relaxed_Load(T* addr) { 297 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 298 return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr))); 299 } 300 301 template <typename T> 302 static void Release_Store(T* addr, 303 typename std::remove_reference<T>::type new_value) { 304 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 305 base::Release_Store(to_storage_addr(addr), to_storage_type(new_value)); 306 } 307 308 template <typename T> 309 static void Relaxed_Store(T* addr, 310 typename std::remove_reference<T>::type new_value) { 311 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 312 base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value)); 313 } 314 315 template <typename T> 316 static T Release_CompareAndSwap( 317 T* addr, typename std::remove_reference<T>::type old_value, 318 typename std::remove_reference<T>::type new_value) { 319 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 320 return to_return_type<T>(base::Release_CompareAndSwap( 321 to_storage_addr(addr), to_storage_type(old_value), 322 to_storage_type(new_value))); 323 } 324 325 private: 326 template <typename T> 327 static base::AtomicWord to_storage_type(T value) { 328 return reinterpret_cast<base::AtomicWord>(value); 329 } 330 template <typename T> 331 static T to_return_type(base::AtomicWord value) { 332 return reinterpret_cast<T>(value); 333 } 334 template <typename T> 335 static base::AtomicWord* to_storage_addr(T* value) { 336 return reinterpret_cast<base::AtomicWord*>(value); 337 } 338 template <typename T> 339 static const base::AtomicWord* to_storage_addr(const T* value) { 340 return reinterpret_cast<const base::AtomicWord*>(value); 341 } 342 }; 343 344 // This class is intended to be used as a wrapper for elements of an array 345 // that is passed in to STL functions such as std::sort. It ensures that 346 // elements accesses are atomic. 347 // Usage example: 348 // Object** given_array; 349 // AtomicElement<Object*>* wrapped = 350 // reinterpret_cast<AtomicElement<Object*>(given_array); 351 // std::sort(wrapped, wrapped + given_length, cmp); 352 // where the cmp function uses the value() accessor to compare the elements. 353 template <typename T> 354 class AtomicElement { 355 public: 356 AtomicElement(const AtomicElement<T>& other) { 357 AsAtomicPointer::Relaxed_Store( 358 &value_, AsAtomicPointer::Relaxed_Load(&other.value_)); 359 } 360 361 void operator=(const AtomicElement<T>& other) { 362 AsAtomicPointer::Relaxed_Store( 363 &value_, AsAtomicPointer::Relaxed_Load(&other.value_)); 364 } 365 366 T value() const { return AsAtomicPointer::Relaxed_Load(&value_); } 367 368 bool operator<(const AtomicElement<T>& other) const { 369 return value() < other.value(); 370 } 371 372 bool operator==(const AtomicElement<T>& other) const { 373 return value() == other.value(); 374 } 375 376 private: 377 T value_; 378 }; 379 380 } // namespace base 381 } // namespace v8 382 383 #endif // V8_BASE_ATOMIC_UTILS_H_ 384