|
25 | 25 | #ifndef SHARE_GC_SHARED_TASKQUEUE_HPP |
26 | 26 | #define SHARE_GC_SHARED_TASKQUEUE_HPP |
27 | 27 |
|
| 28 | +#include "cppstdlib/type_traits.hpp" |
28 | 29 | #include "memory/allocation.hpp" |
29 | 30 | #include "memory/padded.hpp" |
| 31 | +#include "metaprogramming/primitiveConversions.hpp" |
30 | 32 | #include "oops/oopsHierarchy.hpp" |
31 | | -#include "runtime/atomicAccess.hpp" |
| 33 | +#include "runtime/atomic.hpp" |
32 | 34 | #include "utilities/debug.hpp" |
33 | 35 | #include "utilities/globalDefinitions.hpp" |
34 | 36 | #include "utilities/ostream.hpp" |
| 37 | +#include "utilities/powerOfTwo.hpp" |
35 | 38 | #include "utilities/stack.hpp" |
36 | 39 |
|
37 | 40 | #if TASKQUEUE_STATS |
@@ -100,76 +103,92 @@ void TaskQueueStats::reset() { |
100 | 103 | } |
101 | 104 | #endif // TASKQUEUE_STATS |
102 | 105 |
|
| 106 | +// Helper for TaskQueueSuper, encoding {queue index, tag} pair in a form that |
| 107 | +// supports atomic access to the pair. |
| 108 | +class TaskQueueAge { |
| 109 | + friend struct PrimitiveConversions::Translate<TaskQueueAge>; |
| 110 | + |
| 111 | +public: |
| 112 | + // Internal type used for indexing the queue, and for the tag. |
| 113 | + using idx_t = NOT_LP64(uint16_t) LP64_ONLY(uint32_t); |
| 114 | + |
| 115 | + explicit TaskQueueAge(size_t data = 0) : _data{data} {} |
| 116 | + TaskQueueAge(idx_t top, idx_t tag) : _fields{top, tag} {} |
| 117 | + |
| 118 | + idx_t top() const { return _fields._top; } |
| 119 | + idx_t tag() const { return _fields._tag; } |
| 120 | + |
| 121 | + bool operator==(const TaskQueueAge& other) const { return _data == other._data; } |
| 122 | + |
| 123 | +private: |
| 124 | + struct Fields { |
| 125 | + idx_t _top; |
| 126 | + idx_t _tag; |
| 127 | + }; |
| 128 | + union { |
| 129 | + size_t _data; // Provides access to _fields as a single integral value. |
| 130 | + Fields _fields; |
| 131 | + }; |
| 132 | + // _data must be able to hold combined _fields. Must be equal to ensure |
| 133 | + // there isn't any padding that could be uninitialized by 2-arg ctor. |
| 134 | + static_assert(sizeof(_data) == sizeof(_fields)); |
| 135 | +}; |
| 136 | + |
| 137 | +// Support for Atomic<TaskQueueAge>. |
| 138 | +template<> |
| 139 | +struct PrimitiveConversions::Translate<TaskQueueAge> : public std::true_type { |
| 140 | + using Value = TaskQueueAge; |
| 141 | + using Decayed = decltype(TaskQueueAge::_data); |
| 142 | + |
| 143 | + static Decayed decay(Value x) { return x._data; } |
| 144 | + static Value recover(Decayed x) { return Value(x); } |
| 145 | +}; |
| 146 | + |
103 | 147 | // TaskQueueSuper collects functionality common to all GenericTaskQueue instances. |
104 | 148 |
|
105 | 149 | template <unsigned int N, MemTag MT> |
106 | 150 | class TaskQueueSuper: public CHeapObj<MT> { |
107 | 151 | protected: |
108 | | - // Internal type for indexing the queue; also used for the tag. |
109 | | - typedef NOT_LP64(uint16_t) LP64_ONLY(uint32_t) idx_t; |
110 | | - STATIC_ASSERT(N == idx_t(N)); // Ensure N fits in an idx_t. |
| 152 | + using Age = TaskQueueAge; |
| 153 | + using idx_t = Age::idx_t; |
| 154 | + static_assert(N == idx_t(N)); // Ensure N fits in an idx_t. |
111 | 155 |
|
112 | 156 | // N must be a power of 2 for computing modulo via masking. |
113 | 157 | // N must be >= 2 for the algorithm to work at all, though larger is better. |
114 | | - STATIC_ASSERT(N >= 2); |
115 | | - STATIC_ASSERT(is_power_of_2(N)); |
| 158 | + static_assert(N >= 2); |
| 159 | + static_assert(is_power_of_2(N)); |
116 | 160 | static const uint MOD_N_MASK = N - 1; |
117 | 161 |
|
118 | | - class Age { |
119 | | - friend class TaskQueueSuper; |
120 | | - |
121 | | - public: |
122 | | - explicit Age(size_t data = 0) : _data(data) {} |
123 | | - Age(idx_t top, idx_t tag) { _fields._top = top; _fields._tag = tag; } |
124 | | - |
125 | | - idx_t top() const { return _fields._top; } |
126 | | - idx_t tag() const { return _fields._tag; } |
127 | | - |
128 | | - bool operator ==(const Age& other) const { return _data == other._data; } |
129 | | - |
130 | | - private: |
131 | | - struct fields { |
132 | | - idx_t _top; |
133 | | - idx_t _tag; |
134 | | - }; |
135 | | - union { |
136 | | - size_t _data; |
137 | | - fields _fields; |
138 | | - }; |
139 | | - STATIC_ASSERT(sizeof(size_t) >= sizeof(fields)); |
140 | | - }; |
141 | | - |
142 | 162 | uint bottom_relaxed() const { |
143 | | - return AtomicAccess::load(&_bottom); |
| 163 | + return _bottom.load_relaxed(); |
144 | 164 | } |
145 | 165 |
|
146 | 166 | uint bottom_acquire() const { |
147 | | - return AtomicAccess::load_acquire(&_bottom); |
| 167 | + return _bottom.load_acquire(); |
148 | 168 | } |
149 | 169 |
|
150 | 170 | void set_bottom_relaxed(uint new_bottom) { |
151 | | - AtomicAccess::store(&_bottom, new_bottom); |
| 171 | + _bottom.store_relaxed(new_bottom); |
152 | 172 | } |
153 | 173 |
|
154 | 174 | void release_set_bottom(uint new_bottom) { |
155 | | - AtomicAccess::release_store(&_bottom, new_bottom); |
| 175 | + _bottom.release_store(new_bottom); |
156 | 176 | } |
157 | 177 |
|
158 | 178 | Age age_relaxed() const { |
159 | | - return Age(AtomicAccess::load(&_age._data)); |
| 179 | + return _age.load_relaxed(); |
160 | 180 | } |
161 | 181 |
|
162 | 182 | void set_age_relaxed(Age new_age) { |
163 | | - AtomicAccess::store(&_age._data, new_age._data); |
| 183 | + _age.store_relaxed(new_age); |
164 | 184 | } |
165 | 185 |
|
166 | 186 | Age cmpxchg_age(Age old_age, Age new_age) { |
167 | | - return Age(AtomicAccess::cmpxchg(&_age._data, old_age._data, new_age._data)); |
| 187 | + return _age.compare_exchange(old_age, new_age); |
168 | 188 | } |
169 | 189 |
|
170 | 190 | idx_t age_top_relaxed() const { |
171 | | - // Atomically accessing a subfield of an "atomic" member. |
172 | | - return AtomicAccess::load(&_age._fields._top); |
| 191 | + return _age.load_relaxed().top(); |
173 | 192 | } |
174 | 193 |
|
175 | 194 | // These both operate mod N. |
@@ -222,16 +241,16 @@ class TaskQueueSuper: public CHeapObj<MT> { |
222 | 241 | DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0); |
223 | 242 |
|
224 | 243 | // Index of the first free element after the last one pushed (mod N). |
225 | | - volatile uint _bottom; |
226 | | - DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(uint)); |
| 244 | + Atomic<uint> _bottom; |
| 245 | + DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(_bottom)); |
227 | 246 |
|
228 | 247 | // top() is the index of the oldest pushed element (mod N), and tag() |
229 | 248 | // is the associated epoch, to distinguish different modifications of |
230 | 249 | // the age. There is no available element if top() == _bottom or |
231 | 250 | // (_bottom - top()) mod N == N-1; the latter indicates underflow |
232 | 251 | // during concurrent pop_local/pop_global. |
233 | | - volatile Age _age; |
234 | | - DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, sizeof(Age)); |
| 252 | + Atomic<Age> _age; |
| 253 | + DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, sizeof(_age)); |
235 | 254 |
|
236 | 255 | NONCOPYABLE(TaskQueueSuper); |
237 | 256 |
|
|
0 commit comments