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 | |
35 | ShenandoahStrDedupQueue::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 | |
50 | ShenandoahStrDedupQueue::~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 | |
60 | void 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 | |
70 | void ShenandoahStrDedupQueue::cancel_wait_impl() { |
71 | MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
72 | _cancel = true; |
73 | ml.notify(); |
74 | } |
75 | |
76 | void 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 | |
84 | ShenandoahQueueBuffer* 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 | |
96 | void 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 | |
101 | void 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 | |
124 | oop 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 | |
147 | bool 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 | |
173 | ShenandoahQueueBuffer* 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 | |
189 | void 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 | |
205 | void 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 | |
212 | class VerifyQueueClosure : public OopClosure { |
213 | private: |
214 | ShenandoahHeap* _heap; |
215 | public: |
216 | VerifyQueueClosure(); |
217 | |
218 | void do_oop(oop* o); |
219 | void do_oop(narrowOop* o) { |
220 | ShouldNotCallThis(); |
221 | } |
222 | }; |
223 | |
224 | VerifyQueueClosure::VerifyQueueClosure() : |
225 | _heap(ShenandoahHeap::heap()) { |
226 | } |
227 | |
228 | void 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 | |
236 | void 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 | |