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 }