| /* |
| * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| #include "precompiled.hpp" |
| |
| #include "gc/shared/stringdedup/stringDedup.hpp" |
| #include "gc/shared/stringdedup/stringDedupThread.hpp" |
| #include "gc/shenandoah/shenandoahHeap.inline.hpp" |
| #include "gc/shenandoah/shenandoahStrDedupQueue.inline.hpp" |
| #include "gc/shenandoah/shenandoahStringDedup.inline.hpp" |
| #include "logging/log.hpp" |
| #include "runtime/mutex.hpp" |
| #include "runtime/mutexLocker.hpp" |
| |
| ShenandoahStrDedupQueue::ShenandoahStrDedupQueue() : |
| _consumer_queue(NULL), |
| _num_producer_queue(ShenandoahHeap::heap()->max_workers()), |
| _published_queues(NULL), |
| _free_list(NULL), |
| _num_free_buffer(0), |
| _max_free_buffer(ShenandoahHeap::heap()->max_workers() * 2), |
| _cancel(false), |
| _total_buffers(0) { |
| _producer_queues = NEW_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _num_producer_queue, mtGC); |
| for (size_t index = 0; index < _num_producer_queue; index ++) { |
| _producer_queues[index] = NULL; |
| } |
| } |
| |
| ShenandoahStrDedupQueue::~ShenandoahStrDedupQueue() { |
| MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
| for (size_t index = 0; index < num_queues_nv(); index ++) { |
| release_buffers(queue_at(index)); |
| } |
| |
| release_buffers(_free_list); |
| FREE_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _producer_queues); |
| } |
| |
| void ShenandoahStrDedupQueue::wait_impl() { |
| MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
| while (_consumer_queue == NULL && !_cancel) { |
| ml.wait(Mutex::_no_safepoint_check_flag); |
| assert(_consumer_queue == NULL, "Why wait?"); |
| _consumer_queue = _published_queues; |
| _published_queues = NULL; |
| } |
| } |
| |
| void ShenandoahStrDedupQueue::cancel_wait_impl() { |
| MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
| _cancel = true; |
| ml.notify(); |
| } |
| |
| void ShenandoahStrDedupQueue::unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) { |
| ShenandoahQueueBuffer* q = queue_at(queue); |
| while (q != NULL) { |
| q->unlink_or_oops_do(cl); |
| q = q->next(); |
| } |
| } |
| |
| ShenandoahQueueBuffer* ShenandoahStrDedupQueue::queue_at(size_t queue_id) const { |
| assert(queue_id <= num_queues(), "Invalid queue id"); |
| if (queue_id < _num_producer_queue) { |
| return _producer_queues[queue_id]; |
| } else if (queue_id == _num_producer_queue) { |
| return _consumer_queue; |
| } else { |
| assert(queue_id == _num_producer_queue + 1, "Must be"); |
| return _published_queues; |
| } |
| } |
| |
| void ShenandoahStrDedupQueue::set_producer_buffer(ShenandoahQueueBuffer* buf, size_t queue_id) { |
| assert(queue_id < _num_producer_queue, "Not a producer queue id"); |
| _producer_queues[queue_id] = buf; |
| } |
| |
| void ShenandoahStrDedupQueue::push_impl(uint worker_id, oop string_oop) { |
| assert(worker_id < _num_producer_queue, "Invalid queue id. Can only push to producer queue"); |
| assert(ShenandoahStringDedup::is_candidate(string_oop), "Not a candidate"); |
| |
| ShenandoahQueueBuffer* buf = queue_at((size_t)worker_id); |
| |
| if (buf == NULL) { |
| MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
| buf = new_buffer(); |
| set_producer_buffer(buf, worker_id); |
| } else if (buf->is_full()) { |
| MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
| buf->set_next(_published_queues); |
| _published_queues = buf; |
| buf = new_buffer(); |
| set_producer_buffer(buf, worker_id); |
| ml.notify(); |
| } |
| |
| assert(!buf->is_full(), "Sanity"); |
| buf->push(string_oop); |
| } |
| |
| oop ShenandoahStrDedupQueue::pop_impl() { |
| assert(Thread::current() == StringDedupThread::thread(), "Must be dedup thread"); |
| while (true) { |
| if (_consumer_queue == NULL) { |
| MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
| _consumer_queue = _published_queues; |
| _published_queues = NULL; |
| } |
| |
| // there is nothing |
| if (_consumer_queue == NULL) { |
| return NULL; |
| } |
| |
| oop obj = NULL; |
| if (pop_candidate(obj)) { |
| assert(ShenandoahStringDedup::is_candidate(obj), "Must be a candidate"); |
| return obj; |
| } |
| assert(obj == NULL, "No more candidate"); |
| } |
| } |
| |
| bool ShenandoahStrDedupQueue::pop_candidate(oop& obj) { |
| ShenandoahQueueBuffer* to_release = NULL; |
| bool suc = true; |
| do { |
| if (_consumer_queue->is_empty()) { |
| ShenandoahQueueBuffer* buf = _consumer_queue; |
| _consumer_queue = _consumer_queue->next(); |
| buf->set_next(to_release); |
| to_release = buf; |
| |
| if (_consumer_queue == NULL) { |
| suc = false; |
| break; |
| } |
| } |
| obj = _consumer_queue->pop(); |
| } while (obj == NULL); |
| |
| if (to_release != NULL) { |
| MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); |
| release_buffers(to_release); |
| } |
| |
| return suc; |
| } |
| |
| ShenandoahQueueBuffer* ShenandoahStrDedupQueue::new_buffer() { |
| assert_lock_strong(StringDedupQueue_lock); |
| if (_free_list != NULL) { |
| assert(_num_free_buffer > 0, "Sanity"); |
| ShenandoahQueueBuffer* buf = _free_list; |
| _free_list = _free_list->next(); |
| _num_free_buffer --; |
| buf->reset(); |
| return buf; |
| } else { |
| assert(_num_free_buffer == 0, "Sanity"); |
| _total_buffers ++; |
| return new ShenandoahQueueBuffer; |
| } |
| } |
| |
| void ShenandoahStrDedupQueue::release_buffers(ShenandoahQueueBuffer* list) { |
| assert_lock_strong(StringDedupQueue_lock); |
| while (list != NULL) { |
| ShenandoahQueueBuffer* tmp = list; |
| list = list->next(); |
| if (_num_free_buffer < _max_free_buffer) { |
| tmp->set_next(_free_list); |
| _free_list = tmp; |
| _num_free_buffer ++; |
| } else { |
| _total_buffers --; |
| delete tmp; |
| } |
| } |
| } |
| |
| void ShenandoahStrDedupQueue::print_statistics_impl() { |
| Log(gc, stringdedup) log; |
| log.debug(" Queue:"); |
| log.debug(" Total buffers: " SIZE_FORMAT " (" SIZE_FORMAT " %s). " SIZE_FORMAT " buffers are on free list", |
| _total_buffers, |
| byte_size_in_proper_unit(_total_buffers * sizeof(ShenandoahQueueBuffer)), |
| proper_unit_for_byte_size(_total_buffers * sizeof(ShenandoahQueueBuffer)), |
| _num_free_buffer); |
| } |
| |
| class VerifyQueueClosure : public OopClosure { |
| private: |
| ShenandoahHeap* _heap; |
| public: |
| VerifyQueueClosure(); |
| |
| void do_oop(oop* o); |
| void do_oop(narrowOop* o) { |
| ShouldNotCallThis(); |
| } |
| }; |
| |
| VerifyQueueClosure::VerifyQueueClosure() : |
| _heap(ShenandoahHeap::heap()) { |
| } |
| |
| void VerifyQueueClosure::do_oop(oop* o) { |
| if (*o != NULL) { |
| oop obj = *o; |
| shenandoah_assert_correct(o, obj); |
| assert(java_lang_String::is_instance(obj), "Object must be a String"); |
| } |
| } |
| |
| void ShenandoahStrDedupQueue::verify_impl() { |
| VerifyQueueClosure vcl; |
| for (size_t index = 0; index < num_queues(); index ++) { |
| ShenandoahQueueBuffer* buf = queue_at(index); |
| while (buf != NULL) { |
| buf->oops_do(&vcl); |
| buf = buf->next(); |
| } |
| } |
| } |