Home | History | Annotate | Download | only in base
      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