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() const {
     35     return static_cast<T>(base::Acquire_Load(&value_));
     36   }
     37 
     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:
     61   NoBarrierAtomicValue() : value_(0) {}
     62 
     63   explicit NoBarrierAtomicValue(T initial)
     64       : value_(cast_helper<T>::to_storage_type(initial)) {}
     65 
     66   static NoBarrierAtomicValue* FromAddress(void* address) {
     67     return reinterpret_cast<base::NoBarrierAtomicValue<T>*>(address);
     68   }
     69 
     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 
     77   V8_INLINE T Value() const {
     78     return cast_helper<T>::to_return_type(base::NoBarrier_Load(&value_));
     79   }
     80 
     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 {
     90     static base::AtomicWord to_storage_type(S value) {
     91       return static_cast<base::AtomicWord>(value);
     92     }
     93     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