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 }