// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_ATOMIC_UTILS_H_ #define V8_ATOMIC_UTILS_H_ #include #include "src/base/atomicops.h" #include "src/base/macros.h" namespace v8 { namespace base { template class AtomicNumber { public: AtomicNumber() : value_(0) {} explicit AtomicNumber(T initial) : value_(initial) {} // Returns the value after incrementing. V8_INLINE T Increment(T increment) { return static_cast(base::Barrier_AtomicIncrement( &value_, static_cast(increment))); } // Returns the value after decrementing. V8_INLINE T Decrement(T decrement) { return static_cast(base::Barrier_AtomicIncrement( &value_, -static_cast(decrement))); } V8_INLINE T Value() const { return static_cast(base::Acquire_Load(&value_)); } V8_INLINE void SetValue(T new_value) { base::Release_Store(&value_, static_cast(new_value)); } V8_INLINE T operator=(T value) { SetValue(value); return value; } V8_INLINE T operator+=(T value) { return Increment(value); } V8_INLINE T operator-=(T value) { return Decrement(value); } private: STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); base::AtomicWord value_; }; // This type uses no barrier accessors to change atomic word. Be careful with // data races. template class NoBarrierAtomicValue { public: NoBarrierAtomicValue() : value_(0) {} explicit NoBarrierAtomicValue(T initial) : value_(cast_helper::to_storage_type(initial)) {} static NoBarrierAtomicValue* FromAddress(void* address) { return reinterpret_cast*>(address); } V8_INLINE bool TrySetValue(T old_value, T new_value) { return base::NoBarrier_CompareAndSwap( &value_, cast_helper::to_storage_type(old_value), cast_helper::to_storage_type(new_value)) == cast_helper::to_storage_type(old_value); } V8_INLINE T Value() const { return cast_helper::to_return_type(base::NoBarrier_Load(&value_)); } V8_INLINE void SetValue(T new_value) { base::NoBarrier_Store(&value_, cast_helper::to_storage_type(new_value)); } private: STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); template struct cast_helper { static base::AtomicWord to_storage_type(S value) { return static_cast(value); } static S to_return_type(base::AtomicWord value) { return static_cast(value); } }; template struct cast_helper { static base::AtomicWord to_storage_type(S* value) { return reinterpret_cast(value); } static S* to_return_type(base::AtomicWord value) { return reinterpret_cast(value); } }; base::AtomicWord value_; }; // Flag using T atomically. Also accepts void* as T. template class AtomicValue { public: AtomicValue() : value_(0) {} explicit AtomicValue(T initial) : value_(cast_helper::to_storage_type(initial)) {} V8_INLINE T Value() const { return cast_helper::to_return_type(base::Acquire_Load(&value_)); } V8_INLINE bool TrySetValue(T old_value, T new_value) { return base::Release_CompareAndSwap( &value_, cast_helper::to_storage_type(old_value), cast_helper::to_storage_type(new_value)) == cast_helper::to_storage_type(old_value); } V8_INLINE void SetBits(T bits, T mask) { DCHECK_EQ(bits & ~mask, static_cast(0)); T old_value; T new_value; do { old_value = Value(); new_value = (old_value & ~mask) | bits; } while (!TrySetValue(old_value, new_value)); } V8_INLINE void SetBit(int bit) { SetBits(static_cast(1) << bit, static_cast(1) << bit); } V8_INLINE void ClearBit(int bit) { SetBits(0, 1 << bit); } V8_INLINE void SetValue(T new_value) { base::Release_Store(&value_, cast_helper::to_storage_type(new_value)); } private: STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord)); template struct cast_helper { static base::AtomicWord to_storage_type(S value) { return static_cast(value); } static S to_return_type(base::AtomicWord value) { return static_cast(value); } }; template struct cast_helper { static base::AtomicWord to_storage_type(S* value) { return reinterpret_cast(value); } static S* to_return_type(base::AtomicWord value) { return reinterpret_cast(value); } }; base::AtomicWord value_; }; // See utils.h for EnumSet. Storage is always base::AtomicWord. // Requirements on E: // - No explicit values. // - E::kLastValue defined to be the last actually used value. // // Example: // enum E { kA, kB, kC, kLastValue = kC }; template class AtomicEnumSet { public: explicit AtomicEnumSet(base::AtomicWord bits = 0) : bits_(bits) {} bool IsEmpty() const { return ToIntegral() == 0; } bool Contains(E element) const { return (ToIntegral() & Mask(element)) != 0; } bool ContainsAnyOf(const AtomicEnumSet& set) const { return (ToIntegral() & set.ToIntegral()) != 0; } void RemoveAll() { base::Release_Store(&bits_, 0); } bool operator==(const AtomicEnumSet& set) const { return ToIntegral() == set.ToIntegral(); } bool operator!=(const AtomicEnumSet& set) const { return ToIntegral() != set.ToIntegral(); } AtomicEnumSet operator|(const AtomicEnumSet& set) const { return AtomicEnumSet(ToIntegral() | set.ToIntegral()); } // The following operations modify the underlying storage. #define ATOMIC_SET_WRITE(OP, NEW_VAL) \ do { \ base::AtomicWord old; \ do { \ old = base::Acquire_Load(&bits_); \ } while (base::Release_CompareAndSwap(&bits_, old, old OP NEW_VAL) != \ old); \ } while (false) void Add(E element) { ATOMIC_SET_WRITE(|, Mask(element)); } void Add(const AtomicEnumSet& set) { ATOMIC_SET_WRITE(|, set.ToIntegral()); } void Remove(E element) { ATOMIC_SET_WRITE(&, ~Mask(element)); } void Remove(const AtomicEnumSet& set) { ATOMIC_SET_WRITE(&, ~set.ToIntegral()); } void Intersect(const AtomicEnumSet& set) { ATOMIC_SET_WRITE(&, set.ToIntegral()); } #undef ATOMIC_SET_OP private: // Check whether there's enough storage to hold E. STATIC_ASSERT(E::kLastValue < (sizeof(base::AtomicWord) * CHAR_BIT)); V8_INLINE base::AtomicWord ToIntegral() const { return base::Acquire_Load(&bits_); } V8_INLINE base::AtomicWord Mask(E element) const { return static_cast(1) << element; } base::AtomicWord bits_; }; } // namespace base } // namespace v8 #endif // #define V8_ATOMIC_UTILS_H_