atomic-utils.h 5.68 KB
Newer Older
1 2 3 4
// 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.

5 6
#ifndef V8_BASE_ATOMIC_UTILS_H_
#define V8_BASE_ATOMIC_UTILS_H_
7 8

#include <limits.h>
9
#include <type_traits>
10 11 12 13 14

#include "src/base/atomicops.h"
#include "src/base/macros.h"

namespace v8 {
lpy's avatar
lpy committed
15
namespace base {
16

17
// Deprecated. Use std::atomic<T> for new code.
18 19
// Flag using T atomically. Also accepts void* as T.
template <typename T>
20 21
class AtomicValue {
 public:
22 23
  AtomicValue() : value_(0) {}

24 25
  explicit AtomicValue(T initial)
      : value_(cast_helper<T>::to_storage_type(initial)) {}
26

27
  V8_INLINE T Value() const {
28
    return cast_helper<T>::to_return_type(base::Acquire_Load(&value_));
29 30
  }

31 32
  V8_INLINE void SetValue(T new_value) {
    base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value));
33 34 35
  }

 private:
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
  STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));

  template <typename S>
  struct cast_helper {
    static base::AtomicWord to_storage_type(S value) {
      return static_cast<base::AtomicWord>(value);
    }
    static S to_return_type(base::AtomicWord value) {
      return static_cast<S>(value);
    }
  };

  template <typename S>
  struct cast_helper<S*> {
    static base::AtomicWord to_storage_type(S* value) {
      return reinterpret_cast<base::AtomicWord>(value);
    }
    static S* to_return_type(base::AtomicWord value) {
      return reinterpret_cast<S*>(value);
    }
  };

58 59 60
  base::AtomicWord value_;
};

61 62 63
// Provides atomic operations for a values stored at some address.
template <typename TAtomicStorageType>
class AsAtomicImpl {
64
 public:
65
  using AtomicStorageType = TAtomicStorageType;
66

67 68
  template <typename T>
  static T Acquire_Load(T* addr) {
69 70 71
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    return cast_helper<T>::to_return_type(
        base::Acquire_Load(to_storage_addr(addr)));
72 73 74 75
  }

  template <typename T>
  static T Relaxed_Load(T* addr) {
76 77 78
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    return cast_helper<T>::to_return_type(
        base::Relaxed_Load(to_storage_addr(addr)));
79 80 81 82 83
  }

  template <typename T>
  static void Release_Store(T* addr,
                            typename std::remove_reference<T>::type new_value) {
84 85 86
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    base::Release_Store(to_storage_addr(addr),
                        cast_helper<T>::to_storage_type(new_value));
87 88 89 90 91
  }

  template <typename T>
  static void Relaxed_Store(T* addr,
                            typename std::remove_reference<T>::type new_value) {
92 93 94
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    base::Relaxed_Store(to_storage_addr(addr),
                        cast_helper<T>::to_storage_type(new_value));
95 96 97 98 99 100
  }

  template <typename T>
  static T Release_CompareAndSwap(
      T* addr, typename std::remove_reference<T>::type old_value,
      typename std::remove_reference<T>::type new_value) {
101 102 103 104
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    return cast_helper<T>::to_return_type(base::Release_CompareAndSwap(
        to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
        cast_helper<T>::to_storage_type(new_value)));
105 106 107 108 109 110
  }

  // Atomically sets bits selected by the mask to the given value.
  // Returns false if the bits are already set as needed.
  template <typename T>
  static bool SetBits(T* addr, T bits, T mask) {
111
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
112 113 114 115 116 117 118 119 120 121 122 123
    DCHECK_EQ(bits & ~mask, static_cast<T>(0));
    T old_value;
    T new_value;
    do {
      old_value = Relaxed_Load(addr);
      if ((old_value & mask) == bits) return false;
      new_value = (old_value & ~mask) | bits;
    } while (Release_CompareAndSwap(addr, old_value, new_value) != old_value);
    return true;
  }

 private:
124 125 126 127 128 129 130 131 132
  template <typename U>
  struct cast_helper {
    static AtomicStorageType to_storage_type(U value) {
      return static_cast<AtomicStorageType>(value);
    }
    static U to_return_type(AtomicStorageType value) {
      return static_cast<U>(value);
    }
  };
133

134 135 136 137 138 139 140 141 142
  template <typename U>
  struct cast_helper<U*> {
    static AtomicStorageType to_storage_type(U* value) {
      return reinterpret_cast<AtomicStorageType>(value);
    }
    static U* to_return_type(AtomicStorageType value) {
      return reinterpret_cast<U*>(value);
    }
  };
143 144

  template <typename T>
145 146
  static AtomicStorageType* to_storage_addr(T* value) {
    return reinterpret_cast<AtomicStorageType*>(value);
147 148
  }
  template <typename T>
149 150
  static const AtomicStorageType* to_storage_addr(const T* value) {
    return reinterpret_cast<const AtomicStorageType*>(value);
151 152 153
  }
};

154 155 156
using AsAtomic8 = AsAtomicImpl<base::Atomic8>;
using AsAtomic32 = AsAtomicImpl<base::Atomic32>;
using AsAtomicWord = AsAtomicImpl<base::AtomicWord>;
157

158 159 160 161 162
// This is similar to AsAtomicWord but it explicitly deletes functionality
// provided atomic access to bit representation of stored values.
template <typename TAtomicStorageType>
class AsAtomicPointerImpl : public AsAtomicImpl<TAtomicStorageType> {
 public:
163
  template <typename T>
164
  static bool SetBits(T* addr, T bits, T mask) = delete;
165 166
};

167 168
using AsAtomicPointer = AsAtomicPointerImpl<base::AtomicWord>;

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
template <typename T,
          typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
inline void CheckedIncrement(std::atomic<T>* number, T amount) {
  const T old = number->fetch_add(amount);
  DCHECK_GE(old + amount, old);
  USE(old);
}

template <typename T,
          typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
inline void CheckedDecrement(std::atomic<T>* number, T amount) {
  const T old = number->fetch_sub(amount);
  DCHECK_GE(old, amount);
  USE(old);
}

lpy's avatar
lpy committed
185
}  // namespace base
186 187
}  // namespace v8

188
#endif  // V8_BASE_ATOMIC_UTILS_H_