atomic-utils.h 8.55 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 10

#include <atomic>
11
#include <type_traits>
12 13 14 15 16

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

namespace v8 {
lpy's avatar
lpy committed
17
namespace base {
18

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

26 27
  explicit AtomicValue(T initial)
      : value_(cast_helper<T>::to_storage_type(initial)) {}
28

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

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

 private:
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
  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);
    }
  };

60 61 62
  base::AtomicWord value_;
};

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

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

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

  template <typename T>
  static T Relaxed_Load(T* addr) {
85 86 87
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    return cast_helper<T>::to_return_type(
        base::Relaxed_Load(to_storage_addr(addr)));
88 89
  }

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

98 99 100
  template <typename T>
  static void Release_Store(T* addr,
                            typename std::remove_reference<T>::type new_value) {
101 102 103
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    base::Release_Store(to_storage_addr(addr),
                        cast_helper<T>::to_storage_type(new_value));
104 105 106 107 108
  }

  template <typename T>
  static void Relaxed_Store(T* addr,
                            typename std::remove_reference<T>::type new_value) {
109 110 111
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    base::Relaxed_Store(to_storage_addr(addr),
                        cast_helper<T>::to_storage_type(new_value));
112 113
  }

114 115 116 117 118 119 120 121
  template <typename T>
  static T SeqCst_Swap(T* addr,
                       typename std::remove_reference<T>::type new_value) {
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    return base::SeqCst_AtomicExchange(
        to_storage_addr(addr), cast_helper<T>::to_storage_type(new_value));
  }

122 123 124 125
  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) {
126 127 128 129
    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)));
130 131 132 133 134 135 136 137 138 139
  }

  template <typename T>
  static T Relaxed_CompareAndSwap(
      T* addr, typename std::remove_reference<T>::type old_value,
      typename std::remove_reference<T>::type new_value) {
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    return cast_helper<T>::to_return_type(base::Relaxed_CompareAndSwap(
        to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
        cast_helper<T>::to_storage_type(new_value)));
140 141
  }

142 143 144 145 146 147 148 149 150 151
  template <typename T>
  static T AcquireRelease_CompareAndSwap(
      T* addr, typename std::remove_reference<T>::type old_value,
      typename std::remove_reference<T>::type new_value) {
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
    return cast_helper<T>::to_return_type(base::AcquireRelease_CompareAndSwap(
        to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
        cast_helper<T>::to_storage_type(new_value)));
  }

152 153 154 155
  // 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) {
156
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
157
    DCHECK_EQ(bits & ~mask, static_cast<T>(0));
158 159
    T old_value = Relaxed_Load(addr);
    T new_value, old_value_before_cas;
160 161 162
    do {
      if ((old_value & mask) == bits) return false;
      new_value = (old_value & ~mask) | bits;
163 164 165
      old_value_before_cas = old_value;
      old_value = Release_CompareAndSwap(addr, old_value, new_value);
    } while (old_value != old_value_before_cas);
166 167 168 169
    return true;
  }

 private:
170 171 172 173 174 175 176 177 178
  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);
    }
  };
179

180 181 182 183 184 185 186 187 188
  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);
    }
  };
189 190

  template <typename T>
191 192
  static AtomicStorageType* to_storage_addr(T* value) {
    return reinterpret_cast<AtomicStorageType*>(value);
193 194
  }
  template <typename T>
195 196
  static const AtomicStorageType* to_storage_addr(const T* value) {
    return reinterpret_cast<const AtomicStorageType*>(value);
197 198 199
  }
};

200
using AsAtomic8 = AsAtomicImpl<base::Atomic8>;
201
using AsAtomic16 = AsAtomicImpl<base::Atomic16>;
202 203
using AsAtomic32 = AsAtomicImpl<base::Atomic32>;
using AsAtomicWord = AsAtomicImpl<base::AtomicWord>;
204

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
template <int Width>
struct AtomicTypeFromByteWidth {};
template <>
struct AtomicTypeFromByteWidth<1> {
  using type = base::Atomic8;
};
template <>
struct AtomicTypeFromByteWidth<2> {
  using type = base::Atomic16;
};
template <>
struct AtomicTypeFromByteWidth<4> {
  using type = base::Atomic32;
};
#if V8_HOST_ARCH_64_BIT
template <>
struct AtomicTypeFromByteWidth<8> {
  using type = base::Atomic64;
};
#endif

226 227 228 229 230
// 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:
231
  template <typename T>
232
  static bool SetBits(T* addr, T bits, T mask) = delete;
233 234
};

235 236
using AsAtomicPointer = AsAtomicPointerImpl<base::AtomicWord>;

237 238
template <typename T,
          typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
239 240 241 242
inline void CheckedIncrement(
    std::atomic<T>* number, T amount,
    std::memory_order order = std::memory_order_seq_cst) {
  const T old = number->fetch_add(amount, order);
243 244 245 246 247 248
  DCHECK_GE(old + amount, old);
  USE(old);
}

template <typename T,
          typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
249 250 251 252
inline void CheckedDecrement(
    std::atomic<T>* number, T amount,
    std::memory_order order = std::memory_order_seq_cst) {
  const T old = number->fetch_sub(amount, order);
253 254 255 256
  DCHECK_GE(old, amount);
  USE(old);
}

257 258
template <typename T>
V8_INLINE std::atomic<T>* AsAtomicPtr(T* t) {
259 260
  STATIC_ASSERT(sizeof(T) == sizeof(std::atomic<T>));
  STATIC_ASSERT(alignof(T) >= alignof(std::atomic<T>));
261 262 263 264 265
  return reinterpret_cast<std::atomic<T>*>(t);
}

template <typename T>
V8_INLINE const std::atomic<T>* AsAtomicPtr(const T* t) {
266 267
  STATIC_ASSERT(sizeof(T) == sizeof(std::atomic<T>));
  STATIC_ASSERT(alignof(T) >= alignof(std::atomic<T>));
268 269 270
  return reinterpret_cast<const std::atomic<T>*>(t);
}

lpy's avatar
lpy committed
271
}  // namespace base
272 273
}  // namespace v8

274
#endif  // V8_BASE_ATOMIC_UTILS_H_