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_ATOMIC_UTILS_H_ 6 #define V8_ATOMIC_UTILS_H_ 7 8 #include <limits.h> 9 10 #include "src/base/atomicops.h" 11 #include "src/base/macros.h" 12 13 namespace v8 { 14 namespace base { 15 16 template <class T> 17 class AtomicNumber { 18 public: 19 AtomicNumber() : value_(0) {} 20 explicit AtomicNumber(T initial) : value_(initial) {} 21 22 // Returns the value after incrementing. 23 V8_INLINE T Increment(T increment) { 24 return static_cast<T>(base::Barrier_AtomicIncrement( 25 &value_, static_cast<base::AtomicWord>(increment))); 26 } 27 28 // Returns the value after decrementing. 29 V8_INLINE T Decrement(T decrement) { 30 return static_cast<T>(base::Barrier_AtomicIncrement( 31 &value_, -static_cast<base::AtomicWord>(decrement))); 32 } 33 34 V8_INLINE T Value() { return static_cast<T>(base::Acquire_Load(&value_)); } 35 36 V8_INLINE void SetValue(T new_value) { 37 base::Release_Store(&value_, static_cast<base::AtomicWord>(new_value)); 38 } 39 40 V8_INLINE T operator=(T value) { 41 SetValue(value); 42 return value; 43 } 44 45 V8_INLINE T operator+=(T value) { return Increment(value); } 46 V8_INLINE T operator-=(T value) { return Decrement(value); } 47 48 private: 49 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 50 51 base::AtomicWord value_; 52 }; 53 54 // This type uses no barrier accessors to change atomic word. Be careful with 55 // data races. 56 template <typename T> 57 class NoBarrierAtomicValue { 58 public: 59 NoBarrierAtomicValue() : value_(0) {} 60 61 explicit NoBarrierAtomicValue(T initial) 62 : value_(cast_helper<T>::to_storage_type(initial)) {} 63 64 static NoBarrierAtomicValue* FromAddress(void* address) { 65 return reinterpret_cast<base::NoBarrierAtomicValue<T>*>(address); 66 } 67 68 V8_INLINE bool TrySetValue(T old_value, T new_value) { 69 return base::NoBarrier_CompareAndSwap( 70 &value_, cast_helper<T>::to_storage_type(old_value), 71 cast_helper<T>::to_storage_type(new_value)) == 72 cast_helper<T>::to_storage_type(old_value); 73 } 74 75 V8_INLINE T Value() const { 76 return cast_helper<T>::to_return_type(base::NoBarrier_Load(&value_)); 77 } 78 79 V8_INLINE void SetValue(T new_value) { 80 base::NoBarrier_Store(&value_, cast_helper<T>::to_storage_type(new_value)); 81 } 82 83 private: 84 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 85 86 template <typename S> 87 struct cast_helper { 88 static base::AtomicWord to_storage_type(S value) { 89 return static_cast<base::AtomicWord>(value); 90 } 91 static S to_return_type(base::AtomicWord value) { 92 return static_cast<S>(value); 93 } 94 }; 95 96 template <typename S> 97 struct cast_helper<S*> { 98 static base::AtomicWord to_storage_type(S* value) { 99 return reinterpret_cast<base::AtomicWord>(value); 100 } 101 static S* to_return_type(base::AtomicWord value) { 102 return reinterpret_cast<S*>(value); 103 } 104 }; 105 106 base::AtomicWord value_; 107 }; 108 109 // Flag using T atomically. Also accepts void* as T. 110 template <typename T> 111 class AtomicValue { 112 public: 113 AtomicValue() : value_(0) {} 114 115 explicit AtomicValue(T initial) 116 : value_(cast_helper<T>::to_storage_type(initial)) {} 117 118 V8_INLINE T Value() const { 119 return cast_helper<T>::to_return_type(base::Acquire_Load(&value_)); 120 } 121 122 V8_INLINE bool TrySetValue(T old_value, T new_value) { 123 return base::Release_CompareAndSwap( 124 &value_, cast_helper<T>::to_storage_type(old_value), 125 cast_helper<T>::to_storage_type(new_value)) == 126 cast_helper<T>::to_storage_type(old_value); 127 } 128 129 V8_INLINE void SetBits(T bits, T mask) { 130 DCHECK_EQ(bits & ~mask, static_cast<T>(0)); 131 T old_value; 132 T new_value; 133 do { 134 old_value = Value(); 135 new_value = (old_value & ~mask) | bits; 136 } while (!TrySetValue(old_value, new_value)); 137 } 138 139 V8_INLINE void SetBit(int bit) { 140 SetBits(static_cast<T>(1) << bit, static_cast<T>(1) << bit); 141 } 142 143 V8_INLINE void ClearBit(int bit) { SetBits(0, 1 << bit); } 144 145 V8_INLINE void SetValue(T new_value) { 146 base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value)); 147 } 148 149 private: 150 STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); 151 152 template <typename S> 153 struct cast_helper { 154 static base::AtomicWord to_storage_type(S value) { 155 return static_cast<base::AtomicWord>(value); 156 } 157 static S to_return_type(base::AtomicWord value) { 158 return static_cast<S>(value); 159 } 160 }; 161 162 template <typename S> 163 struct cast_helper<S*> { 164 static base::AtomicWord to_storage_type(S* value) { 165 return reinterpret_cast<base::AtomicWord>(value); 166 } 167 static S* to_return_type(base::AtomicWord value) { 168 return reinterpret_cast<S*>(value); 169 } 170 }; 171 172 base::AtomicWord value_; 173 }; 174 175 176 // See utils.h for EnumSet. Storage is always base::AtomicWord. 177 // Requirements on E: 178 // - No explicit values. 179 // - E::kLastValue defined to be the last actually used value. 180 // 181 // Example: 182 // enum E { kA, kB, kC, kLastValue = kC }; 183 template <class E> 184 class AtomicEnumSet { 185 public: 186 explicit AtomicEnumSet(base::AtomicWord bits = 0) : bits_(bits) {} 187 188 bool IsEmpty() const { return ToIntegral() == 0; } 189 190 bool Contains(E element) const { return (ToIntegral() & Mask(element)) != 0; } 191 bool ContainsAnyOf(const AtomicEnumSet& set) const { 192 return (ToIntegral() & set.ToIntegral()) != 0; 193 } 194 195 void RemoveAll() { base::Release_Store(&bits_, 0); } 196 197 bool operator==(const AtomicEnumSet& set) const { 198 return ToIntegral() == set.ToIntegral(); 199 } 200 201 bool operator!=(const AtomicEnumSet& set) const { 202 return ToIntegral() != set.ToIntegral(); 203 } 204 205 AtomicEnumSet<E> operator|(const AtomicEnumSet& set) const { 206 return AtomicEnumSet<E>(ToIntegral() | set.ToIntegral()); 207 } 208 209 // The following operations modify the underlying storage. 210 211 #define ATOMIC_SET_WRITE(OP, NEW_VAL) \ 212 do { \ 213 base::AtomicWord old; \ 214 do { \ 215 old = base::Acquire_Load(&bits_); \ 216 } while (base::Release_CompareAndSwap(&bits_, old, old OP NEW_VAL) != \ 217 old); \ 218 } while (false) 219 220 void Add(E element) { ATOMIC_SET_WRITE(|, Mask(element)); } 221 222 void Add(const AtomicEnumSet& set) { ATOMIC_SET_WRITE(|, set.ToIntegral()); } 223 224 void Remove(E element) { ATOMIC_SET_WRITE(&, ~Mask(element)); } 225 226 void Remove(const AtomicEnumSet& set) { 227 ATOMIC_SET_WRITE(&, ~set.ToIntegral()); 228 } 229 230 void Intersect(const AtomicEnumSet& set) { 231 ATOMIC_SET_WRITE(&, set.ToIntegral()); 232 } 233 234 #undef ATOMIC_SET_OP 235 236 private: 237 // Check whether there's enough storage to hold E. 238 STATIC_ASSERT(E::kLastValue < (sizeof(base::AtomicWord) * CHAR_BIT)); 239 240 V8_INLINE base::AtomicWord ToIntegral() const { 241 return base::Acquire_Load(&bits_); 242 } 243 244 V8_INLINE base::AtomicWord Mask(E element) const { 245 return static_cast<base::AtomicWord>(1) << element; 246 } 247 248 base::AtomicWord bits_; 249 }; 250 251 } // namespace base 252 } // namespace v8 253 254 #endif // #define V8_ATOMIC_UTILS_H_ 255