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() : _op_waiting_for_jni_critical(NULL) {
  45 }
  46 
  47 /*
  48  * This is called by the Java thread who leaves the last JNI critical block.
  49  */
  50 void ShenandoahJNICritical::notify_jni_critical() {
  51   assert(Thread::current()->is_Java_thread(), "call only from Java thread");
  52   assert(_op_waiting_for_jni_critical != NULL, "must be waiting for jni critical notification");
  53 
  54   MonitorLockerEx ml(ShenandoahJNICritical_lock, true);
  55 
  56   VMThread::execute(_op_waiting_for_jni_critical);
  57   _op_waiting_for_jni_critical = NULL;
  58 
  59   ml.notify_all();
  60 
  61 }
  62 
  63 /*
  64  * This is called by the VM thread, if it determines that the task must wait
  65  * for JNI critical regions to be left.
  66  */
  67 void ShenandoahJNICritical::set_waiting_for_jni_before_gc(VM_Operation* op) {
  68   assert(Thread::current()->is_VM_thread(), "call only from VM thread");
  69   _op_waiting_for_jni_critical = op;
  70 }
  71 
  72 /**
  73  * This is called by the Shenandoah concurrent thread in order
  74  * to execute a VM_Operation on the VM thread, that needs to perform
  75  * a JNI critical region check.
  76  */
  77 void ShenandoahJNICritical::execute_in_vm_thread(VM_Operation* op) {
  78   MonitorLockerEx ml(ShenandoahJNICritical_lock, true);
  79   VM_ShenandoahJNICriticalOperation jni_op(op);
  80   VMThread::execute(&jni_op);
  81   while (_op_waiting_for_jni_critical != NULL) {
  82     ml.wait(true);
  83   }
  84 }
  85 
  86 
  87 VM_ShenandoahJNICriticalOperation::VM_ShenandoahJNICriticalOperation(VM_Operation* target)
  88   : _target(target) {
  89 }
  90 
  91 VM_Operation::VMOp_Type VM_ShenandoahJNICriticalOperation::type() const {
  92   return _target->type();
  93 }
  94 
  95 const char* VM_ShenandoahJNICriticalOperation::name() const {
  96   return _target->name();
  97 }
  98 
  99 bool VM_ShenandoahJNICriticalOperation::doit_prologue() {
 100   return _target->doit_prologue();
 101 }
 102 
 103 void VM_ShenandoahJNICriticalOperation::doit_epilogue() {
 104   _target->doit_epilogue();
 105 }
 106 
 107 void VM_ShenandoahJNICriticalOperation::doit() {
 108   if (! GC_locker::check_active_before_gc()) {
 109     _target->doit();
 110   } else {
 111 
 112     if (ShenandoahTraceJNICritical) {
 113       gclog_or_tty->print_cr("Deferring JNI critical op because of active JNI critical regions");
 114     }
 115 
 116     // This makes the GC background thread wait, and kick off evacuation as
 117     // soon as JNI notifies us that critical regions have all been left.
 118     ShenandoahHeap *sh = ShenandoahHeap::heap();
 119     sh->jni_critical()->set_waiting_for_jni_before_gc(this);
 120   }
 121 }