1 /* 2 * Copyright (c) 2015, 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 "gc/shenandoah/shenandoahJNICritical.hpp" 25 #include "gc/shenandoah/shenandoahHeap.hpp" 26 27 #include "gc/shared/gcLocker.hpp" 28 #include "runtime/mutexLocker.hpp" 29 #include "runtime/thread.hpp" 30 #include "runtime/vmThread.hpp" 31 32 class VM_ShenandoahJNICriticalOperation : public VM_Operation { 33 private: 34 VM_Operation* _target; 35 public: 36 VM_ShenandoahJNICriticalOperation(VM_Operation* target); 37 VMOp_Type type() const; 38 bool doit_prologue(); 39 void doit_epilogue(); 40 void doit(); 41 const char* name() const; 42 }; 43 44 ShenandoahJNICritical::ShenandoahJNICritical() : 45 _lock(Mutex::leaf, "ShenandoahJNICritical_lock", true, Monitor::_safepoint_check_sometimes), 46 _op_waiting_for_jni_critical(NULL), 47 _op_ready_for_execution(NULL) 48 { 49 } 50 51 /* 52 * This is called by the Java thread who leaves the last JNI critical block. 53 */ 54 void ShenandoahJNICritical::notify_jni_critical() { 55 assert(Thread::current()->is_Java_thread(), "call only from Java thread"); 56 57 assert(_op_waiting_for_jni_critical != NULL, "must be waiting for jni critical notification"); 58 59 if (ShenandoahTraceJNICritical) { 60 tty->print_cr("Shenandoah JNI critical: waiting until task is ready for re-execution"); 61 } 62 63 { 64 MonitorLockerEx ml(&_lock); 65 while (_op_ready_for_execution == NULL) { 66 ml.wait(); 67 } 68 } 69 70 assert(_op_waiting_for_jni_critical != NULL, "must be waiting for jni critical notification"); 71 assert(_op_ready_for_execution != NULL, "must be ready for re-execution"); 72 73 if (ShenandoahTraceJNICritical) { 74 tty->print_cr("Shenandoah JNI critical: re-executing VM task after JNI critical notification"); 75 } 76 77 VMThread::execute(_op_ready_for_execution); 78 79 { 80 MonitorLockerEx ml(&_lock, Mutex::_no_safepoint_check_flag); 81 _op_waiting_for_jni_critical = NULL; 82 _op_ready_for_execution = NULL; 83 ml.notify(); 84 } 85 86 if (ShenandoahTraceJNICritical) { 87 tty->print_cr("Shenandoah JNI critical: resuming Java thread after VM task re-execution"); 88 } 89 90 } 91 92 /* 93 * This is called by the VM thread, if it determines that the task must wait 94 * for JNI critical regions to be left. 95 */ 96 void ShenandoahJNICritical::set_waiting_for_jni_before_gc(VM_Operation* op) { 97 assert(Thread::current()->is_VM_thread(), "call only from VM thread"); 98 _op_waiting_for_jni_critical = op; 99 } 100 101 /** 102 * This is called by the Shenandoah concurrent thread in order 103 * to execute a VM_Operation on the VM thread, that needs to perform 104 * a JNI critical region check. 105 */ 106 void ShenandoahJNICritical::execute_in_vm_thread(VM_Operation* op) { 107 assert(_op_waiting_for_jni_critical == NULL, "start out with no waiting op"); 108 assert(_op_ready_for_execution == NULL, "start out with no ready op"); 109 VM_ShenandoahJNICriticalOperation jni_op(op); 110 VMThread::execute(&jni_op); 111 112 { 113 MonitorLockerEx ml(&_lock, Mutex::_no_safepoint_check_flag); 114 115 if (_op_waiting_for_jni_critical != NULL) { 116 if (ShenandoahTraceJNICritical) { 117 tty->print_cr("Shenandoah JNI critical: make task ready for re-execution"); 118 } 119 120 _op_ready_for_execution = _op_waiting_for_jni_critical; 121 ml.notify(); 122 123 if (ShenandoahTraceJNICritical) { 124 tty->print_cr("Shenandoah JNI critical: waiting for task to get re-executed"); 125 } 126 127 while (_op_ready_for_execution != NULL) { 128 ml.wait(Mutex::_no_safepoint_check_flag); 129 } 130 131 if (ShenandoahTraceJNICritical) { 132 tty->print_cr("Shenandoah JNI critical: resuming concurrent GC thread after task has been re-executed"); 133 } 134 } 135 } 136 137 assert(_op_waiting_for_jni_critical == NULL, "finish with no waiting op"); 138 assert(_op_ready_for_execution == NULL, "finish with no ready op"); 139 } 140 141 142 VM_ShenandoahJNICriticalOperation::VM_ShenandoahJNICriticalOperation(VM_Operation* target) 143 : _target(target) { 144 } 145 146 VM_Operation::VMOp_Type VM_ShenandoahJNICriticalOperation::type() const { 147 return _target->type(); 148 } 149 150 const char* VM_ShenandoahJNICriticalOperation::name() const { 151 return _target->name(); 152 } 153 154 bool VM_ShenandoahJNICriticalOperation::doit_prologue() { 155 return _target->doit_prologue(); 156 } 157 158 void VM_ShenandoahJNICriticalOperation::doit_epilogue() { 159 _target->doit_epilogue(); 160 } 161 162 void VM_ShenandoahJNICriticalOperation::doit() { 163 if (! GCLocker::check_active_before_gc()) { 164 _target->doit(); 165 } else { 166 167 if (ShenandoahTraceJNICritical) { 168 tty->print_cr("Shenandoah JNI critical: Deferring JNI critical op because of active JNI critical regions"); 169 } 170 171 // This makes the GC background thread wait, and kick off evacuation as 172 // soon as JNI notifies us that critical regions have all been left. 173 ShenandoahHeap *sh = ShenandoahHeap::heap(); 174 sh->jni_critical()->set_waiting_for_jni_before_gc(this); 175 } 176 }