8252543: [JVMCI] Libgraal can deadlock in blocking compilation mode

Reviewed-by: kvn
This commit is contained in:
Doug Simon 2020-09-12 05:26:16 +00:00
parent b1b0f0b2cc
commit 998ce78e53
11 changed files with 112 additions and 54 deletions

@ -249,11 +249,6 @@ bool compileBroker_init() {
CompileTaskWrapper::CompileTaskWrapper(CompileTask* task) {
CompilerThread* thread = CompilerThread::current();
thread->set_task(task);
#if INCLUDE_JVMCI
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->is_unloaded()) task->log_task_start(log);
}
@ -277,7 +272,7 @@ CompileTaskWrapper::~CompileTaskWrapper() {
// The waiting thread timed out and thus did not free the task.
free_task = true;
}
task->set_jvmci_compiler_thread(NULL);
task->set_blocking_jvmci_compile_state(NULL);
}
#endif
if (!free_task) {
@ -1604,22 +1599,27 @@ bool CompileBroker::wait_for_jvmci_completion(JVMCICompiler* jvmci, CompileTask*
assert(UseJVMCICompiler, "sanity");
MonitorLocker ml(thread, task->lock());
int progress_wait_attempts = 0;
int methods_compiled = jvmci->methods_compiled();
jint thread_jvmci_compilation_ticks = 0;
jint global_jvmci_compilation_ticks = jvmci->global_compilation_ticks();
while (!task->is_complete() && !is_compilation_disabled_forever() &&
ml.wait(JVMCI_COMPILATION_PROGRESS_WAIT_TIMESLICE)) {
CompilerThread* jvmci_compiler_thread = task->jvmci_compiler_thread();
JVMCICompileState* jvmci_compile_state = task->blocking_jvmci_compile_state();
bool progress;
if (jvmci_compiler_thread != NULL) {
// If the JVMCI compiler thread is not blocked or suspended, we deem it to be making progress.
progress = jvmci_compiler_thread->thread_state() != _thread_blocked &&
!jvmci_compiler_thread->is_external_suspend();
if (jvmci_compile_state != NULL) {
jint ticks = jvmci_compile_state->compilation_ticks();
progress = (ticks - thread_jvmci_compilation_ticks) != 0;
JVMCI_event_1("waiting on compilation %d [ticks=%d]", task->compile_id(), ticks);
thread_jvmci_compilation_ticks = ticks;
} else {
// Still waiting on JVMCI compiler queue. This thread may be holding a lock
// that all JVMCI compiler threads are blocked on. We use the counter for
// successful JVMCI compilations to determine whether JVMCI compilation
// that all JVMCI compiler threads are blocked on. We use the global JVMCI
// compilation ticks to determine whether JVMCI compilation
// is still making progress through the JVMCI compiler queue.
progress = jvmci->methods_compiled() != methods_compiled;
jint ticks = jvmci->global_compilation_ticks();
progress = (ticks - global_jvmci_compilation_ticks) != 0;
JVMCI_event_1("waiting on compilation %d to be queued [ticks=%d]", task->compile_id(), ticks);
global_jvmci_compilation_ticks = ticks;
}
if (!progress) {
@ -1627,13 +1627,11 @@ bool CompileBroker::wait_for_jvmci_completion(JVMCICompiler* jvmci, CompileTask*
if (PrintCompilation) {
task->print(tty, "wait for blocking compilation timed out");
}
JVMCI_event_1("waiting on compilation %d timed out", task->compile_id());
break;
}
} else {
progress_wait_attempts = 0;
if (jvmci_compiler_thread == NULL) {
methods_compiled = jvmci->methods_compiled();
}
}
}
task->clear_waiter();
@ -2152,7 +2150,7 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
TraceTime t1("compilation", &time);
EventCompilation event;
JVMCICompileState compile_state(task);
JVMCICompileState compile_state(task, jvmci);
JVMCIRuntime *runtime = NULL;
if (JVMCI::in_shutdown()) {

@ -100,7 +100,7 @@ void CompileTask::initialize(int compile_id,
_osr_bci = osr_bci;
_is_blocking = is_blocking;
JVMCI_ONLY(_has_waiter = CompileBroker::compiler(comp_level)->is_jvmci();)
JVMCI_ONLY(_jvmci_compiler_thread = NULL;)
JVMCI_ONLY(_blocking_jvmci_compile_state = NULL;)
_comp_level = comp_level;
_num_inlined_bytecodes = 0;

@ -31,6 +31,8 @@
#include "memory/allocation.hpp"
#include "utilities/xmlstream.hpp"
JVMCI_ONLY(class JVMCICompileState;)
// CompileTask
//
// An entry in the compile queue. It represents a pending or current
@ -81,8 +83,8 @@ class CompileTask : public CHeapObj<mtCompiler> {
bool _is_blocking;
#if INCLUDE_JVMCI
bool _has_waiter;
// Compiler thread for a blocking JVMCI compilation
CompilerThread* _jvmci_compiler_thread;
// Compilation state for a blocking JVMCI compilation
JVMCICompileState* _blocking_jvmci_compile_state;
#endif
int _comp_level;
int _num_inlined_bytecodes;
@ -144,11 +146,9 @@ class CompileTask : public CHeapObj<mtCompiler> {
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;
JVMCICompileState* blocking_jvmci_compile_state() const { return _blocking_jvmci_compile_state; }
void set_blocking_jvmci_compile_state(JVMCICompileState* state) {
_blocking_jvmci_compile_state = state;
}
#endif

@ -23,9 +23,11 @@
#include "precompiled.hpp"
#include "classfile/systemDictionary.hpp"
#include "compiler/compileTask.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "jvmci/jvmci.hpp"
#include "jvmci/jvmciJavaClasses.hpp"
#include "jvmci/jvmciEnv.hpp"
#include "jvmci/jvmciRuntime.hpp"
#include "jvmci/metadataHandles.hpp"
#include "memory/resourceArea.hpp"
@ -123,6 +125,18 @@ void JVMCI::initialize_globals() {
}
}
JavaThread* JVMCI::compilation_tick(JavaThread* thread) {
if (thread->is_Compiler_thread()) {
CompileTask *task = thread->as_CompilerThread()->task();
if (task != NULL) {
JVMCICompileState *state = task->blocking_jvmci_compile_state();
if (state != NULL) {
state->inc_compilation_ticks();
}
}
}
return thread;
}
void JVMCI::metadata_do(void f(Metadata*)) {
if (_java_runtime != NULL) {

@ -110,6 +110,11 @@ class JVMCI : public AllStatic {
static void initialize_compiler(TRAPS);
// Increments a value indicating some JVMCI compilation activity
// happened on `thread` if it is a CompilerThread.
// Returns `thread`.
static JavaThread* compilation_tick(JavaThread* thread);
static JVMCIRuntime* compiler_runtime() { return _compiler_runtime; }
// Gets the single runtime for JVMCI on the Java heap. This is the only
// JVMCI runtime available when !UseJVMCINativeLibrary.

@ -37,6 +37,7 @@ JVMCICompiler::JVMCICompiler() : AbstractCompiler(compiler_jvmci) {
_bootstrapping = false;
_bootstrap_compilation_request_handled = false;
_methods_compiled = 0;
_global_compilation_ticks = 0;
assert(_instance == NULL, "only one instance allowed");
_instance = this;
}
@ -154,3 +155,12 @@ void JVMCICompiler::print_compilation_timers() {
tty->print_cr(" JVMCI code install time: %6.3f s", code_install_time);
}
}
void JVMCICompiler::inc_methods_compiled() {
Atomic::inc(&_methods_compiled);
Atomic::inc(&_global_compilation_ticks);
}
void JVMCICompiler::inc_global_compilation_ticks() {
Atomic::inc(&_global_compilation_ticks);
}

@ -42,6 +42,10 @@ private:
*/
volatile int _methods_compiled;
// Incremented periodically by JVMCI compiler threads
// to indicate JVMCI compilation activity.
volatile int _global_compilation_ticks;
static JVMCICompiler* _instance;
static elapsedTimer _codeInstallTimer;
@ -99,15 +103,16 @@ public:
// Print compilation timers and statistics
virtual void print_timers();
/**
* Gets the number of methods that have been successfully compiled by
* a call to JVMCICompiler::compile_method().
*/
// Gets the number of methods that have been successfully compiled by
// a call to JVMCICompiler::compile_method().
int methods_compiled() { return _methods_compiled; }
void inc_methods_compiled();
void inc_methods_compiled() {
Atomic::inc(&_methods_compiled);
}
// Gets a value indicating JVMCI compilation activity on any thread.
// If successive calls to this method return a different value, then
// some degree of JVMCI compilation occurred between the calls.
int global_compilation_ticks() const { return _global_compilation_ticks; }
void inc_global_compilation_ticks();
// Print compilation timers and statistics
static void print_compilation_timers();

@ -132,7 +132,7 @@ Handle JavaArgumentUnboxer::next_arg(BasicType expectedType) {
TRACE_CALL(result_type, jvmci_ ## name signature) \
JVMCI_VM_ENTRY_MARK; \
ResourceMark rm; \
JNI_JVMCIENV(thread, env);
JNI_JVMCIENV(JVMCI::compilation_tick(thread), env);
static JavaThread* get_current_thread(bool allow_null=true) {
Thread* thread = Thread::current_or_null_safe();

@ -36,10 +36,12 @@
#include "runtime/jniHandles.inline.hpp"
#include "runtime/javaCalls.hpp"
#include "jvmci/jniAccessMark.inline.hpp"
#include "jvmci/jvmciCompiler.hpp"
#include "jvmci/jvmciRuntime.hpp"
JVMCICompileState::JVMCICompileState(CompileTask* task):
JVMCICompileState::JVMCICompileState(CompileTask* task, JVMCICompiler* compiler):
_task(task),
_compiler(compiler),
_retryable(true),
_failure_reason(NULL),
_failure_reason_on_C_heap(false) {
@ -51,6 +53,20 @@ JVMCICompileState::JVMCICompileState(CompileTask* task):
_jvmti_can_post_on_exceptions = JvmtiExport::can_post_on_exceptions() ? 1 : 0;
_jvmti_can_pop_frame = JvmtiExport::can_pop_frame() ? 1 : 0;
_target_method_is_old = _task != NULL && _task->method()->is_old();
if (task->is_blocking()) {
task->set_blocking_jvmci_compile_state(this);
}
}
// Update global JVMCI compilation ticks after 512 thread-local JVMCI compilation ticks.
// This mitigates the overhead of the atomic operation used for the global update.
#define THREAD_TICKS_PER_GLOBAL_TICKS (2 << 9)
#define THREAD_TICKS_PER_GLOBAL_TICKS_MASK (THREAD_TICKS_PER_GLOBAL_TICKS - 1)
void JVMCICompileState::inc_compilation_ticks() {
if ((++_compilation_ticks & THREAD_TICKS_PER_GLOBAL_TICKS_MASK) == 0) {
_compiler->inc_global_compilation_ticks();
}
}
bool JVMCICompileState::jvmti_state_changed() const {
@ -620,8 +636,8 @@ void JVMCIEnv::fthrow_error(const char* file, int line, const char* format, ...)
JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_compileMethod (JVMCIObject runtime, JVMCIObject method, int entry_bci,
jlong compile_state, int id) {
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
Thread* THREAD = Thread::current();
JavaCallArguments jargs;
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(runtime)));
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(method)));
@ -635,7 +651,7 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_compileMethod (JVMCIObject runtim
vmSymbols::compileMethod_signature(), &jargs, CHECK_(JVMCIObject()));
return wrap((oop) result.get_jobject());
} else {
JNIAccessMark jni(this);
JNIAccessMark jni(this, THREAD);
jobject result = jni()->CallNonvirtualObjectMethod(runtime.as_jobject(),
JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
JNIJVMCI::HotSpotJVMCIRuntime::compileMethod_method(),
@ -648,14 +664,14 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_compileMethod (JVMCIObject runtim
}
void JVMCIEnv::call_HotSpotJVMCIRuntime_bootstrapFinished (JVMCIObject runtime, JVMCIEnv* JVMCIENV) {
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
Thread* THREAD = Thread::current();
JavaCallArguments jargs;
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(runtime)));
JavaValue result(T_VOID);
JavaCalls::call_special(&result, HotSpotJVMCI::HotSpotJVMCIRuntime::klass(), vmSymbols::bootstrapFinished_name(), vmSymbols::void_method_signature(), &jargs, CHECK);
} else {
JNIAccessMark jni(this);
JNIAccessMark jni(this, THREAD);
jni()->CallNonvirtualVoidMethod(runtime.as_jobject(), JNIJVMCI::HotSpotJVMCIRuntime::clazz(), JNIJVMCI::HotSpotJVMCIRuntime::bootstrapFinished_method());
}
@ -681,7 +697,7 @@ void JVMCIEnv::call_HotSpotJVMCIRuntime_shutdown (JVMCIObject runtime) {
}
JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_runtime (JVMCIEnv* JVMCIENV) {
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
JavaCallArguments jargs;
JavaValue result(T_OBJECT);
@ -698,7 +714,7 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_runtime (JVMCIEnv* JVMCIENV) {
}
JVMCIObject JVMCIEnv::call_JVMCI_getRuntime (JVMCIEnv* JVMCIENV) {
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
JavaCallArguments jargs;
JavaValue result(T_OBJECT);
@ -715,7 +731,7 @@ JVMCIObject JVMCIEnv::call_JVMCI_getRuntime (JVMCIEnv* JVMCIENV) {
}
JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_getCompiler (JVMCIObject runtime, JVMCIEnv* JVMCIENV) {
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
JavaCallArguments jargs;
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(runtime)));
@ -734,7 +750,7 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_getCompiler (JVMCIObject runtime,
JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_callToString(JVMCIObject object, JVMCIEnv* JVMCIENV) {
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
JavaCallArguments jargs;
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(object)));
@ -758,7 +774,7 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_callToString(JVMCIObject object,
JVMCIObject JVMCIEnv::call_PrimitiveConstant_forTypeChar(jchar kind, jlong value, JVMCI_TRAPS) {
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
JavaCallArguments jargs;
jargs.push_int(kind);
@ -782,7 +798,7 @@ JVMCIObject JVMCIEnv::call_PrimitiveConstant_forTypeChar(jchar kind, jlong value
}
JVMCIObject JVMCIEnv::call_JavaConstant_forFloat(float value, JVMCI_TRAPS) {
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
JavaCallArguments jargs;
jargs.push_float(value);
@ -805,7 +821,7 @@ JVMCIObject JVMCIEnv::call_JavaConstant_forFloat(float value, JVMCI_TRAPS) {
}
JVMCIObject JVMCIEnv::call_JavaConstant_forDouble(double value, JVMCI_TRAPS) {
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
JavaCallArguments jargs;
jargs.push_double(value);
@ -886,7 +902,7 @@ JVMCIObject JVMCIEnv::new_StackTraceElement(const methodHandle& method, int bci,
}
JVMCIObject JVMCIEnv::new_HotSpotNmethod(const methodHandle& method, const char* name, jboolean isDefault, jlong compileId, JVMCI_TRAPS) {
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
JVMCIObject methodObject = get_jvmci_method(method, JVMCI_CHECK_(JVMCIObject()));
@ -989,7 +1005,7 @@ JVMCIObject JVMCIEnv::get_jvmci_method(const methodHandle& method, JVMCI_TRAPS)
return method_object;
}
Thread* THREAD = Thread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
jmetadata handle = _runtime->allocate_handle(method);
jboolean exception = false;
if (is_hotspot()) {
@ -1005,7 +1021,7 @@ JVMCIObject JVMCIEnv::get_jvmci_method(const methodHandle& method, JVMCI_TRAPS)
method_object = wrap((oop)result.get_jobject());
}
} else {
JNIAccessMark jni(this);
JNIAccessMark jni(this, THREAD);
method_object = JNIJVMCI::wrap(jni()->CallStaticObjectMethod(JNIJVMCI::HotSpotResolvedJavaMethodImpl::clazz(),
JNIJVMCI::HotSpotResolvedJavaMethodImpl_fromMetaspace_method(),
(jlong) handle));
@ -1032,7 +1048,7 @@ JVMCIObject JVMCIEnv::get_jvmci_type(const JVMCIKlassHandle& klass, JVMCI_TRAPS)
}
jlong pointer = (jlong) klass();
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
JVMCIObject signature = create_string(klass->signature_name(), JVMCI_CHECK_(JVMCIObject()));
jboolean exception = false;
if (is_hotspot()) {
@ -1071,7 +1087,7 @@ JVMCIObject JVMCIEnv::get_jvmci_constant_pool(const constantPoolHandle& cp, JVMC
JVMCIObject cp_object;
jmetadata handle = _runtime->allocate_handle(cp);
jboolean exception = false;
JavaThread* THREAD = JavaThread::current();
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current());
if (is_hotspot()) {
JavaValue result(T_OBJECT);
JavaCallArguments args;

@ -94,6 +94,7 @@ class JVMCICompileState : public ResourceObj {
friend class JVMCIVMStructs;
private:
CompileTask* _task;
JVMCICompiler* _compiler;
// Cache JVMTI state. Defined as bytes so that reading them from Java
// via Unsafe is well defined (the C++ type for bool is implementation
@ -113,8 +114,13 @@ class JVMCICompileState : public ResourceObj {
// with the mtJVMCI NMT flag.
bool _failure_reason_on_C_heap;
// A value indicating compilation activity during the compilation.
// If successive calls to this method return a different value, then
// some degree of JVMCI compilation occurred between the calls.
jint _compilation_ticks;
public:
JVMCICompileState(CompileTask* task);
JVMCICompileState(CompileTask* task, JVMCICompiler* compiler);
CompileTask* task() { return _task; }
@ -135,6 +141,9 @@ class JVMCICompileState : public ResourceObj {
_failure_reason_on_C_heap = reason_on_C_heap;
_retryable = retryable;
}
jint compilation_ticks() const { return _compilation_ticks; }
void inc_compilation_ticks();
};
@ -284,7 +293,7 @@ public:
JVMCIPrimitiveArray wrap(typeArrayOop obj) { assert(is_hotspot(), "must be"); return (JVMCIPrimitiveArray) wrap(JNIHandles::make_local(obj)); }
public:
// Compiles a method with the JVMIC compiler.
// Compiles a method with the JVMCI compiler.
// Caller must handle pending exception.
JVMCIObject call_HotSpotJVMCIRuntime_compileMethod(JVMCIObject runtime, JVMCIObject method, int entry_bci,
jlong compile_state, int id);

@ -163,6 +163,7 @@
nonstatic_field(JVMCICompileState, _jvmti_can_access_local_variables, jbyte) \
nonstatic_field(JVMCICompileState, _jvmti_can_post_on_exceptions, jbyte) \
nonstatic_field(JVMCICompileState, _jvmti_can_pop_frame, jbyte) \
nonstatic_field(JVMCICompileState, _compilation_ticks, jint) \
\
nonstatic_field(JavaThread, _threadObj, OopHandle) \
nonstatic_field(JavaThread, _anchor, JavaFrameAnchor) \