--- /dev/null 2018-11-30 10:10:44.238550338 +0100 +++ new/src/hotspot/share/gc/shenandoah/shenandoahStrDedupQueue.cpp 2018-11-30 10:22:56.660302544 +0100 @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2017, 2018, 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.hpp" +#include "gc/shenandoah/shenandoahStrDedupQueue.inline.hpp" +#include "gc/shenandoah/shenandoahStringDedup.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(); 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 " K). " SIZE_FORMAT " buffers are on free list", + _total_buffers, (_total_buffers * sizeof(ShenandoahQueueBuffer) / K), _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(); + } + } +}