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_ATOMIC_UTILS_H_
6#define V8_ATOMIC_UTILS_H_
7
8#include <limits.h>
9
10#include "src/base/atomicops.h"
11#include "src/base/macros.h"
12
13namespace v8 {
14namespace base {
15
16template <class T>
17class AtomicNumber {
18 public:
19  AtomicNumber() : value_(0) {}
20  explicit AtomicNumber(T initial) : value_(initial) {}
21
22  // Returns the newly set value.
23  V8_INLINE T Increment(T increment) {
24    return static_cast<T>(base::Barrier_AtomicIncrement(
25        &value_, static_cast<base::AtomicWord>(increment)));
26  }
27
28  V8_INLINE T Value() { return static_cast<T>(base::Acquire_Load(&value_)); }
29
30  V8_INLINE void SetValue(T new_value) {
31    base::Release_Store(&value_, static_cast<base::AtomicWord>(new_value));
32  }
33
34  V8_INLINE T operator=(T value) {
35    SetValue(value);
36    return value;
37  }
38
39 private:
40  STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
41
42  base::AtomicWord value_;
43};
44
45
46// Flag using T atomically. Also accepts void* as T.
47template <typename T>
48class AtomicValue {
49 public:
50  AtomicValue() : value_(0) {}
51
52  explicit AtomicValue(T initial)
53      : value_(cast_helper<T>::to_storage_type(initial)) {}
54
55  V8_INLINE T Value() {
56    return cast_helper<T>::to_return_type(base::Acquire_Load(&value_));
57  }
58
59  V8_INLINE bool TrySetValue(T old_value, T new_value) {
60    return base::Release_CompareAndSwap(
61               &value_, cast_helper<T>::to_storage_type(old_value),
62               cast_helper<T>::to_storage_type(new_value)) ==
63           cast_helper<T>::to_storage_type(old_value);
64  }
65
66  V8_INLINE void SetValue(T new_value) {
67    base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value));
68  }
69
70 private:
71  STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
72
73  template <typename S>
74  struct cast_helper {
75    static base::AtomicWord to_storage_type(S value) {
76      return static_cast<base::AtomicWord>(value);
77    }
78    static S to_return_type(base::AtomicWord value) {
79      return static_cast<S>(value);
80    }
81  };
82
83  template <typename S>
84  struct cast_helper<S*> {
85    static base::AtomicWord to_storage_type(S* value) {
86      return reinterpret_cast<base::AtomicWord>(value);
87    }
88    static S* to_return_type(base::AtomicWord value) {
89      return reinterpret_cast<S*>(value);
90    }
91  };
92
93  base::AtomicWord value_;
94};
95
96
97// See utils.h for EnumSet. Storage is always base::AtomicWord.
98// Requirements on E:
99// - No explicit values.
100// - E::kLastValue defined to be the last actually used value.
101//
102// Example:
103// enum E { kA, kB, kC, kLastValue = kC };
104template <class E>
105class AtomicEnumSet {
106 public:
107  explicit AtomicEnumSet(base::AtomicWord bits = 0) : bits_(bits) {}
108
109  bool IsEmpty() const { return ToIntegral() == 0; }
110
111  bool Contains(E element) const { return (ToIntegral() & Mask(element)) != 0; }
112  bool ContainsAnyOf(const AtomicEnumSet& set) const {
113    return (ToIntegral() & set.ToIntegral()) != 0;
114  }
115
116  void RemoveAll() { base::Release_Store(&bits_, 0); }
117
118  bool operator==(const AtomicEnumSet& set) const {
119    return ToIntegral() == set.ToIntegral();
120  }
121
122  bool operator!=(const AtomicEnumSet& set) const {
123    return ToIntegral() != set.ToIntegral();
124  }
125
126  AtomicEnumSet<E> operator|(const AtomicEnumSet& set) const {
127    return AtomicEnumSet<E>(ToIntegral() | set.ToIntegral());
128  }
129
130// The following operations modify the underlying storage.
131
132#define ATOMIC_SET_WRITE(OP, NEW_VAL)                                     \
133  do {                                                                    \
134    base::AtomicWord old;                                                 \
135    do {                                                                  \
136      old = base::Acquire_Load(&bits_);                                   \
137    } while (base::Release_CompareAndSwap(&bits_, old, old OP NEW_VAL) != \
138             old);                                                        \
139  } while (false)
140
141  void Add(E element) { ATOMIC_SET_WRITE(|, Mask(element)); }
142
143  void Add(const AtomicEnumSet& set) { ATOMIC_SET_WRITE(|, set.ToIntegral()); }
144
145  void Remove(E element) { ATOMIC_SET_WRITE(&, ~Mask(element)); }
146
147  void Remove(const AtomicEnumSet& set) {
148    ATOMIC_SET_WRITE(&, ~set.ToIntegral());
149  }
150
151  void Intersect(const AtomicEnumSet& set) {
152    ATOMIC_SET_WRITE(&, set.ToIntegral());
153  }
154
155#undef ATOMIC_SET_OP
156
157 private:
158  // Check whether there's enough storage to hold E.
159  STATIC_ASSERT(E::kLastValue < (sizeof(base::AtomicWord) * CHAR_BIT));
160
161  V8_INLINE base::AtomicWord ToIntegral() const {
162    return base::Acquire_Load(&bits_);
163  }
164
165  V8_INLINE base::AtomicWord Mask(E element) const {
166    return static_cast<base::AtomicWord>(1) << element;
167  }
168
169  base::AtomicWord bits_;
170};
171
172}  // namespace base
173}  // namespace v8
174
175#endif  // #define V8_ATOMIC_UTILS_H_
176