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