|
1 /* |
|
2 * Copyright (c) 2017, 2018, 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.hpp" |
|
30 #include "gc/shenandoah/shenandoahStrDedupQueue.inline.hpp" |
|
31 #include "gc/shenandoah/shenandoahStringDedup.hpp" |
|
32 #include "logging/log.hpp" |
|
33 #include "runtime/mutex.hpp" |
|
34 #include "runtime/mutexLocker.hpp" |
|
35 |
|
36 ShenandoahStrDedupQueue::ShenandoahStrDedupQueue() : |
|
37 _consumer_queue(NULL), |
|
38 _num_producer_queue(ShenandoahHeap::heap()->max_workers()), |
|
39 _published_queues(NULL), |
|
40 _free_list(NULL), |
|
41 _num_free_buffer(0), |
|
42 _max_free_buffer(ShenandoahHeap::heap()->max_workers() * 2), |
|
43 _cancel(false), |
|
44 _total_buffers(0) { |
|
45 _producer_queues = NEW_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _num_producer_queue, mtGC); |
|
46 for (size_t index = 0; index < _num_producer_queue; index ++) { |
|
47 _producer_queues[index] = NULL; |
|
48 } |
|
49 } |
|
50 |
|
51 ShenandoahStrDedupQueue::~ShenandoahStrDedupQueue() { |
|
52 MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
|
53 for (size_t index = 0; index < num_queues(); index ++) { |
|
54 release_buffers(queue_at(index)); |
|
55 } |
|
56 |
|
57 release_buffers(_free_list); |
|
58 FREE_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _producer_queues); |
|
59 } |
|
60 |
|
61 void ShenandoahStrDedupQueue::wait_impl() { |
|
62 MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
|
63 while (_consumer_queue == NULL && !_cancel) { |
|
64 ml.wait(Mutex::_no_safepoint_check_flag); |
|
65 assert(_consumer_queue == NULL, "Why wait?"); |
|
66 _consumer_queue = _published_queues; |
|
67 _published_queues = NULL; |
|
68 } |
|
69 } |
|
70 |
|
71 void ShenandoahStrDedupQueue::cancel_wait_impl() { |
|
72 MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
|
73 _cancel = true; |
|
74 ml.notify(); |
|
75 } |
|
76 |
|
77 void ShenandoahStrDedupQueue::unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) { |
|
78 ShenandoahQueueBuffer* q = queue_at(queue); |
|
79 while (q != NULL) { |
|
80 q->unlink_or_oops_do(cl); |
|
81 q = q->next(); |
|
82 } |
|
83 } |
|
84 |
|
85 ShenandoahQueueBuffer* ShenandoahStrDedupQueue::queue_at(size_t queue_id) const { |
|
86 assert(queue_id <= num_queues(), "Invalid queue id"); |
|
87 if (queue_id < _num_producer_queue) { |
|
88 return _producer_queues[queue_id]; |
|
89 } else if (queue_id == _num_producer_queue) { |
|
90 return _consumer_queue; |
|
91 } else { |
|
92 assert(queue_id == _num_producer_queue + 1, "Must be"); |
|
93 return _published_queues; |
|
94 } |
|
95 } |
|
96 |
|
97 void ShenandoahStrDedupQueue::set_producer_buffer(ShenandoahQueueBuffer* buf, size_t queue_id) { |
|
98 assert(queue_id < _num_producer_queue, "Not a producer queue id"); |
|
99 _producer_queues[queue_id] = buf; |
|
100 } |
|
101 |
|
102 void ShenandoahStrDedupQueue::push_impl(uint worker_id, oop string_oop) { |
|
103 assert(worker_id < _num_producer_queue, "Invalid queue id. Can only push to producer queue"); |
|
104 assert(ShenandoahStringDedup::is_candidate(string_oop), "Not a candidate"); |
|
105 |
|
106 ShenandoahQueueBuffer* buf = queue_at((size_t)worker_id); |
|
107 |
|
108 if (buf == NULL) { |
|
109 MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
|
110 buf = new_buffer(); |
|
111 set_producer_buffer(buf, worker_id); |
|
112 } else if (buf->is_full()) { |
|
113 MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
|
114 buf->set_next(_published_queues); |
|
115 _published_queues = buf; |
|
116 buf = new_buffer(); |
|
117 set_producer_buffer(buf, worker_id); |
|
118 ml.notify(); |
|
119 } |
|
120 |
|
121 assert(!buf->is_full(), "Sanity"); |
|
122 buf->push(string_oop); |
|
123 } |
|
124 |
|
125 oop ShenandoahStrDedupQueue::pop_impl() { |
|
126 assert(Thread::current() == StringDedupThread::thread(), "Must be dedup thread"); |
|
127 while (true) { |
|
128 if (_consumer_queue == NULL) { |
|
129 MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
|
130 _consumer_queue = _published_queues; |
|
131 _published_queues = NULL; |
|
132 } |
|
133 |
|
134 // there is nothing |
|
135 if (_consumer_queue == NULL) { |
|
136 return NULL; |
|
137 } |
|
138 |
|
139 oop obj = NULL; |
|
140 if (pop_candidate(obj)) { |
|
141 assert(ShenandoahStringDedup::is_candidate(obj), "Must be a candidate"); |
|
142 return obj; |
|
143 } |
|
144 assert(obj == NULL, "No more candidate"); |
|
145 } |
|
146 } |
|
147 |
|
148 bool ShenandoahStrDedupQueue::pop_candidate(oop& obj) { |
|
149 ShenandoahQueueBuffer* to_release = NULL; |
|
150 bool suc = true; |
|
151 do { |
|
152 if (_consumer_queue->is_empty()) { |
|
153 ShenandoahQueueBuffer* buf = _consumer_queue; |
|
154 _consumer_queue = _consumer_queue->next(); |
|
155 buf->set_next(to_release); |
|
156 to_release = buf; |
|
157 |
|
158 if (_consumer_queue == NULL) { |
|
159 suc = false; |
|
160 break; |
|
161 } |
|
162 } |
|
163 obj = _consumer_queue->pop(); |
|
164 } while (obj == NULL); |
|
165 |
|
166 if (to_release != NULL) { |
|
167 MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
|
168 release_buffers(to_release); |
|
169 } |
|
170 |
|
171 return suc; |
|
172 } |
|
173 |
|
174 ShenandoahQueueBuffer* ShenandoahStrDedupQueue::new_buffer() { |
|
175 assert_lock_strong(StringDedupQueue_lock); |
|
176 if (_free_list != NULL) { |
|
177 assert(_num_free_buffer > 0, "Sanity"); |
|
178 ShenandoahQueueBuffer* buf = _free_list; |
|
179 _free_list = _free_list->next(); |
|
180 _num_free_buffer --; |
|
181 buf->reset(); |
|
182 return buf; |
|
183 } else { |
|
184 assert(_num_free_buffer == 0, "Sanity"); |
|
185 _total_buffers ++; |
|
186 return new ShenandoahQueueBuffer; |
|
187 } |
|
188 } |
|
189 |
|
190 void ShenandoahStrDedupQueue::release_buffers(ShenandoahQueueBuffer* list) { |
|
191 assert_lock_strong(StringDedupQueue_lock); |
|
192 while (list != NULL) { |
|
193 ShenandoahQueueBuffer* tmp = list; |
|
194 list = list->next(); |
|
195 if (_num_free_buffer < _max_free_buffer) { |
|
196 tmp->set_next(_free_list); |
|
197 _free_list = tmp; |
|
198 _num_free_buffer ++; |
|
199 } else { |
|
200 _total_buffers --; |
|
201 delete tmp; |
|
202 } |
|
203 } |
|
204 } |
|
205 |
|
206 void ShenandoahStrDedupQueue::print_statistics_impl() { |
|
207 Log(gc, stringdedup) log; |
|
208 log.debug(" Queue:"); |
|
209 log.debug(" Total buffers: " SIZE_FORMAT " (" SIZE_FORMAT " K). " SIZE_FORMAT " buffers are on free list", |
|
210 _total_buffers, (_total_buffers * sizeof(ShenandoahQueueBuffer) / K), _num_free_buffer); |
|
211 } |
|
212 |
|
213 class VerifyQueueClosure : public OopClosure { |
|
214 private: |
|
215 ShenandoahHeap* _heap; |
|
216 public: |
|
217 VerifyQueueClosure(); |
|
218 |
|
219 void do_oop(oop* o); |
|
220 void do_oop(narrowOop* o) { |
|
221 ShouldNotCallThis(); |
|
222 } |
|
223 }; |
|
224 |
|
225 VerifyQueueClosure::VerifyQueueClosure() : |
|
226 _heap(ShenandoahHeap::heap()) { |
|
227 } |
|
228 |
|
229 void VerifyQueueClosure::do_oop(oop* o) { |
|
230 if (*o != NULL) { |
|
231 oop obj = *o; |
|
232 shenandoah_assert_correct(o, obj); |
|
233 assert(java_lang_String::is_instance(obj), "Object must be a String"); |
|
234 } |
|
235 } |
|
236 |
|
237 void ShenandoahStrDedupQueue::verify_impl() { |
|
238 VerifyQueueClosure vcl; |
|
239 for (size_t index = 0; index < num_queues(); index ++) { |
|
240 ShenandoahQueueBuffer* buf = queue_at(index); |
|
241 while (buf != NULL) { |
|
242 buf->oops_do(&vcl); |
|
243 buf = buf->next(); |
|
244 } |
|
245 } |
|
246 } |