1/*
2 * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This code is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * version 2 for more details (a copy is included in the LICENSE file that
12 * accompanied this code).
13 *
14 * You should have received a copy of the GNU General Public License version
15 * 2 along with this work; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19 * or visit www.oracle.com if you need additional information or have any
20 * questions.
21 *
22 */
23
24#include "precompiled.hpp"
25
26#include "gc/shared/stringdedup/stringDedup.hpp"
27#include "gc/shared/stringdedup/stringDedupThread.hpp"
28#include "gc/shenandoah/shenandoahHeap.inline.hpp"
29#include "gc/shenandoah/shenandoahStrDedupQueue.inline.hpp"
30#include "gc/shenandoah/shenandoahStringDedup.inline.hpp"
31#include "logging/log.hpp"
32#include "runtime/mutex.hpp"
33#include "runtime/mutexLocker.hpp"
34
35ShenandoahStrDedupQueue::ShenandoahStrDedupQueue() :
36 _consumer_queue(NULL),
37 _num_producer_queue(ShenandoahHeap::heap()->max_workers()),
38 _published_queues(NULL),
39 _free_list(NULL),
40 _num_free_buffer(0),
41 _max_free_buffer(ShenandoahHeap::heap()->max_workers() * 2),
42 _cancel(false),
43 _total_buffers(0) {
44 _producer_queues = NEW_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _num_producer_queue, mtGC);
45 for (size_t index = 0; index < _num_producer_queue; index ++) {
46 _producer_queues[index] = NULL;
47 }
48}
49
50ShenandoahStrDedupQueue::~ShenandoahStrDedupQueue() {
51 MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
52 for (size_t index = 0; index < num_queues(); index ++) {
53 release_buffers(queue_at(index));
54 }
55
56 release_buffers(_free_list);
57 FREE_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _producer_queues);
58}
59
60void ShenandoahStrDedupQueue::wait_impl() {
61 MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
62 while (_consumer_queue == NULL && !_cancel) {
63 ml.wait();
64 assert(_consumer_queue == NULL, "Why wait?");
65 _consumer_queue = _published_queues;
66 _published_queues = NULL;
67 }
68}
69
70void ShenandoahStrDedupQueue::cancel_wait_impl() {
71 MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
72 _cancel = true;
73 ml.notify();
74}
75
76void ShenandoahStrDedupQueue::unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) {
77 ShenandoahQueueBuffer* q = queue_at(queue);
78 while (q != NULL) {
79 q->unlink_or_oops_do(cl);
80 q = q->next();
81 }
82}
83
84ShenandoahQueueBuffer* ShenandoahStrDedupQueue::queue_at(size_t queue_id) const {
85 assert(queue_id <= num_queues(), "Invalid queue id");
86 if (queue_id < _num_producer_queue) {
87 return _producer_queues[queue_id];
88 } else if (queue_id == _num_producer_queue) {
89 return _consumer_queue;
90 } else {
91 assert(queue_id == _num_producer_queue + 1, "Must be");
92 return _published_queues;
93 }
94}
95
96void ShenandoahStrDedupQueue::set_producer_buffer(ShenandoahQueueBuffer* buf, size_t queue_id) {
97 assert(queue_id < _num_producer_queue, "Not a producer queue id");
98 _producer_queues[queue_id] = buf;
99}
100
101void ShenandoahStrDedupQueue::push_impl(uint worker_id, oop string_oop) {
102 assert(worker_id < _num_producer_queue, "Invalid queue id. Can only push to producer queue");
103 assert(ShenandoahStringDedup::is_candidate(string_oop), "Not a candidate");
104
105 ShenandoahQueueBuffer* buf = queue_at((size_t)worker_id);
106
107 if (buf == NULL) {
108 MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
109 buf = new_buffer();
110 set_producer_buffer(buf, worker_id);
111 } else if (buf->is_full()) {
112 MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
113 buf->set_next(_published_queues);
114 _published_queues = buf;
115 buf = new_buffer();
116 set_producer_buffer(buf, worker_id);
117 ml.notify();
118 }
119
120 assert(!buf->is_full(), "Sanity");
121 buf->push(string_oop);
122}
123
124oop ShenandoahStrDedupQueue::pop_impl() {
125 assert(Thread::current() == StringDedupThread::thread(), "Must be dedup thread");
126 while (true) {
127 if (_consumer_queue == NULL) {
128 MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
129 _consumer_queue = _published_queues;
130 _published_queues = NULL;
131 }
132
133 // there is nothing
134 if (_consumer_queue == NULL) {
135 return NULL;
136 }
137
138 oop obj = NULL;
139 if (pop_candidate(obj)) {
140 assert(ShenandoahStringDedup::is_candidate(obj), "Must be a candidate");
141 return obj;
142 }
143 assert(obj == NULL, "No more candidate");
144 }
145}
146
147bool ShenandoahStrDedupQueue::pop_candidate(oop& obj) {
148 ShenandoahQueueBuffer* to_release = NULL;
149 bool suc = true;
150 do {
151 if (_consumer_queue->is_empty()) {
152 ShenandoahQueueBuffer* buf = _consumer_queue;
153 _consumer_queue = _consumer_queue->next();
154 buf->set_next(to_release);
155 to_release = buf;
156
157 if (_consumer_queue == NULL) {
158 suc = false;
159 break;
160 }
161 }
162 obj = _consumer_queue->pop();
163 } while (obj == NULL);
164
165 if (to_release != NULL) {
166 MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
167 release_buffers(to_release);
168 }
169
170 return suc;
171}
172
173ShenandoahQueueBuffer* ShenandoahStrDedupQueue::new_buffer() {
174 assert_lock_strong(StringDedupQueue_lock);
175 if (_free_list != NULL) {
176 assert(_num_free_buffer > 0, "Sanity");
177 ShenandoahQueueBuffer* buf = _free_list;
178 _free_list = _free_list->next();
179 _num_free_buffer --;
180 buf->reset();
181 return buf;
182 } else {
183 assert(_num_free_buffer == 0, "Sanity");
184 _total_buffers ++;
185 return new ShenandoahQueueBuffer;
186 }
187}
188
189void ShenandoahStrDedupQueue::release_buffers(ShenandoahQueueBuffer* list) {
190 assert_lock_strong(StringDedupQueue_lock);
191 while (list != NULL) {
192 ShenandoahQueueBuffer* tmp = list;
193 list = list->next();
194 if (_num_free_buffer < _max_free_buffer) {
195 tmp->set_next(_free_list);
196 _free_list = tmp;
197 _num_free_buffer ++;
198 } else {
199 _total_buffers --;
200 delete tmp;
201 }
202 }
203}
204
205void ShenandoahStrDedupQueue::print_statistics_impl() {
206 Log(gc, stringdedup) log;
207 log.debug(" Queue:");
208 log.debug(" Total buffers: " SIZE_FORMAT " (" SIZE_FORMAT " K). " SIZE_FORMAT " buffers are on free list",
209 _total_buffers, (_total_buffers * sizeof(ShenandoahQueueBuffer) / K), _num_free_buffer);
210}
211
212class VerifyQueueClosure : public OopClosure {
213private:
214 ShenandoahHeap* _heap;
215public:
216 VerifyQueueClosure();
217
218 void do_oop(oop* o);
219 void do_oop(narrowOop* o) {
220 ShouldNotCallThis();
221 }
222};
223
224VerifyQueueClosure::VerifyQueueClosure() :
225 _heap(ShenandoahHeap::heap()) {
226}
227
228void VerifyQueueClosure::do_oop(oop* o) {
229 if (*o != NULL) {
230 oop obj = *o;
231 shenandoah_assert_correct(o, obj);
232 assert(java_lang_String::is_instance(obj), "Object must be a String");
233 }
234}
235
236void ShenandoahStrDedupQueue::verify_impl() {
237 VerifyQueueClosure vcl;
238 for (size_t index = 0; index < num_queues(); index ++) {
239 ShenandoahQueueBuffer* buf = queue_at(index);
240 while (buf != NULL) {
241 buf->oops_do(&vcl);
242 buf = buf->next();
243 }
244 }
245}
246