1 /*
2  * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 
25 #include "precompiled.hpp"
26 
27 #include "gc/shared/stringdedup/stringDedup.hpp"
28 #include "gc/shared/stringdedup/stringDedupThread.hpp"
29 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
30 #include "gc/shenandoah/shenandoahStrDedupQueue.inline.hpp"
31 #include "gc/shenandoah/shenandoahStringDedup.inline.hpp"
32 #include "logging/log.hpp"
33 #include "runtime/mutex.hpp"
34 #include "runtime/mutexLocker.hpp"
35 
ShenandoahStrDedupQueue()36 ShenandoahStrDedupQueue::ShenandoahStrDedupQueue() :
37   _consumer_queue(NULL),
38   _num_producer_queue(ShenandoahHeap::heap()->max_workers()),
39   _published_queues(NULL),
40   _free_list(NULL),
41   _num_free_buffer(0),
42   _max_free_buffer(ShenandoahHeap::heap()->max_workers() * 2),
43   _cancel(false),
44   _total_buffers(0) {
45   _producer_queues = NEW_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _num_producer_queue, mtGC);
46   for (size_t index = 0; index < _num_producer_queue; index ++) {
47     _producer_queues[index] = NULL;
48   }
49 }
50 
~ShenandoahStrDedupQueue()51 ShenandoahStrDedupQueue::~ShenandoahStrDedupQueue() {
52   MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
53   for (size_t index = 0; index < num_queues_nv(); index ++) {
54     release_buffers(queue_at(index));
55   }
56 
57   release_buffers(_free_list);
58   FREE_C_HEAP_ARRAY(ShenandoahQueueBuffer*, _producer_queues);
59 }
60 
wait_impl()61 void ShenandoahStrDedupQueue::wait_impl() {
62   MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
63   while (_consumer_queue == NULL && !_cancel) {
64     ml.wait();
65     assert(_consumer_queue == NULL, "Why wait?");
66     _consumer_queue = _published_queues;
67     _published_queues = NULL;
68   }
69 }
70 
cancel_wait_impl()71 void ShenandoahStrDedupQueue::cancel_wait_impl() {
72   MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
73   _cancel = true;
74   ml.notify();
75 }
76 
unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure * cl,size_t queue)77 void ShenandoahStrDedupQueue::unlink_or_oops_do_impl(StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) {
78   ShenandoahQueueBuffer* q = queue_at(queue);
79   while (q != NULL) {
80     q->unlink_or_oops_do(cl);
81     q = q->next();
82   }
83 }
84 
queue_at(size_t queue_id) const85 ShenandoahQueueBuffer* ShenandoahStrDedupQueue::queue_at(size_t queue_id) const {
86   assert(queue_id <= num_queues(), "Invalid queue id");
87   if (queue_id < _num_producer_queue) {
88     return _producer_queues[queue_id];
89   } else if (queue_id == _num_producer_queue) {
90     return _consumer_queue;
91   } else {
92     assert(queue_id == _num_producer_queue + 1, "Must be");
93     return _published_queues;
94   }
95 }
96 
set_producer_buffer(ShenandoahQueueBuffer * buf,size_t queue_id)97 void ShenandoahStrDedupQueue::set_producer_buffer(ShenandoahQueueBuffer* buf, size_t queue_id) {
98   assert(queue_id < _num_producer_queue, "Not a producer queue id");
99   _producer_queues[queue_id] = buf;
100 }
101 
push_impl(uint worker_id,oop string_oop)102 void ShenandoahStrDedupQueue::push_impl(uint worker_id, oop string_oop) {
103   assert(worker_id < _num_producer_queue, "Invalid queue id. Can only push to producer queue");
104   assert(ShenandoahStringDedup::is_candidate(string_oop), "Not a candidate");
105 
106   ShenandoahQueueBuffer* buf = queue_at((size_t)worker_id);
107 
108   if (buf == NULL) {
109     MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
110     buf = new_buffer();
111     set_producer_buffer(buf, worker_id);
112   } else if (buf->is_full()) {
113     MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
114     buf->set_next(_published_queues);
115     _published_queues = buf;
116     buf = new_buffer();
117     set_producer_buffer(buf, worker_id);
118     ml.notify();
119   }
120 
121   assert(!buf->is_full(), "Sanity");
122   buf->push(string_oop);
123 }
124 
pop_impl()125 oop ShenandoahStrDedupQueue::pop_impl() {
126   assert(Thread::current() == StringDedupThread::thread(), "Must be dedup thread");
127   while (true) {
128     if (_consumer_queue == NULL) {
129       MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
130       _consumer_queue = _published_queues;
131       _published_queues = NULL;
132     }
133 
134     // there is nothing
135     if (_consumer_queue == NULL) {
136       return NULL;
137     }
138 
139     oop obj = NULL;
140     if (pop_candidate(obj)) {
141       assert(ShenandoahStringDedup::is_candidate(obj), "Must be a candidate");
142       return obj;
143     }
144     assert(obj == NULL, "No more candidate");
145   }
146 }
147 
pop_candidate(oop & obj)148 bool ShenandoahStrDedupQueue::pop_candidate(oop& obj) {
149   ShenandoahQueueBuffer* to_release = NULL;
150   bool suc = true;
151   do {
152     if (_consumer_queue->is_empty()) {
153       ShenandoahQueueBuffer* buf = _consumer_queue;
154       _consumer_queue = _consumer_queue->next();
155       buf->set_next(to_release);
156       to_release = buf;
157 
158       if (_consumer_queue == NULL) {
159         suc = false;
160         break;
161       }
162     }
163     obj = _consumer_queue->pop();
164   } while (obj == NULL);
165 
166   if (to_release != NULL) {
167     MonitorLocker ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag);
168     release_buffers(to_release);
169   }
170 
171   return suc;
172 }
173 
new_buffer()174 ShenandoahQueueBuffer* ShenandoahStrDedupQueue::new_buffer() {
175   assert_lock_strong(StringDedupQueue_lock);
176   if (_free_list != NULL) {
177     assert(_num_free_buffer > 0, "Sanity");
178     ShenandoahQueueBuffer* buf = _free_list;
179     _free_list = _free_list->next();
180     _num_free_buffer --;
181     buf->reset();
182     return buf;
183   } else {
184     assert(_num_free_buffer == 0, "Sanity");
185     _total_buffers ++;
186     return new ShenandoahQueueBuffer;
187   }
188 }
189 
release_buffers(ShenandoahQueueBuffer * list)190 void ShenandoahStrDedupQueue::release_buffers(ShenandoahQueueBuffer* list) {
191   assert_lock_strong(StringDedupQueue_lock);
192   while (list != NULL) {
193     ShenandoahQueueBuffer* tmp = list;
194     list = list->next();
195     if (_num_free_buffer < _max_free_buffer) {
196       tmp->set_next(_free_list);
197       _free_list = tmp;
198       _num_free_buffer ++;
199     } else {
200       _total_buffers --;
201       delete tmp;
202     }
203   }
204 }
205 
print_statistics_impl()206 void ShenandoahStrDedupQueue::print_statistics_impl() {
207   Log(gc, stringdedup) log;
208   log.debug("  Queue:");
209   log.debug("    Total buffers: " SIZE_FORMAT " (" SIZE_FORMAT " %s). " SIZE_FORMAT " buffers are on free list",
210     _total_buffers,
211     byte_size_in_proper_unit(_total_buffers * sizeof(ShenandoahQueueBuffer)),
212     proper_unit_for_byte_size(_total_buffers * sizeof(ShenandoahQueueBuffer)),
213     _num_free_buffer);
214 }
215 
216 class VerifyQueueClosure : public OopClosure {
217 private:
218   ShenandoahHeap* _heap;
219 public:
220   VerifyQueueClosure();
221 
222   void do_oop(oop* o);
do_oop(narrowOop * o)223   void do_oop(narrowOop* o) {
224     ShouldNotCallThis();
225   }
226 };
227 
VerifyQueueClosure()228 VerifyQueueClosure::VerifyQueueClosure() :
229   _heap(ShenandoahHeap::heap()) {
230 }
231 
do_oop(oop * o)232 void VerifyQueueClosure::do_oop(oop* o) {
233   if (*o != NULL) {
234     oop obj = *o;
235     shenandoah_assert_correct(o, obj);
236     assert(java_lang_String::is_instance(obj), "Object must be a String");
237   }
238 }
239 
verify_impl()240 void ShenandoahStrDedupQueue::verify_impl() {
241   VerifyQueueClosure vcl;
242   for (size_t index = 0; index < num_queues(); index ++) {
243     ShenandoahQueueBuffer* buf = queue_at(index);
244     while (buf != NULL) {
245       buf->oops_do(&vcl);
246       buf = buf->next();
247     }
248   }
249 }
250