1 /* 2 * Copyright (c) 2016, 2018, 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/shenandoah/shenandoahHeap.hpp" 27 #include "gc/shenandoah/shenandoahTaskqueue.hpp" 28 #include "logging/log.hpp" 29 #include "logging/logStream.hpp" 30 31 void ShenandoahObjToScanQueueSet::clear() { 32 uint size = GenericTaskQueueSet<ShenandoahObjToScanQueue, mtGC>::size(); 33 for (uint index = 0; index < size; index ++) { 34 ShenandoahObjToScanQueue* q = queue(index); 35 assert(q != NULL, "Sanity"); 36 q->clear(); 37 } 38 } 39 40 bool ShenandoahObjToScanQueueSet::is_empty() { 41 uint size = GenericTaskQueueSet<ShenandoahObjToScanQueue, mtGC>::size(); 42 for (uint index = 0; index < size; index ++) { 43 ShenandoahObjToScanQueue* q = queue(index); 44 assert(q != NULL, "Sanity"); 45 if (!q->is_empty()) { 46 return false; 47 } 48 } 49 return true; 50 } 51 52 bool ShenandoahTaskTerminator::offer_termination(ShenandoahTerminatorTerminator* terminator) { 53 assert(_n_threads > 0, "Initialization is incorrect"); 54 assert(_offered_termination < _n_threads, "Invariant"); 55 assert(_blocker != NULL, "Invariant"); 56 57 // single worker, done 58 if (_n_threads == 1) { 59 return true; 60 } 61 62 _blocker->lock_without_safepoint_check(); 63 // all arrived, done 64 if (++ _offered_termination == _n_threads) { 65 _blocker->notify_all(); 66 _blocker->unlock(); 67 return true; 68 } 69 70 Thread* the_thread = Thread::current(); 71 while (true) { 72 if (_spin_master == NULL) { 73 _spin_master = the_thread; 74 75 _blocker->unlock(); 76 77 if (do_spin_master_work(terminator)) { 78 assert(_offered_termination == _n_threads, "termination condition"); 79 return true; 80 } else { 81 _blocker->lock_without_safepoint_check(); 82 } 83 } else { 84 _blocker->wait(true, WorkStealingSleepMillis); 85 86 if (_offered_termination == _n_threads) { 87 _blocker->unlock(); 88 return true; 89 } 90 } 91 92 bool force = (terminator != NULL) && terminator->should_force_termination(); 93 bool exit = (terminator != NULL) && terminator->should_exit_termination(); 94 if ((!force && peek_in_queue_set()) || exit) { 95 _offered_termination --; 96 _blocker->unlock(); 97 return false; 98 } 99 } 100 } 101 102 #if TASKQUEUE_STATS 103 void ShenandoahObjToScanQueueSet::print_taskqueue_stats_hdr(outputStream* const st) { 104 st->print_raw_cr("GC Task Stats"); 105 st->print_raw("thr "); TaskQueueStats::print_header(1, st); st->cr(); 106 st->print_raw("--- "); TaskQueueStats::print_header(2, st); st->cr(); 107 } 108 109 void ShenandoahObjToScanQueueSet::print_taskqueue_stats() const { 110 if (!log_develop_is_enabled(Trace, gc, task, stats)) { 111 return; 112 } 113 Log(gc, task, stats) log; 114 ResourceMark rm; 115 LogStream ls(log.trace()); 116 outputStream* st = &ls; 117 print_taskqueue_stats_hdr(st); 118 119 ShenandoahObjToScanQueueSet* queues = const_cast<ShenandoahObjToScanQueueSet*>(this); 120 TaskQueueStats totals; 121 const uint n = size(); 122 for (uint i = 0; i < n; ++i) { 123 st->print(UINT32_FORMAT_W(3), i); 124 queues->queue(i)->stats.print(st); 125 st->cr(); 126 totals += queues->queue(i)->stats; 127 } 128 st->print("tot "); totals.print(st); st->cr(); 129 DEBUG_ONLY(totals.verify()); 130 131 } 132 133 void ShenandoahObjToScanQueueSet::reset_taskqueue_stats() { 134 const uint n = size(); 135 for (uint i = 0; i < n; ++i) { 136 queue(i)->stats.reset(); 137 } 138 } 139 #endif // TASKQUEUE_STATS 140 141 bool ShenandoahTaskTerminator::do_spin_master_work(ShenandoahTerminatorTerminator* terminator) { 142 uint yield_count = 0; 143 // Number of hard spin loops done since last yield 144 uint hard_spin_count = 0; 145 // Number of iterations in the hard spin loop. 146 uint hard_spin_limit = WorkStealingHardSpins; 147 148 // If WorkStealingSpinToYieldRatio is 0, no hard spinning is done. 149 // If it is greater than 0, then start with a small number 150 // of spins and increase number with each turn at spinning until 151 // the count of hard spins exceeds WorkStealingSpinToYieldRatio. 152 // Then do a yield() call and start spinning afresh. 153 if (WorkStealingSpinToYieldRatio > 0) { 154 hard_spin_limit = WorkStealingHardSpins >> WorkStealingSpinToYieldRatio; 155 hard_spin_limit = MAX2(hard_spin_limit, 1U); 156 } 157 // Remember the initial spin limit. 158 uint hard_spin_start = hard_spin_limit; 159 160 // Loop waiting for all threads to offer termination or 161 // more work. 162 while (true) { 163 // Look for more work. 164 // Periodically sleep() instead of yield() to give threads 165 // waiting on the cores the chance to grab this code 166 if (yield_count <= WorkStealingYieldsBeforeSleep) { 167 // Do a yield or hardspin. For purposes of deciding whether 168 // to sleep, count this as a yield. 169 yield_count++; 170 171 // Periodically call yield() instead spinning 172 // After WorkStealingSpinToYieldRatio spins, do a yield() call 173 // and reset the counts and starting limit. 174 if (hard_spin_count > WorkStealingSpinToYieldRatio) { 175 yield(); 176 hard_spin_count = 0; 177 hard_spin_limit = hard_spin_start; 178 #ifdef TRACESPINNING 179 _total_yields++; 180 #endif 181 } else { 182 // Hard spin this time 183 // Increase the hard spinning period but only up to a limit. 184 hard_spin_limit = MIN2(2*hard_spin_limit, 185 (uint) WorkStealingHardSpins); 186 for (uint j = 0; j < hard_spin_limit; j++) { 187 SpinPause(); 188 } 189 hard_spin_count++; 190 #ifdef TRACESPINNING 191 _total_spins++; 192 #endif 193 } 194 } else { 195 log_develop_trace(gc, task)("ShenanddoahTaskTerminator::do_spin_master_work() thread " PTR_FORMAT " sleeps after %u yields", 196 p2i(Thread::current()), yield_count); 197 yield_count = 0; 198 199 MonitorLockerEx locker(_blocker, Mutex::_no_safepoint_check_flag); // no safepoint check 200 _spin_master = NULL; 201 locker.wait(Mutex::_no_safepoint_check_flag, WorkStealingSleepMillis); 202 if (_spin_master == NULL) { 203 _spin_master = Thread::current(); 204 } else { 205 return false; 206 } 207 } 208 209 #ifdef TRACESPINNING 210 _total_peeks++; 211 #endif 212 size_t tasks = tasks_in_queue_set(); 213 if (tasks > 0 && (terminator == NULL || ! terminator->should_force_termination())) { 214 MonitorLockerEx locker(_blocker, Mutex::_no_safepoint_check_flag); // no safepoint check 215 216 if (tasks >= _offered_termination - 1) { 217 locker.notify_all(); 218 } else { 219 for (; tasks > 1; tasks --) { 220 locker.notify(); 221 } 222 } 223 _spin_master = NULL; 224 return false; 225 } else if (_offered_termination == _n_threads) { 226 return true; 227 } 228 } 229 }