changeset 22762:9d78d44d3aac

improved JVMCI support for blocking compilation (GRAAL-1387)
author Doug Simon <doug.simon@oracle.com>
date Fri, 08 Jan 2016 17:30:33 +0100
parents f2206f5bb62e
children 94b7354ef0e0
files src/share/vm/compiler/compileBroker.cpp src/share/vm/compiler/compileBroker.hpp src/share/vm/runtime/advancedThresholdPolicy.cpp src/share/vm/runtime/compilationPolicy.cpp src/share/vm/runtime/compilationPolicy.hpp src/share/vm/runtime/simpleThresholdPolicy.cpp
diffstat 6 files changed, 131 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/compiler/compileBroker.cpp	Wed Dec 30 17:55:07 2015 +0100
+++ b/src/share/vm/compiler/compileBroker.cpp	Fri Jan 08 17:30:33 2016 +0100
@@ -237,6 +237,11 @@
 CompileTaskWrapper::CompileTaskWrapper(CompileTask* task) {
   CompilerThread* thread = CompilerThread::current();
   thread->set_task(task);
+#ifdef COMPILERJVMCI
+  if (task->is_blocking() && CompileBroker::compiler(task->comp_level())->is_jvmci()) {
+    task->set_jvmci_compiler_thread(thread);
+  }
+#endif
   CompileLog*     log  = thread->log();
   if (log != NULL)  task->log_task_start(log);
 }
@@ -256,25 +261,17 @@
       task->mark_complete();
 #ifdef COMPILERJVMCI
       if (CompileBroker::compiler(task->comp_level())->is_jvmci()) {
-        // Blocking JVMCI compilations are performed with a timeout so as
-        // to avoid deadlock between an application thread and a JVMCI
-        // compiler thread (both of which execute Java code).
-        if (task->has_waiter()) {
-          // Notify the waiting thread that the compilation has completed
-          // and let it free the CompileTask.
-          task->lock()->notify_all();
-        } else {
-          // The waiting thread timed out did not free the task.
+        if (!task->has_waiter()) {
+          // The waiting thread timed out and thus did not free the task.
           free_task = true;
         }
-      } else {
+        task->set_jvmci_compiler_thread(NULL);
+      }
+#endif
+      if (!free_task) {
         // Notify the waiting thread that the compilation has completed.
         task->lock()->notify_all();
       }
-#else
-      // Notify the waiting thread that the compilation has completed.
-      task->lock()->notify_all();
-#endif
     }
     if (free_task) {
       // The task can only be freed once the task lock is released.
@@ -308,7 +305,7 @@
   } else {
     task = new CompileTask();
     DEBUG_ONLY(_num_allocated_tasks++;)
-    assert (_num_allocated_tasks < 10000, "Leaking compilation tasks?");
+    NOT_COMPILERJVMCI(assert (_num_allocated_tasks < 10000, "Leaking compilation tasks?");)
     task->set_next(NULL);
     task->set_is_free(true);
   }
@@ -351,6 +348,7 @@
   _osr_bci = osr_bci;
   _is_blocking = is_blocking;
   COMPILERJVMCI_PRESENT(_has_waiter = CompileBroker::compiler(comp_level)->is_jvmci();)
+  COMPILERJVMCI_PRESENT(_jvmci_compiler_thread = NULL;)
   _comp_level = comp_level;
   _num_inlined_bytecodes = 0;
 
@@ -1713,11 +1711,59 @@
   return new_task;
 }
 
-// 1 second should be long enough to complete most JVMCI compilations
-// and not too long to stall a blocking JVMCI compilation that
-// is trying to acquire a lock held by the app thread that submitted the
-// compilation.
-static const long BLOCKING_JVMCI_COMPILATION_TIMEOUT = 1000;
+#ifdef COMPILERJVMCI
+// The number of milliseconds to wait before checking if the
+// JVMCI compiler has become blocked during compilation.
+static const long BLOCKING_JVMCI_COMPILATION_WAIT_TIMESLICE = 500;
+
+// The number of successive times the above check is allowed to
+// see a blocked JVMCI compiler thread before unblocking the
+// thread waiting for the compilation to finish.
+static const int BLOCKING_JVMCI_COMPILATION_WAIT_TO_UNBLOCK_ATTEMPTS = 5;
+
+/**
+ * Waits for a JVMCI compiler to complete a given task. This thread
+ * waits until either the task completes or it sees the JVMCI compiler
+ * thread is blocked for N consecutive milliseconds where N is
+ * BLOCKING_JVMCI_COMPILATION_WAIT_TIMESLICE *
+ * BLOCKING_JVMCI_COMPILATION_WAIT_TO_UNBLOCK_ATTEMPTS.
+ *
+ * @return true if this thread needs to free/recycle the task
+ */
+bool CompileBroker::wait_for_jvmci_completion(CompileTask* task, JavaThread* thread) {
+  MutexLocker waiter(task->lock(), thread);
+  int consecutively_blocked = 0;
+  while (task->lock()->wait(!Mutex::_no_safepoint_check_flag, BLOCKING_JVMCI_COMPILATION_WAIT_TIMESLICE)) {
+    CompilerThread* jvmci_compiler_thread = task->jvmci_compiler_thread();
+    if (jvmci_compiler_thread != NULL) {
+      JavaThreadState state;
+      {
+        // A JVMCI compiler thread should not disappear at this point
+        // but let's be extra safe.
+        MutexLocker mu(Threads_lock, thread);
+        state = jvmci_compiler_thread->thread_state();
+      }
+      if (state == _thread_blocked) {
+        if (++consecutively_blocked == BLOCKING_JVMCI_COMPILATION_WAIT_TO_UNBLOCK_ATTEMPTS) {
+          if (_compilation_log != NULL) {
+            _compilation_log->log_failure(thread, task, "compilation timed out", NULL);
+          }
+          if (PrintCompilation) {
+            task->print_compilation(tty, "compilation timed out");
+          }
+          break;
+        }
+      } else {
+        consecutively_blocked = 0;
+      }
+    } else {
+      // Still waiting on JVMCI compiler queue
+    }
+  }
+  task->clear_waiter();
+  return task->is_complete();
+}
+#endif
 
 /**
  *  Wait for the compilation task to complete.
@@ -1737,18 +1783,7 @@
   bool free_task = true;
 #ifdef COMPILERJVMCI
   if (compiler(task->comp_level())->is_jvmci()) {
-    {
-      MutexLocker waiter(task->lock(), thread);
-      // No need to check if compilation has completed - just
-      // rely on the time out. The JVMCI compiler thread will
-      // recycle the CompileTask.
-      task->lock()->wait(!Mutex::_no_safepoint_check_flag, BLOCKING_JVMCI_COMPILATION_TIMEOUT);
-      // If the compilation completes while has_waiter is true then
-      // this thread is responsible for freeing the task.  Otherwise
-      // the compiler thread will free the task.
-      task->clear_waiter();
-      free_task = task->is_complete();
-    }
+    free_task = wait_for_jvmci_completion(task, thread);
   } else
 #endif
   {
--- a/src/share/vm/compiler/compileBroker.hpp	Wed Dec 30 17:55:07 2015 +0100
+++ b/src/share/vm/compiler/compileBroker.hpp	Fri Jan 08 17:30:33 2016 +0100
@@ -56,6 +56,8 @@
   bool         _is_blocking;
 #ifdef COMPILERJVMCI
   bool         _has_waiter;
+  // Compiler thread for a blocking JVMCI compilation
+  CompilerThread* _jvmci_compiler_thread;
 #endif
   int          _comp_level;
   int          _num_inlined_bytecodes;
@@ -91,6 +93,12 @@
 #ifdef COMPILERJVMCI
   bool         has_waiter() const                { return _has_waiter; }
   void         clear_waiter()                    { _has_waiter = false; }
+  CompilerThread* jvmci_compiler_thread() const  { return _jvmci_compiler_thread; }
+  void         set_jvmci_compiler_thread(CompilerThread* t) {
+    assert(is_blocking(), "must be");
+    assert((t == NULL) != (_jvmci_compiler_thread == NULL), "must be");
+    _jvmci_compiler_thread = t;
+  }
 #endif
 
   nmethodLocker* code_handle() const             { return _code_handle; }
@@ -363,6 +371,9 @@
                                           const char*   comment,
                                           bool          blocking);
   static void wait_for_completion(CompileTask* task);
+#ifdef COMPILERJVMCI
+  static bool wait_for_jvmci_completion(CompileTask* task, JavaThread* thread);
+#endif
 
   static void invoke_compiler_on_method(CompileTask* task);
   static void post_compile(CompilerThread* thread, CompileTask* task, EventCompilation& event, bool success, ciEnv* ci_env);
--- a/src/share/vm/runtime/advancedThresholdPolicy.cpp	Wed Dec 30 17:55:07 2015 +0100
+++ b/src/share/vm/runtime/advancedThresholdPolicy.cpp	Fri Jan 08 17:30:33 2016 +0100
@@ -161,6 +161,9 @@
 
 // Called with the queue locked and with at least one element
 CompileTask* AdvancedThresholdPolicy::select_task(CompileQueue* compile_queue) {
+#ifdef COMPILERJVMCI
+  CompileTask *max_blocking_task = NULL;
+#endif
   CompileTask *max_task = NULL;
   Method* max_method = NULL;
   jlong t = os::javaTimeMillis();
@@ -191,9 +194,28 @@
         max_method = method;
       }
     }
+#ifdef COMPILERJVMCI
+    if (task->is_blocking()) {
+      if (max_blocking_task == NULL || compare_methods(method, max_blocking_task->method())) {
+        max_blocking_task = task;
+      }
+    }
+#endif
     task = next_task;
   }
 
+#ifdef COMPILERJVMCI
+  if (max_blocking_task != NULL) {
+    // In blocking compilation mode, the CompileBroker will make
+    // compilations submitted by a JVMCI compiler thread non-blocking. These
+    // compilations should be scheduled after all blocking compilations
+    // to service non-compiler related compilations sooner and reduce the
+    // chance of such compilations timing out.
+    max_task = max_blocking_task;
+    max_method = max_task->method();
+  }
+#endif
+
   if (max_task->comp_level() == CompLevel_full_profile && TieredStopAtLevel > CompLevel_full_profile
       && is_method_profiled(max_method)) {
     max_task->set_comp_level(CompLevel_limited_profile);
--- a/src/share/vm/runtime/compilationPolicy.cpp	Wed Dec 30 17:55:07 2015 +0100
+++ b/src/share/vm/runtime/compilationPolicy.cpp	Fri Jan 08 17:30:33 2016 +0100
@@ -160,6 +160,19 @@
   return !delay_compilation_during_startup() && CompileBroker::should_compile_new_jobs();
 }
 
+#ifdef COMPILERJVMCI
+CompileTask* CompilationPolicy::select_task_blocking_aware(CompileQueue* compile_queue) {
+  if (!BackgroundCompilation) {
+    for (CompileTask* task = compile_queue->first(); task != NULL; task = task->next()) {
+      if (task->is_blocking()) {
+        return task;
+      }
+    }
+  }
+  return compile_queue->first();
+}
+#endif
+
 #ifndef PRODUCT
 void CompilationPolicy::print_time() {
   tty->print_cr ("Accumulated compilationPolicy times:");
@@ -340,6 +353,7 @@
 }
 
 CompileTask* NonTieredCompPolicy::select_task(CompileQueue* compile_queue) {
+  COMPILERJVMCI_PRESENT(return select_task_blocking_aware(compile_queue);)
   return compile_queue->first();
 }
 
--- a/src/share/vm/runtime/compilationPolicy.hpp	Wed Dec 30 17:55:07 2015 +0100
+++ b/src/share/vm/runtime/compilationPolicy.hpp	Fri Jan 08 17:30:33 2016 +0100
@@ -58,6 +58,22 @@
   static void set_policy(CompilationPolicy* policy) { _policy = policy; }
   static CompilationPolicy* policy()                { return _policy; }
 
+#ifdef COMPILERJVMCI
+  /**
+   * If in blocking compilation mode and the JVMCI compiler is in use,
+   * this method selects a blocking task (if any) before a non-blocking
+   * task. In blocking compilation mode, the CompileBroker will make
+   * compilations submitted by a JVMCI compiler thread non-blocking. These
+   * compilations should be scheduled after all blocking compilations
+   * to service non-compiler related compilations sooner and reduce the
+   * chance of such compilations timing out.
+   *
+   * @return the first non-blocking task in compile_queue if there is one otherwise
+   *         the first task in compile_queue
+   */
+  static CompileTask* select_task_blocking_aware(CompileQueue* compile_queue);
+#endif
+
   // Profiling
   elapsedTimer* accumulated_time() { return &_accumulated_time; }
   void print_time() PRODUCT_RETURN;
--- a/src/share/vm/runtime/simpleThresholdPolicy.cpp	Wed Dec 30 17:55:07 2015 +0100
+++ b/src/share/vm/runtime/simpleThresholdPolicy.cpp	Fri Jan 08 17:30:33 2016 +0100
@@ -168,6 +168,7 @@
 
 // Called with the queue locked and with at least one element
 CompileTask* SimpleThresholdPolicy::select_task(CompileQueue* compile_queue) {
+  COMPILERJVMCI_PRESENT(return select_task_blocking_aware(compile_queue);)
   return compile_queue->first();
 }