• 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 #include <type_traits>
10 
11 #include "src/base/atomicops.h"
12 #include "src/base/macros.h"
13 
14 namespace v8 {
15 namespace base {
16 
17 // Deprecated. Use std::atomic<T> for new code.
18 // Flag using T atomically. Also accepts void* as T.
19 template <typename T>
20 class AtomicValue {
21  public:
AtomicValue()22   AtomicValue() : value_(0) {}
23 
AtomicValue(T initial)24   explicit AtomicValue(T initial)
25       : value_(cast_helper<T>::to_storage_type(initial)) {}
26 
Value()27   V8_INLINE T Value() const {
28     return cast_helper<T>::to_return_type(base::Acquire_Load(&value_));
29   }
30 
TrySetValue(T old_value,T new_value)31   V8_INLINE bool TrySetValue(T old_value, T new_value) {
32     return base::Release_CompareAndSwap(
33                &value_, cast_helper<T>::to_storage_type(old_value),
34                cast_helper<T>::to_storage_type(new_value)) ==
35            cast_helper<T>::to_storage_type(old_value);
36   }
37 
SetBits(T bits,T mask)38   V8_INLINE void SetBits(T bits, T mask) {
39     DCHECK_EQ(bits & ~mask, static_cast<T>(0));
40     T old_value;
41     T new_value;
42     do {
43       old_value = Value();
44       new_value = (old_value & ~mask) | bits;
45     } while (!TrySetValue(old_value, new_value));
46   }
47 
SetBit(int bit)48   V8_INLINE void SetBit(int bit) {
49     SetBits(static_cast<T>(1) << bit, static_cast<T>(1) << bit);
50   }
51 
ClearBit(int bit)52   V8_INLINE void ClearBit(int bit) { SetBits(0, 1 << bit); }
53 
SetValue(T new_value)54   V8_INLINE void SetValue(T new_value) {
55     base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value));
56   }
57 
58  private:
59   STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
60 
61   template <typename S>
62   struct cast_helper {
to_storage_typecast_helper63     static base::AtomicWord to_storage_type(S value) {
64       return static_cast<base::AtomicWord>(value);
65     }
to_return_typecast_helper66     static S to_return_type(base::AtomicWord value) {
67       return static_cast<S>(value);
68     }
69   };
70 
71   template <typename S>
72   struct cast_helper<S*> {
73     static base::AtomicWord to_storage_type(S* value) {
74       return reinterpret_cast<base::AtomicWord>(value);
75     }
76     static S* to_return_type(base::AtomicWord value) {
77       return reinterpret_cast<S*>(value);
78     }
79   };
80 
81   base::AtomicWord value_;
82 };
83 
84 class AsAtomic32 {
85  public:
86   template <typename T>
87   static T Acquire_Load(T* addr) {
88     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
89     return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr)));
90   }
91 
92   template <typename T>
93   static T Relaxed_Load(T* addr) {
94     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
95     return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr)));
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(base::Atomic32));
102     base::Release_Store(to_storage_addr(addr), to_storage_type(new_value));
103   }
104 
105   template <typename T>
106   static void Relaxed_Store(T* addr,
107                             typename std::remove_reference<T>::type new_value) {
108     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
109     base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value));
110   }
111 
112   template <typename T>
113   static T Release_CompareAndSwap(
114       T* addr, typename std::remove_reference<T>::type old_value,
115       typename std::remove_reference<T>::type new_value) {
116     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
117     return to_return_type<T>(base::Release_CompareAndSwap(
118         to_storage_addr(addr), to_storage_type(old_value),
119         to_storage_type(new_value)));
120   }
121 
122   // Atomically sets bits selected by the mask to the given value.
123   // Returns false if the bits are already set as needed.
124   template <typename T>
125   static bool SetBits(T* addr, T bits, T mask) {
126     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
127     DCHECK_EQ(bits & ~mask, static_cast<T>(0));
128     T old_value;
129     T new_value;
130     do {
131       old_value = Relaxed_Load(addr);
132       if ((old_value & mask) == bits) return false;
133       new_value = (old_value & ~mask) | bits;
134     } while (Release_CompareAndSwap(addr, old_value, new_value) != old_value);
135     return true;
136   }
137 
138  private:
139   template <typename T>
140   static base::Atomic32 to_storage_type(T value) {
141     return static_cast<base::Atomic32>(value);
142   }
143   template <typename T>
144   static T to_return_type(base::Atomic32 value) {
145     return static_cast<T>(value);
146   }
147   template <typename T>
148   static base::Atomic32* to_storage_addr(T* value) {
149     return reinterpret_cast<base::Atomic32*>(value);
150   }
151   template <typename T>
152   static const base::Atomic32* to_storage_addr(const T* value) {
153     return reinterpret_cast<const base::Atomic32*>(value);
154   }
155 };
156 
157 class AsAtomicWord {
158  public:
159   template <typename T>
160   static T Acquire_Load(T* addr) {
161     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
162     return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr)));
163   }
164 
165   template <typename T>
166   static T Relaxed_Load(T* addr) {
167     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
168     return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr)));
169   }
170 
171   template <typename T>
172   static void Release_Store(T* addr,
173                             typename std::remove_reference<T>::type new_value) {
174     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
175     base::Release_Store(to_storage_addr(addr), to_storage_type(new_value));
176   }
177 
178   template <typename T>
179   static void Relaxed_Store(T* addr,
180                             typename std::remove_reference<T>::type new_value) {
181     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
182     base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value));
183   }
184 
185   template <typename T>
186   static T Release_CompareAndSwap(
187       T* addr, typename std::remove_reference<T>::type old_value,
188       typename std::remove_reference<T>::type new_value) {
189     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
190     return to_return_type<T>(base::Release_CompareAndSwap(
191         to_storage_addr(addr), to_storage_type(old_value),
192         to_storage_type(new_value)));
193   }
194 
195   // Atomically sets bits selected by the mask to the given value.
196   // Returns false if the bits are already set as needed.
197   template <typename T>
198   static bool SetBits(T* addr, T bits, T mask) {
199     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
200     DCHECK_EQ(bits & ~mask, static_cast<T>(0));
201     T old_value;
202     T new_value;
203     do {
204       old_value = Relaxed_Load(addr);
205       if ((old_value & mask) == bits) return false;
206       new_value = (old_value & ~mask) | bits;
207     } while (Release_CompareAndSwap(addr, old_value, new_value) != old_value);
208     return true;
209   }
210 
211  private:
212   template <typename T>
213   static base::AtomicWord to_storage_type(T value) {
214     return static_cast<base::AtomicWord>(value);
215   }
216   template <typename T>
217   static T to_return_type(base::AtomicWord value) {
218     return static_cast<T>(value);
219   }
220   template <typename T>
221   static base::AtomicWord* to_storage_addr(T* value) {
222     return reinterpret_cast<base::AtomicWord*>(value);
223   }
224   template <typename T>
225   static const base::AtomicWord* to_storage_addr(const T* value) {
226     return reinterpret_cast<const base::AtomicWord*>(value);
227   }
228 };
229 
230 class AsAtomic8 {
231  public:
232   template <typename T>
233   static T Acquire_Load(T* addr) {
234     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
235     return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr)));
236   }
237 
238   template <typename T>
239   static T Relaxed_Load(T* addr) {
240     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
241     return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr)));
242   }
243 
244   template <typename T>
245   static void Release_Store(T* addr,
246                             typename std::remove_reference<T>::type new_value) {
247     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
248     base::Release_Store(to_storage_addr(addr), to_storage_type(new_value));
249   }
250 
251   template <typename T>
252   static void Relaxed_Store(T* addr,
253                             typename std::remove_reference<T>::type new_value) {
254     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
255     base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value));
256   }
257 
258   template <typename T>
259   static T Release_CompareAndSwap(
260       T* addr, typename std::remove_reference<T>::type old_value,
261       typename std::remove_reference<T>::type new_value) {
262     STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
263     return to_return_type<T>(base::Release_CompareAndSwap(
264         to_storage_addr(addr), to_storage_type(old_value),
265         to_storage_type(new_value)));
266   }
267 
268  private:
269   template <typename T>
270   static base::Atomic8 to_storage_type(T value) {
271     return static_cast<base::Atomic8>(value);
272   }
273   template <typename T>
274   static T to_return_type(base::Atomic8 value) {
275     return static_cast<T>(value);
276   }
277   template <typename T>
278   static base::Atomic8* to_storage_addr(T* value) {
279     return reinterpret_cast<base::Atomic8*>(value);
280   }
281   template <typename T>
282   static const base::Atomic8* to_storage_addr(const T* value) {
283     return reinterpret_cast<const base::Atomic8*>(value);
284   }
285 };
286 
287 class AsAtomicPointer {
288  public:
289   template <typename T>
290   static T Acquire_Load(T* addr) {
291     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
292     return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr)));
293   }
294 
295   template <typename T>
296   static T Relaxed_Load(T* addr) {
297     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
298     return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr)));
299   }
300 
301   template <typename T>
302   static void Release_Store(T* addr,
303                             typename std::remove_reference<T>::type new_value) {
304     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
305     base::Release_Store(to_storage_addr(addr), to_storage_type(new_value));
306   }
307 
308   template <typename T>
309   static void Relaxed_Store(T* addr,
310                             typename std::remove_reference<T>::type new_value) {
311     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
312     base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value));
313   }
314 
315   template <typename T>
316   static T Release_CompareAndSwap(
317       T* addr, typename std::remove_reference<T>::type old_value,
318       typename std::remove_reference<T>::type new_value) {
319     STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
320     return to_return_type<T>(base::Release_CompareAndSwap(
321         to_storage_addr(addr), to_storage_type(old_value),
322         to_storage_type(new_value)));
323   }
324 
325  private:
326   template <typename T>
327   static base::AtomicWord to_storage_type(T value) {
328     return reinterpret_cast<base::AtomicWord>(value);
329   }
330   template <typename T>
331   static T to_return_type(base::AtomicWord value) {
332     return reinterpret_cast<T>(value);
333   }
334   template <typename T>
335   static base::AtomicWord* to_storage_addr(T* value) {
336     return reinterpret_cast<base::AtomicWord*>(value);
337   }
338   template <typename T>
339   static const base::AtomicWord* to_storage_addr(const T* value) {
340     return reinterpret_cast<const base::AtomicWord*>(value);
341   }
342 };
343 
344 // This class is intended to be used as a wrapper for elements of an array
345 // that is passed in to STL functions such as std::sort. It ensures that
346 // elements accesses are atomic.
347 // Usage example:
348 //   Object** given_array;
349 //   AtomicElement<Object*>* wrapped =
350 //       reinterpret_cast<AtomicElement<Object*>(given_array);
351 //   std::sort(wrapped, wrapped + given_length, cmp);
352 // where the cmp function uses the value() accessor to compare the elements.
353 template <typename T>
354 class AtomicElement {
355  public:
356   AtomicElement(const AtomicElement<T>& other) {
357     AsAtomicPointer::Relaxed_Store(
358         &value_, AsAtomicPointer::Relaxed_Load(&other.value_));
359   }
360 
361   void operator=(const AtomicElement<T>& other) {
362     AsAtomicPointer::Relaxed_Store(
363         &value_, AsAtomicPointer::Relaxed_Load(&other.value_));
364   }
365 
366   T value() const { return AsAtomicPointer::Relaxed_Load(&value_); }
367 
368   bool operator<(const AtomicElement<T>& other) const {
369     return value() < other.value();
370   }
371 
372   bool operator==(const AtomicElement<T>& other) const {
373     return value() == other.value();
374   }
375 
376  private:
377   T value_;
378 };
379 
380 }  // namespace base
381 }  // namespace v8
382 
383 #endif  // V8_BASE_ATOMIC_UTILS_H_
384