1/*
2 * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25#ifndef SHARE_GC_SHARED_PTRQUEUE_HPP
26#define SHARE_GC_SHARED_PTRQUEUE_HPP
27
28#include "memory/padded.hpp"
29#include "utilities/align.hpp"
30#include "utilities/debug.hpp"
31#include "utilities/lockFreeStack.hpp"
32#include "utilities/sizes.hpp"
33
34class Mutex;
35class Monitor;
36
37// There are various techniques that require threads to be able to log
38// addresses. For example, a generational write barrier might log
39// the addresses of modified old-generation objects. This type supports
40// this operation.
41
42class BufferNode;
43class PtrQueueSet;
44class PtrQueue {
45 friend class VMStructs;
46
47 // Noncopyable - not defined.
48 PtrQueue(const PtrQueue&);
49 PtrQueue& operator=(const PtrQueue&);
50
51 // The ptr queue set to which this queue belongs.
52 PtrQueueSet* const _qset;
53
54 // Whether updates should be logged.
55 bool _active;
56
57 // The (byte) index at which an object was last enqueued. Starts at
58 // capacity_in_bytes (indicating an empty buffer) and goes towards zero.
59 // Value is always pointer-size aligned.
60 size_t _index;
61
62 // Size of the current buffer, in bytes.
63 // Value is always pointer-size aligned.
64 size_t _capacity_in_bytes;
65
66 static const size_t _element_size = sizeof(void*);
67
68 // Get the capacity, in bytes. The capacity must have been set.
69 size_t capacity_in_bytes() const {
70 assert(_capacity_in_bytes > 0, "capacity not set");
71 return _capacity_in_bytes;
72 }
73
74 static size_t byte_index_to_index(size_t ind) {
75 assert(is_aligned(ind, _element_size), "precondition");
76 return ind / _element_size;
77 }
78
79 static size_t index_to_byte_index(size_t ind) {
80 return ind * _element_size;
81 }
82
83protected:
84 // The buffer.
85 void** _buf;
86
87 size_t index() const {
88 return byte_index_to_index(_index);
89 }
90
91 void set_index(size_t new_index) {
92 size_t byte_index = index_to_byte_index(new_index);
93 assert(byte_index <= capacity_in_bytes(), "precondition");
94 _index = byte_index;
95 }
96
97 size_t capacity() const {
98 return byte_index_to_index(capacity_in_bytes());
99 }
100
101 PtrQueueSet* qset() const { return _qset; }
102
103 // Process queue entries and release resources.
104 void flush_impl();
105
106 // Process (some of) the buffer and leave it in place for further use,
107 // or enqueue the buffer and allocate a new one.
108 virtual void handle_completed_buffer() = 0;
109
110 void allocate_buffer();
111
112 // Enqueue the current buffer in the qset and allocate a new buffer.
113 void enqueue_completed_buffer();
114
115 // Initialize this queue to contain a null buffer, and be part of the
116 // given PtrQueueSet.
117 PtrQueue(PtrQueueSet* qset, bool active = false);
118
119 // Requires queue flushed.
120 ~PtrQueue();
121
122public:
123
124 // Forcibly set empty.
125 void reset() {
126 if (_buf != NULL) {
127 _index = capacity_in_bytes();
128 }
129 }
130
131 void enqueue(volatile void* ptr) {
132 enqueue((void*)(ptr));
133 }
134
135 // Enqueues the given "obj".
136 void enqueue(void* ptr) {
137 if (!_active) return;
138 else enqueue_known_active(ptr);
139 }
140
141 void handle_zero_index();
142
143 void enqueue_known_active(void* ptr);
144
145 // Return the size of the in-use region.
146 size_t size() const {
147 size_t result = 0;
148 if (_buf != NULL) {
149 assert(_index <= capacity_in_bytes(), "Invariant");
150 result = byte_index_to_index(capacity_in_bytes() - _index);
151 }
152 return result;
153 }
154
155 bool is_empty() const {
156 return _buf == NULL || capacity_in_bytes() == _index;
157 }
158
159 // Set the "active" property of the queue to "b". An enqueue to an
160 // inactive thread is a no-op. Setting a queue to inactive resets its
161 // log to the empty state.
162 void set_active(bool b) {
163 _active = b;
164 if (!b && _buf != NULL) {
165 reset();
166 } else if (b && _buf != NULL) {
167 assert(index() == capacity(),
168 "invariant: queues are empty when activated.");
169 }
170 }
171
172 bool is_active() const { return _active; }
173
174 // To support compiler.
175
176protected:
177 template<typename Derived>
178 static ByteSize byte_offset_of_index() {
179 return byte_offset_of(Derived, _index);
180 }
181
182 static ByteSize byte_width_of_index() { return in_ByteSize(sizeof(size_t)); }
183
184 template<typename Derived>
185 static ByteSize byte_offset_of_buf() {
186 return byte_offset_of(Derived, _buf);
187 }
188
189 static ByteSize byte_width_of_buf() { return in_ByteSize(_element_size); }
190
191 template<typename Derived>
192 static ByteSize byte_offset_of_active() {
193 return byte_offset_of(Derived, _active);
194 }
195
196 static ByteSize byte_width_of_active() { return in_ByteSize(sizeof(bool)); }
197
198};
199
200class BufferNode {
201 size_t _index;
202 BufferNode* volatile _next;
203 void* _buffer[1]; // Pseudo flexible array member.
204
205 BufferNode() : _index(0), _next(NULL) { }
206 ~BufferNode() { }
207
208 static size_t buffer_offset() {
209 return offset_of(BufferNode, _buffer);
210 }
211
212 static BufferNode* volatile* next_ptr(BufferNode& bn) { return &bn._next; }
213
214AIX_ONLY(public:) // xlC 12 on AIX doesn't implement C++ DR45.
215 // Allocate a new BufferNode with the "buffer" having size elements.
216 static BufferNode* allocate(size_t size);
217
218 // Free a BufferNode.
219 static void deallocate(BufferNode* node);
220
221public:
222 typedef LockFreeStack<BufferNode, &next_ptr> Stack;
223
224 BufferNode* next() const { return _next; }
225 void set_next(BufferNode* n) { _next = n; }
226 size_t index() const { return _index; }
227 void set_index(size_t i) { _index = i; }
228
229 // Return the BufferNode containing the buffer, after setting its index.
230 static BufferNode* make_node_from_buffer(void** buffer, size_t index) {
231 BufferNode* node =
232 reinterpret_cast<BufferNode*>(
233 reinterpret_cast<char*>(buffer) - buffer_offset());
234 node->set_index(index);
235 return node;
236 }
237
238 // Return the buffer for node.
239 static void** make_buffer_from_node(BufferNode *node) {
240 // &_buffer[0] might lead to index out of bounds warnings.
241 return reinterpret_cast<void**>(
242 reinterpret_cast<char*>(node) + buffer_offset());
243 }
244
245 class Allocator; // Free-list based allocator.
246 class TestSupport; // Unit test support.
247};
248
249// Allocation is based on a lock-free free list of nodes, linked through
250// BufferNode::_next (see BufferNode::Stack). To solve the ABA problem,
251// popping a node from the free list is performed within a GlobalCounter
252// critical section, and pushing nodes onto the free list is done after
253// a GlobalCounter synchronization associated with the nodes to be pushed.
254// This is documented behavior so that other parts of the node life-cycle
255// can depend on and make use of it too.
256class BufferNode::Allocator {
257 friend class TestSupport;
258
259 // Since we don't expect many instances, and measured >15% speedup
260 // on stress gtest, padding seems like a good tradeoff here.
261#define DECLARE_PADDED_MEMBER(Id, Type, Name) \
262 Type Name; DEFINE_PAD_MINUS_SIZE(Id, DEFAULT_CACHE_LINE_SIZE, sizeof(Type))
263
264 const size_t _buffer_size;
265 char _name[DEFAULT_CACHE_LINE_SIZE - sizeof(size_t)]; // Use name as padding.
266 DECLARE_PADDED_MEMBER(1, Stack, _pending_list);
267 DECLARE_PADDED_MEMBER(2, Stack, _free_list);
268 DECLARE_PADDED_MEMBER(3, volatile size_t, _pending_count);
269 DECLARE_PADDED_MEMBER(4, volatile size_t, _free_count);
270 DECLARE_PADDED_MEMBER(5, volatile bool, _transfer_lock);
271
272#undef DECLARE_PADDED_MEMBER
273
274 void delete_list(BufferNode* list);
275 bool try_transfer_pending();
276
277public:
278 Allocator(const char* name, size_t buffer_size);
279 ~Allocator();
280
281 const char* name() const { return _name; }
282 size_t buffer_size() const { return _buffer_size; }
283 size_t free_count() const;
284 BufferNode* allocate();
285 void release(BufferNode* node);
286
287 // Deallocate some of the available buffers. remove_goal is the target
288 // number to remove. Returns the number actually deallocated, which may
289 // be less than the goal if there were fewer available.
290 size_t reduce_free_list(size_t remove_goal);
291};
292
293// A PtrQueueSet represents resources common to a set of pointer queues.
294// In particular, the individual queues allocate buffers from this shared
295// set, and return completed buffers to the set.
296class PtrQueueSet {
297 BufferNode::Allocator* _allocator;
298
299 Monitor* _cbl_mon; // Protects the fields below.
300 BufferNode* _completed_buffers_head;
301 BufferNode* _completed_buffers_tail;
302 volatile size_t _n_completed_buffers;
303
304 size_t _process_completed_buffers_threshold;
305 volatile bool _process_completed_buffers;
306
307 // If true, notify_all on _cbl_mon when the threshold is reached.
308 bool _notify_when_complete;
309
310 void assert_completed_buffers_list_len_correct_locked() NOT_DEBUG_RETURN;
311
312protected:
313 bool _all_active;
314
315 // Create an empty ptr queue set.
316 PtrQueueSet(bool notify_when_complete = false);
317 ~PtrQueueSet();
318
319 // Because of init-order concerns, we can't pass these as constructor
320 // arguments.
321 void initialize(Monitor* cbl_mon, BufferNode::Allocator* allocator);
322
323 // For (unlocked!) iteration over the completed buffers.
324 BufferNode* completed_buffers_head() const { return _completed_buffers_head; }
325
326 // Deallocate all of the completed buffers.
327 void abandon_completed_buffers();
328
329public:
330
331 // Return the buffer for a BufferNode of size buffer_size().
332 void** allocate_buffer();
333
334 // Return an empty buffer to the free list. The node is required
335 // to have been allocated with a size of buffer_size().
336 void deallocate_buffer(BufferNode* node);
337
338 // A completed buffer is a buffer the mutator is finished with, and
339 // is ready to be processed by the collector. It need not be full.
340
341 // Adds node to the completed buffer list.
342 void enqueue_completed_buffer(BufferNode* node);
343
344 // If the number of completed buffers is > stop_at, then remove and
345 // return a completed buffer from the list. Otherwise, return NULL.
346 BufferNode* get_completed_buffer(size_t stop_at = 0);
347
348 bool process_completed_buffers() { return _process_completed_buffers; }
349 void set_process_completed_buffers(bool x) { _process_completed_buffers = x; }
350
351 bool is_active() { return _all_active; }
352
353 size_t buffer_size() const {
354 return _allocator->buffer_size();
355 }
356
357 // Get/Set the number of completed buffers that triggers log processing.
358 // Log processing should be done when the number of buffers exceeds the
359 // threshold.
360 void set_process_completed_buffers_threshold(size_t sz) {
361 _process_completed_buffers_threshold = sz;
362 }
363 size_t process_completed_buffers_threshold() const {
364 return _process_completed_buffers_threshold;
365 }
366 static const size_t ProcessCompletedBuffersThresholdNever = ~size_t(0);
367
368 size_t completed_buffers_num() const { return _n_completed_buffers; }
369
370 void merge_bufferlists(PtrQueueSet* src);
371
372 // Notify the consumer if the number of buffers crossed the threshold
373 void notify_if_necessary();
374};
375
376#endif // SHARE_GC_SHARED_PTRQUEUE_HPP
377