atomic-utils.h 6.9 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 111 112 113 114
  }

  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)));
115 116
  }

117 118 119 120 121 122 123 124 125 126
  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)));
  }

127 128 129 130
  // 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) {
131
    STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
132
    DCHECK_EQ(bits & ~mask, static_cast<T>(0));
133 134
    T old_value = Relaxed_Load(addr);
    T new_value, old_value_before_cas;
135 136 137
    do {
      if ((old_value & mask) == bits) return false;
      new_value = (old_value & ~mask) | bits;
138 139 140
      old_value_before_cas = old_value;
      old_value = Release_CompareAndSwap(addr, old_value, new_value);
    } while (old_value != old_value_before_cas);
141 142 143 144
    return true;
  }

 private:
145 146 147 148 149 150 151 152 153
  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);
    }
  };
154

155 156 157 158 159 160 161 162 163
  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);
    }
  };
164 165

  template <typename T>
166 167
  static AtomicStorageType* to_storage_addr(T* value) {
    return reinterpret_cast<AtomicStorageType*>(value);
168 169
  }
  template <typename T>
170 171
  static const AtomicStorageType* to_storage_addr(const T* value) {
    return reinterpret_cast<const AtomicStorageType*>(value);
172 173 174
  }
};

175 176 177
using AsAtomic8 = AsAtomicImpl<base::Atomic8>;
using AsAtomic32 = AsAtomicImpl<base::Atomic32>;
using AsAtomicWord = AsAtomicImpl<base::AtomicWord>;
178

179 180 181 182 183
// 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:
184
  template <typename T>
185
  static bool SetBits(T* addr, T bits, T mask) = delete;
186 187
};

188 189
using AsAtomicPointer = AsAtomicPointerImpl<base::AtomicWord>;

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
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);
}

206 207 208 209 210 211 212 213 214 215
template <typename T>
V8_INLINE std::atomic<T>* AsAtomicPtr(T* t) {
  return reinterpret_cast<std::atomic<T>*>(t);
}

template <typename T>
V8_INLINE const std::atomic<T>* AsAtomicPtr(const T* t) {
  return reinterpret_cast<const std::atomic<T>*>(t);
}

lpy's avatar
lpy committed
216
}  // namespace base
217 218
}  // namespace v8

219
#endif  // V8_BASE_ATOMIC_UTILS_H_