--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shenandoah/shenandoahStrDedupQueue.cpp Mon Dec 10 15:47:44 2018 +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();
+ }
+ }
+}