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 MonitorLockerEx 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 MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); 62 while (_consumer_queue == NULL && !_cancel) { 63 ml.wait(Mutex::_no_safepoint_check_flag); 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 MonitorLockerEx 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 MonitorLockerEx 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 MonitorLockerEx 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 MonitorLockerEx 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 MonitorLockerEx 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 " %s). " SIZE_FORMAT " buffers are on free list", 209 _total_buffers, 210 byte_size_in_proper_unit(_total_buffers * sizeof(ShenandoahQueueBuffer)), 211 proper_unit_for_byte_size(_total_buffers * sizeof(ShenandoahQueueBuffer)), 212 _num_free_buffer); 213 } 214 215 class VerifyQueueClosure : public OopClosure { 216 private: 217 ShenandoahHeap* _heap; 218 public: 219 VerifyQueueClosure(); 220 221 void do_oop(oop* o); 222 void do_oop(narrowOop* o) { 223 ShouldNotCallThis(); 224 } 225 }; 226 227 VerifyQueueClosure::VerifyQueueClosure() : 228 _heap(ShenandoahHeap::heap()) { 229 } 230 231 void VerifyQueueClosure::do_oop(oop* o) { 232 if (*o != NULL) { 233 oop obj = *o; 234 shenandoah_assert_correct(o, obj); 235 assert(java_lang_String::is_instance(obj), "Object must be a String"); 236 } 237 } 238 239 void ShenandoahStrDedupQueue::verify_impl() { 240 VerifyQueueClosure vcl; 241 for (size_t index = 0; index < num_queues(); index ++) { 242 ShenandoahQueueBuffer* buf = queue_at(index); 243 while (buf != NULL) { 244 buf->oops_do(&vcl); 245 buf = buf->next(); 246 } 247 } 248 }