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_implementation/shenandoah/shenandoahHeap.inline.hpp"
  27 #include "gc_implementation/shenandoah/shenandoahStrDedupQueue.inline.hpp"
  28 #include "gc_implementation/shenandoah/shenandoahStringDedup.hpp"
  29 #include "memory/allocation.inline.hpp"
  30 #include "runtime/atomic.hpp"
  31 
  32 ShenandoahStrDedupQueue::ShenandoahStrDedupQueue(ShenandoahStrDedupQueueSet* queue_set, uint num) :
  33   _queue_set(queue_set), _current_list(NULL), _queue_num(num) {
  34   assert(num < _queue_set->num_queues(), "Not valid queue number");
  35 }
  36 
  37 ShenandoahStrDedupQueue::~ShenandoahStrDedupQueue() {
  38   if (_current_list != NULL) {
  39     delete _current_list;
  40   }
  41 }
  42 
  43 void ShenandoahStrDedupQueue::oops_do(OopClosure* cl) {
  44   if (_current_list != NULL) {
  45     _current_list->oops_do(cl);
  46   }
  47 }
  48 
  49 ShenandoahStrDedupQueueSet::ShenandoahStrDedupQueueSet(uint n) :
  50   _num_queues(n), _free_list(NULL), _num_free_queues(0), _terminated(false), _claimed(0) {
  51   _lock = new Monitor(Mutex::leaf, "ShenandoahStrDedupQueueLock", false);
  52 
  53   _local_queues = NEW_C_HEAP_ARRAY(ShenandoahStrDedupQueue*, num_queues(), mtGC);
  54   _outgoing_work_list = NEW_C_HEAP_ARRAY(QueueChunkedList*, num_queues(), mtGC);
  55 
  56   for (uint index = 0; index < num_queues(); index ++) {
  57     _local_queues[index] = new ShenandoahStrDedupQueue(this, index);
  58     _outgoing_work_list[index] = NULL;
  59   }
  60 }
  61 
  62 ShenandoahStrDedupQueueSet::~ShenandoahStrDedupQueueSet() {
  63   QueueChunkedList* q;
  64   QueueChunkedList* tmp;
  65 
  66   for (uint index = 0; index < num_queues_nv(); index ++) {
  67     if (_local_queues[index] != NULL) {
  68       delete _local_queues[index];
  69     }
  70 
  71     q = _outgoing_work_list[index];
  72     while (q != NULL) {
  73       tmp = q;
  74       q = q->next();
  75       delete tmp;
  76     }
  77   }
  78 
  79   q = _free_list;
  80   while (q != NULL) {
  81     tmp = q;
  82     q = tmp->next();
  83     delete tmp;
  84   }
  85 
  86   FREE_C_HEAP_ARRAY(ShenandoahStrDedupQueue*, _local_queues, mtGC);
  87   FREE_C_HEAP_ARRAY(QueueChunkedList*, _outgoing_work_list, mtGC);
  88 
  89   delete _lock;
  90 }
  91 
  92 size_t ShenandoahStrDedupQueueSet::claim() {
  93   size_t index = (size_t)Atomic::add(1, (volatile jint*)&_claimed) - 1;
  94   return index;
  95 }
  96 
  97 void ShenandoahStrDedupQueueSet::parallel_oops_do(OopClosure* cl) {
  98   assert(cl != NULL, "No closure");
  99   assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
 100   size_t claimed_index;
 101   while ((claimed_index = claim()) < num_queues()) {
 102     queue_at(claimed_index)->oops_do(cl);
 103     QueueChunkedList* head = _outgoing_work_list[claimed_index];
 104     while (head != NULL) {
 105       head->oops_do(cl);
 106       head = head->next();
 107     }
 108   }
 109 }
 110 
 111 void ShenandoahStrDedupQueueSet::oops_do_slow(OopClosure* cl) {
 112   assert(cl != NULL, "No closure");
 113   assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
 114   for (size_t index = 0; index < num_queues(); index ++) {
 115     queue_at(index)->oops_do(cl);
 116     QueueChunkedList* head = _outgoing_work_list[index];
 117     while (head != NULL) {
 118       head->oops_do(cl);
 119       head = head->next();
 120     }
 121   }
 122 }
 123 
 124 void ShenandoahStrDedupQueueSet::terminate() {
 125   MonitorLockerEx locker(_lock, Mutex::_no_safepoint_check_flag);
 126   _terminated = true;
 127   locker.notify_all();
 128 }
 129 
 130 void ShenandoahStrDedupQueueSet::release_chunked_list(QueueChunkedList* q) {
 131   assert(q != NULL, "null queue");
 132   MutexLockerEx locker(lock(), Mutex::_no_safepoint_check_flag);
 133   if (_num_free_queues >= 2 * num_queues()) {
 134     delete q;
 135   } else {
 136     q->set_next(_free_list);
 137     _free_list = q;
 138     _num_free_queues ++;
 139   }
 140 }
 141 
 142 QueueChunkedList* ShenandoahStrDedupQueueSet::allocate_no_lock() {
 143   assert_lock_strong(lock());
 144 
 145   if (_free_list != NULL) {
 146     QueueChunkedList* q = _free_list;
 147     _free_list = _free_list->next();
 148     _num_free_queues --;
 149     q->reset();
 150     return q;
 151   } else {
 152     return new QueueChunkedList();
 153   }
 154 }
 155 
 156 QueueChunkedList* ShenandoahStrDedupQueueSet::allocate_chunked_list() {
 157   MutexLockerEx locker(_lock, Mutex::_no_safepoint_check_flag);
 158   return allocate_no_lock();
 159 }
 160 
 161 QueueChunkedList* ShenandoahStrDedupQueueSet::push_and_get_atomic(QueueChunkedList* q, uint queue_num) {
 162   QueueChunkedList* head = _outgoing_work_list[queue_num];
 163   QueueChunkedList* result;
 164   q->set_next(head);
 165   while ((result = (QueueChunkedList*)Atomic::cmpxchg_ptr(q, &_outgoing_work_list[queue_num], head)) != head) {
 166     head = result;
 167     q->set_next(head);
 168   }
 169 
 170   {
 171     MutexLockerEx locker(lock(), Mutex::_no_safepoint_check_flag);
 172     q = allocate_no_lock();
 173     lock()->notify();
 174   }
 175   return q;
 176 }
 177 
 178 QueueChunkedList* ShenandoahStrDedupQueueSet::remove_work_list_atomic(uint queue_num) {
 179   assert(queue_num < num_queues(), "Invalid queue number");
 180 
 181   QueueChunkedList* list = _outgoing_work_list[queue_num];
 182   QueueChunkedList* result;
 183   while ((result = (QueueChunkedList*)Atomic::cmpxchg_ptr((QueueChunkedList*)NULL, &_outgoing_work_list[queue_num], list)) != list) {
 184     list = result;
 185   }
 186 
 187   return list;
 188 }
 189 
 190 void ShenandoahStrDedupQueueSet::parallel_cleanup() {
 191   ShenandoahStrDedupQueueCleanupClosure cl;
 192   parallel_oops_do(&cl);
 193 }