• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_BASE_ATOMIC_UTILS_H_
6 #define V8_BASE_ATOMIC_UTILS_H_
7 
8 #include <limits.h>
9 
10 #include <atomic>
11 #include <type_traits>
12 
13 #include "src/base/atomicops.h"
14 #include "src/base/macros.h"
15 
16 namespace v8 {
17 namespace base {
18 
19 // Deprecated. Use std::atomic<T> for new code.
20 // Flag using T atomically. Also accepts void* as T.
21 template <typename T>
22 class AtomicValue {
23  public:
AtomicValue()24   AtomicValue() : value_(0) {}
25 
AtomicValue(T initial)26   explicit AtomicValue(T initial)
27       : value_(cast_helper<T>::to_storage_type(initial)) {}
28 
Value()29   V8_INLINE T Value() const {
30     return cast_helper<T>::to_return_type(base::Acquire_Load(&value_));
31   }
32 
SetValue(T new_value)33   V8_INLINE void SetValue(T new_value) {
34     base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value));
35   }
36 
37  private:
38   STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
39 
40   template <typename S>
41   struct cast_helper {
to_storage_typecast_helper42     static base::AtomicWord to_storage_type(S value) {
43       return static_cast<base::AtomicWord>(value);
44     }
to_return_typecast_helper45     static S to_return_type(base::AtomicWord value) {
46       return static_cast<S>(value);
47     }
48   };
49 
50   template <typename S>
51   struct cast_helper<S*> {
52     static base::AtomicWord to_storage_type(S* value) {
53       return reinterpret_cast<base::AtomicWord>(value);
54     }
55     static S* to_return_type(base::AtomicWord value) {
56       return reinterpret_cast<S*>(value);
57     }
58   };
59 
60   base::AtomicWord value_;
61 };
62 
63 // Provides atomic operations for a values stored at some address.
64 template <typename TAtomicStorageType>
65 class AsAtomicImpl {
66  public:
67   using AtomicStorageType = TAtomicStorageType;
68 
69   template <typename T>
70   static T SeqCst_Load(T* addr) {
71     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
72     return cast_helper<T>::to_return_type(
73         base::SeqCst_Load(to_storage_addr(addr)));
74   }
75 
76   template <typename T>
77   static T Acquire_Load(T* addr) {
78     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
79     return cast_helper<T>::to_return_type(
80         base::Acquire_Load(to_storage_addr(addr)));
81   }
82 
83   template <typename T>
84   static T Relaxed_Load(T* addr) {
85     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
86     return cast_helper<T>::to_return_type(
87         base::Relaxed_Load(to_storage_addr(addr)));
88   }
89 
90   template <typename T>
91   static void SeqCst_Store(T* addr,
92                            typename std::remove_reference<T>::type new_value) {
93     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
94     base::SeqCst_Store(to_storage_addr(addr),
95                        cast_helper<T>::to_storage_type(new_value));
96   }
97 
98   template <typename T>
99   static void Release_Store(T* addr,
100                             typename std::remove_reference<T>::type new_value) {
101     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
102     base::Release_Store(to_storage_addr(addr),
103                         cast_helper<T>::to_storage_type(new_value));
104   }
105 
106   template <typename T>
107   static void Relaxed_Store(T* addr,
108                             typename std::remove_reference<T>::type new_value) {
109     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
110     base::Relaxed_Store(to_storage_addr(addr),
111                         cast_helper<T>::to_storage_type(new_value));
112   }
113 
114   template <typename T>
115   static T SeqCst_Swap(T* addr,
116                        typename std::remove_reference<T>::type new_value) {
117     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
118     return base::SeqCst_AtomicExchange(
119         to_storage_addr(addr), cast_helper<T>::to_storage_type(new_value));
120   }
121 
122   template <typename T>
123   static T Release_CompareAndSwap(
124       T* addr, typename std::remove_reference<T>::type old_value,
125       typename std::remove_reference<T>::type new_value) {
126     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
127     return cast_helper<T>::to_return_type(base::Release_CompareAndSwap(
128         to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
129         cast_helper<T>::to_storage_type(new_value)));
130   }
131 
132   template <typename T>
133   static T Relaxed_CompareAndSwap(
134       T* addr, typename std::remove_reference<T>::type old_value,
135       typename std::remove_reference<T>::type new_value) {
136     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
137     return cast_helper<T>::to_return_type(base::Relaxed_CompareAndSwap(
138         to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
139         cast_helper<T>::to_storage_type(new_value)));
140   }
141 
142   template <typename T>
143   static T AcquireRelease_CompareAndSwap(
144       T* addr, typename std::remove_reference<T>::type old_value,
145       typename std::remove_reference<T>::type new_value) {
146     STATIC_ASSERT(sizeof(T) <= sizeof(AtomicStorageType));
147     return cast_helper<T>::to_return_type(base::AcquireRelease_CompareAndSwap(
148         to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
149         cast_helper<T>::to_storage_type(new_value)));
150   }
151 
152   // Atomically sets bits selected by the mask to the given value.
153   // Returns false if the bits are already set as needed.
154   template <typename T>
155   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     T old_value = Relaxed_Load(addr);
159     T new_value, old_value_before_cas;
160     do {
161       if ((old_value & mask) == bits) return false;
162       new_value = (old_value & ~mask) | bits;
163       old_value_before_cas = old_value;
164       old_value = Release_CompareAndSwap(addr, old_value, new_value);
165     } while (old_value != old_value_before_cas);
166     return true;
167   }
168 
169  private:
170   template <typename U>
171   struct cast_helper {
172     static AtomicStorageType to_storage_type(U value) {
173       return static_cast<AtomicStorageType>(value);
174     }
175     static U to_return_type(AtomicStorageType value) {
176       return static_cast<U>(value);
177     }
178   };
179 
180   template <typename U>
181   struct cast_helper<U*> {
182     static AtomicStorageType to_storage_type(U* value) {
183       return reinterpret_cast<AtomicStorageType>(value);
184     }
185     static U* to_return_type(AtomicStorageType value) {
186       return reinterpret_cast<U*>(value);
187     }
188   };
189 
190   template <typename T>
191   static AtomicStorageType* to_storage_addr(T* value) {
192     return reinterpret_cast<AtomicStorageType*>(value);
193   }
194   template <typename T>
195   static const AtomicStorageType* to_storage_addr(const T* value) {
196     return reinterpret_cast<const AtomicStorageType*>(value);
197   }
198 };
199 
200 using AsAtomic8 = AsAtomicImpl<base::Atomic8>;
201 using AsAtomic16 = AsAtomicImpl<base::Atomic16>;
202 using AsAtomic32 = AsAtomicImpl<base::Atomic32>;
203 using AsAtomicWord = AsAtomicImpl<base::AtomicWord>;
204 
205 template <int Width>
206 struct AtomicTypeFromByteWidth {};
207 template <>
208 struct AtomicTypeFromByteWidth<1> {
209   using type = base::Atomic8;
210 };
211 template <>
212 struct AtomicTypeFromByteWidth<2> {
213   using type = base::Atomic16;
214 };
215 template <>
216 struct AtomicTypeFromByteWidth<4> {
217   using type = base::Atomic32;
218 };
219 #if V8_HOST_ARCH_64_BIT
220 template <>
221 struct AtomicTypeFromByteWidth<8> {
222   using type = base::Atomic64;
223 };
224 #endif
225 
226 // This is similar to AsAtomicWord but it explicitly deletes functionality
227 // provided atomic access to bit representation of stored values.
228 template <typename TAtomicStorageType>
229 class AsAtomicPointerImpl : public AsAtomicImpl<TAtomicStorageType> {
230  public:
231   template <typename T>
232   static bool SetBits(T* addr, T bits, T mask) = delete;
233 };
234 
235 using AsAtomicPointer = AsAtomicPointerImpl<base::AtomicWord>;
236 
237 template <typename T,
238           typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
239 inline void CheckedIncrement(
240     std::atomic<T>* number, T amount,
241     std::memory_order order = std::memory_order_seq_cst) {
242   const T old = number->fetch_add(amount, order);
243   DCHECK_GE(old + amount, old);
244   USE(old);
245 }
246 
247 template <typename T,
248           typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
249 inline void CheckedDecrement(
250     std::atomic<T>* number, T amount,
251     std::memory_order order = std::memory_order_seq_cst) {
252   const T old = number->fetch_sub(amount, order);
253   DCHECK_GE(old, amount);
254   USE(old);
255 }
256 
257 template <typename T>
258 V8_INLINE std::atomic<T>* AsAtomicPtr(T* t) {
259   STATIC_ASSERT(sizeof(T) == sizeof(std::atomic<T>));
260   STATIC_ASSERT(alignof(T) >= alignof(std::atomic<T>));
261   return reinterpret_cast<std::atomic<T>*>(t);
262 }
263 
264 template <typename T>
265 V8_INLINE const std::atomic<T>* AsAtomicPtr(const T* t) {
266   STATIC_ASSERT(sizeof(T) == sizeof(std::atomic<T>));
267   STATIC_ASSERT(alignof(T) >= alignof(std::atomic<T>));
268   return reinterpret_cast<const std::atomic<T>*>(t);
269 }
270 
271 }  // namespace base
272 }  // namespace v8
273 
274 #endif  // V8_BASE_ATOMIC_UTILS_H_
275