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