8242440: use separate, destroyable JavaVM instances per libgraal compiler thread

Reviewed-by: kvn, thartmann
This commit is contained in:
Doug Simon 2022-04-23 06:08:11 +00:00
parent b10833bbf3
commit 357b1b18c2
23 changed files with 1016 additions and 262 deletions

View File

@ -165,6 +165,18 @@ class AbstractCompiler : public CHeapObj<mtCompiler> {
ShouldNotReachHere();
}
// Notifies this compiler that the current thread (`current`) is about to stop.
// The current thread currently holds the CompileThread_lock.
virtual void stopping_compiler_thread(CompilerThread* current) {
// Do nothing
}
// Notifies this compiler that queue is empty just prior to waiting on
// MethodCompileQueue_lock which is held by the current thread (`thread`).
virtual void on_empty_queue(CompileQueue* queue, CompilerThread* thread) {
// Do nothing
}
// Print compilation timers and statistics
virtual void print_timers() {
ShouldNotReachHere();

View File

@ -422,7 +422,7 @@ void CompileQueue::free_all() {
/**
* Get the next CompileTask from a CompileQueue
*/
CompileTask* CompileQueue::get() {
CompileTask* CompileQueue::get(CompilerThread* thread) {
// save methods from RedefineClasses across safepoint
// across MethodCompileQueue_lock below.
methodHandle save_method;
@ -440,6 +440,10 @@ CompileTask* CompileQueue::get() {
return NULL;
}
AbstractCompiler* compiler = thread->compiler();
guarantee(compiler != nullptr, "Compiler object must exist");
compiler->on_empty_queue(this, thread);
// If there are no compilation tasks and we can compile new jobs
// (i.e., there is enough free space in the code cache) there is
// no need to invoke the sweeper. As a result, the hotness of methods
@ -1932,7 +1936,7 @@ void CompileBroker::compiler_thread_loop() {
// We need this HandleMark to avoid leaking VM handles.
HandleMark hm(thread);
CompileTask* task = queue->get();
CompileTask* task = queue->get(thread);
if (task == NULL) {
if (UseDynamicNumberOfCompilerThreads) {
// Access compiler_count under lock to enforce consistency.
@ -1942,6 +1946,10 @@ void CompileBroker::compiler_thread_loop() {
tty->print_cr("Removing compiler thread %s after " JLONG_FORMAT " ms idle time",
thread->name(), thread->idle_time_millis());
}
// Notify compiler that the compiler thread is about to stop
thread->compiler()->stopping_compiler_thread(thread);
// Free buffer blob, if allocated
if (thread->get_buffer_blob() != NULL) {
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
@ -2250,6 +2258,9 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) {
post_compilation_event(event, task);
}
if (runtime != nullptr) {
runtime->post_compile(thread);
}
} else
#endif // INCLUDE_JVMCI
{

View File

@ -108,7 +108,7 @@ class CompileQueue : public CHeapObj<mtCompiler> {
CompileTask* first() { return _first; }
CompileTask* last() { return _last; }
CompileTask* get();
CompileTask* get(CompilerThread* thread);
bool is_empty() const { return _first == NULL; }
int size() const { return _size; }

View File

@ -37,18 +37,19 @@
#include "runtime/atomic.hpp"
#include "utilities/events.hpp"
JVMCIRuntime* JVMCI::_compiler_runtime = NULL;
JVMCIRuntime* JVMCI::_java_runtime = NULL;
JVMCIRuntime* JVMCI::_compiler_runtimes = nullptr;
JVMCIRuntime* JVMCI::_java_runtime = nullptr;
JVMCIRuntime* JVMCI::_shutdown_compiler_runtime = nullptr;
volatile bool JVMCI::_is_initialized = false;
bool JVMCI::_box_caches_initialized = false;
void* JVMCI::_shared_library_handle = NULL;
char* JVMCI::_shared_library_path = NULL;
void* JVMCI::_shared_library_handle = nullptr;
char* JVMCI::_shared_library_path = nullptr;
volatile bool JVMCI::_in_shutdown = false;
StringEventLog* JVMCI::_events = NULL;
StringEventLog* JVMCI::_verbose_events = NULL;
StringEventLog* JVMCI::_events = nullptr;
StringEventLog* JVMCI::_verbose_events = nullptr;
volatile intx JVMCI::_fatal_log_init_thread = -1;
volatile int JVMCI::_fatal_log_fd = -1;
const char* JVMCI::_fatal_log_filename = NULL;
const char* JVMCI::_fatal_log_filename = nullptr;
void jvmci_vmStructs_init() NOT_DEBUG_RETURN;
@ -70,7 +71,7 @@ void* JVMCI::get_shared_library(char*& path, bool load) {
path = _shared_library_path;
return sl_handle;
}
assert(JVMCI_lock->owner() == Thread::current(), "must be");
MutexLocker locker(JVMCI_lock);
path = NULL;
if (_shared_library_handle == NULL) {
char path[JVM_MAXPATHLEN];
@ -103,8 +104,13 @@ void JVMCI::initialize_compiler(TRAPS) {
JNIJVMCI::initialize_ids(NULL);
ShouldNotReachHere();
}
JVMCI::compiler_runtime()->call_getCompiler(CHECK);
JVMCIRuntime* runtime;
if (UseJVMCINativeLibrary) {
runtime = JVMCI::compiler_runtime((JavaThread*) THREAD);
} else {
runtime = JVMCI::java_runtime();
}
runtime->call_getCompiler(CHECK);
}
void JVMCI::initialize_globals() {
@ -122,13 +128,9 @@ void JVMCI::initialize_globals() {
}
}
}
if (UseJVMCINativeLibrary) {
// There are two runtimes.
_compiler_runtime = new JVMCIRuntime(0);
_java_runtime = new JVMCIRuntime(-1);
} else {
// There is only a single runtime
_java_runtime = _compiler_runtime = new JVMCIRuntime(0);
_java_runtime = new JVMCIRuntime(nullptr, -1, false);
if (using_singleton_shared_library_runtime()) {
JVMCI::_compiler_runtimes = new JVMCIRuntime(nullptr, 0, true);
}
}
@ -158,6 +160,16 @@ void JVMCI::ensure_box_caches_initialized(TRAPS) {
_box_caches_initialized = true;
}
JVMCIRuntime* JVMCI::compiler_runtime(JavaThread* thread, bool create) {
assert(thread->is_Java_thread(), "must be") ;
assert(UseJVMCINativeLibrary, "must be");
JVMCIRuntime* runtime = thread->libjvmci_runtime();
if (runtime == nullptr && create) {
runtime = JVMCIRuntime::for_thread(thread);
}
return runtime;
}
JavaThread* JVMCI::compilation_tick(JavaThread* thread) {
if (thread->is_Compiler_thread()) {
CompileTask *task = CompilerThread::cast(thread)->task();
@ -172,21 +184,27 @@ JavaThread* JVMCI::compilation_tick(JavaThread* thread) {
}
void JVMCI::metadata_do(void f(Metadata*)) {
if (_java_runtime != NULL) {
if (_java_runtime != nullptr) {
_java_runtime->_metadata_handles->metadata_do(f);
}
if (_compiler_runtime != NULL && _compiler_runtime != _java_runtime) {
_compiler_runtime->_metadata_handles->metadata_do(f);
for (JVMCIRuntime* runtime = _compiler_runtimes; runtime != nullptr; runtime = runtime->_next) {
runtime->_metadata_handles->metadata_do(f);
}
if (_shutdown_compiler_runtime != nullptr) {
_shutdown_compiler_runtime->_metadata_handles->metadata_do(f);
}
}
void JVMCI::do_unloading(bool unloading_occurred) {
if (unloading_occurred) {
if (_java_runtime != NULL) {
if (_java_runtime != nullptr) {
_java_runtime->_metadata_handles->do_unloading();
}
if (_compiler_runtime != NULL && _compiler_runtime != _java_runtime) {
_compiler_runtime->_metadata_handles->do_unloading();
for (JVMCIRuntime* runtime = _compiler_runtimes; runtime != nullptr; runtime = runtime->_next) {
runtime->_metadata_handles->do_unloading();
}
if (_shutdown_compiler_runtime != nullptr) {
_shutdown_compiler_runtime->_metadata_handles->do_unloading();
}
}
}
@ -195,7 +213,44 @@ bool JVMCI::is_compiler_initialized() {
return _is_initialized;
}
void JVMCI::shutdown() {
void JVMCI::vlog(int level, const char* format, va_list ap) {
if (LogEvents && JVMCIEventLogLevel >= level) {
StringEventLog* events = level == 1 ? _events : _verbose_events;
guarantee(events != nullptr, "JVMCI event log not yet initialized");
Thread* thread = Thread::current_or_null_safe();
if (thread != nullptr) {
events->logv(thread, format, ap);
}
}
}
void JVMCI::vtrace(int level, const char* format, va_list ap) {
if (JVMCITraceLevel >= level) {
Thread* thread = Thread::current_or_null_safe();
if (thread != nullptr) {
ResourceMark rm;
tty->print("JVMCITrace-%d[%s]:%*c", level, thread->name(), level, ' ');
} else {
tty->print("JVMCITrace-%d[?]:%*c", level, level, ' ');
}
tty->vprint_cr(format, ap);
}
}
#define LOG_TRACE(level) { va_list ap; \
va_start(ap, format); vlog(level, format, ap); va_end(ap); \
va_start(ap, format); vtrace(level, format, ap); va_end(ap); \
}
void JVMCI::event(int level, const char* format, ...) LOG_TRACE(level)
void JVMCI::event1(const char* format, ...) LOG_TRACE(1)
void JVMCI::event2(const char* format, ...) LOG_TRACE(2)
void JVMCI::event3(const char* format, ...) LOG_TRACE(3)
void JVMCI::event4(const char* format, ...) LOG_TRACE(4)
#undef LOG_TRACE
void JVMCI::shutdown(JavaThread* thread) {
ResourceMark rm;
{
MutexLocker locker(JVMCI_lock);
@ -203,11 +258,29 @@ void JVMCI::shutdown() {
JVMCI_event_1("shutting down JVMCI");
}
JVMCIRuntime* java_runtime = _java_runtime;
if (java_runtime != compiler_runtime()) {
if (java_runtime != nullptr) {
java_runtime->shutdown();
}
if (compiler_runtime() != NULL) {
compiler_runtime()->shutdown();
JVMCIRuntime* runtime = thread->libjvmci_runtime();
if (runtime != nullptr) {
runtime->detach_thread(thread, "JVMCI shutdown");
}
{
// Attach to JVMCI initialized runtimes that are not already shutting down
// and shut them down. This ensures HotSpotJVMCIRuntime.shutdown() is called
// for each JVMCI runtime.
MutexLocker locker(JVMCI_lock);
for (JVMCIRuntime* rt = JVMCI::_compiler_runtimes; rt != nullptr; rt = rt->_next) {
if (rt->is_HotSpotJVMCIRuntime_initialized() && rt->_num_attached_threads != JVMCIRuntime::cannot_be_attached) {
rt->_num_attached_threads++;
{
MutexUnlocker unlocker(JVMCI_lock);
rt->attach_thread(thread);
rt->shutdown();
rt->detach_thread(thread, "JVMCI shutdown");
}
}
}
}
}
@ -253,40 +326,3 @@ void JVMCI::fatal_log(const char* buf, size_t count) {
log.write(buf, count);
log.flush();
}
void JVMCI::vlog(int level, const char* format, va_list ap) {
if (LogEvents && JVMCIEventLogLevel >= level) {
StringEventLog* events = level == 1 ? _events : _verbose_events;
guarantee(events != NULL, "JVMCI event log not yet initialized");
Thread* thread = Thread::current_or_null_safe();
if (thread != NULL) {
events->logv(thread, format, ap);
}
}
}
void JVMCI::vtrace(int level, const char* format, va_list ap) {
if (JVMCITraceLevel >= level) {
Thread* thread = Thread::current_or_null_safe();
if (thread != NULL) {
ResourceMark rm;
tty->print("JVMCITrace-%d[%s]:%*c", level, thread->name(), level, ' ');
} else {
tty->print("JVMCITrace-%d[?]:%*c", level, level, ' ');
}
tty->vprint_cr(format, ap);
}
}
#define LOG_TRACE(level) { va_list ap; \
va_start(ap, format); vlog(level, format, ap); va_end(ap); \
va_start(ap, format); vtrace(level, format, ap); va_end(ap); \
}
void JVMCI::event(int level, const char* format, ...) LOG_TRACE(level)
void JVMCI::event1(const char* format, ...) LOG_TRACE(1)
void JVMCI::event2(const char* format, ...) LOG_TRACE(2)
void JVMCI::event3(const char* format, ...) LOG_TRACE(3)
void JVMCI::event4(const char* format, ...) LOG_TRACE(4)
#undef LOG_TRACE

View File

@ -51,8 +51,15 @@ class JVMCI : public AllStatic {
friend class JVMCIEnv;
private:
// Access to the HotSpotJVMCIRuntime used by the CompileBroker.
static JVMCIRuntime* _compiler_runtime;
// List of libjvmci based JVMCIRuntimes.
// Should only be accessed under JVMCI_lock.
static JVMCIRuntime* _compiler_runtimes;
// Special libjvmci based JVMCIRuntime reserved for
// threads trying to attach when in JVMCI shutdown.
// This preserves the invariant that JVMCIRuntime::for_thread()
// never returns nullptr.
static JVMCIRuntime* _shutdown_compiler_runtime;
// True when at least one JVMCIRuntime::initialize_HotSpotJVMCIRuntime()
// execution has completed successfully.
@ -95,6 +102,7 @@ class JVMCI : public AllStatic {
static Thread* current_thread_or_null();
public:
enum CodeInstallResult {
ok,
dependencies_failed,
@ -104,10 +112,19 @@ class JVMCI : public AllStatic {
first_permanent_bailout = code_too_large
};
// Returns true iff JVMCIThreadsPerNativeLibraryRuntime == 0.
static bool using_singleton_shared_library_runtime() {
return JVMCIThreadsPerNativeLibraryRuntime == 0;
}
// Returns true iff there is a new shared library JavaVM per compilation.
static bool one_shared_library_javavm_per_compilation() {
return JVMCIThreadsPerNativeLibraryRuntime == 1 && JVMCICompilerIdleDelay == 0;
}
// Gets the handle to the loaded JVMCI shared library, loading it
// first if not yet loaded and `load` is true. The path from
// which the library is loaded is returned in `path`. If
// `load` is true then JVMCI_lock must be locked.
// which the library is loaded is returned in `path`.
static void* get_shared_library(char*& path, bool load);
// Logs the fatal crash data in `buf` to the appropriate stream.
@ -121,7 +138,7 @@ class JVMCI : public AllStatic {
static void metadata_do(void f(Metadata*));
static void shutdown();
static void shutdown(JavaThread* thread);
// Returns whether JVMCI::shutdown has been called.
static bool in_shutdown();
@ -145,11 +162,16 @@ class JVMCI : public AllStatic {
// 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.
static JVMCIRuntime* java_runtime() { return _java_runtime; }
// Gets the JVMCI shared library runtime associated with `thread`.
// This must only be called when UseJVMCINativeLibrary is true.
// If `create` is true and there is no runtime currently associated with
// `thread`, this method creates one.
static JVMCIRuntime* compiler_runtime(JavaThread* thread, bool create=true);
// Appends an event to the JVMCI event log if JVMCIEventLogLevel >= `level`
static void vlog(int level, const char* format, va_list ap) ATTRIBUTE_PRINTF(2, 0);

View File

@ -153,6 +153,36 @@ void JVMCICompiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci,
ShouldNotReachHere();
}
void JVMCICompiler::stopping_compiler_thread(CompilerThread* current) {
if (UseJVMCINativeLibrary) {
JVMCIRuntime* runtime = JVMCI::compiler_runtime(current, false);
if (runtime != nullptr) {
MutexUnlocker unlock(CompileThread_lock);
runtime->detach_thread(current, "stopping idle compiler thread");
}
}
}
void JVMCICompiler::on_empty_queue(CompileQueue* queue, CompilerThread* thread) {
if (UseJVMCINativeLibrary) {
int delay = JVMCICompilerIdleDelay;
JVMCIRuntime* runtime = JVMCI::compiler_runtime(thread, false);
// Don't detach JVMCI compiler threads from their JVMCI
// runtime during the VM startup grace period
if (runtime != nullptr && delay > 0 && tty->time_stamp().milliseconds() > DEFAULT_COMPILER_IDLE_DELAY) {
bool timeout = MethodCompileQueue_lock->wait(delay);
// Unlock as detaching or repacking can result in a JNI call to shutdown a JavaVM
// and locks cannot be held when making a VM to native transition.
MutexUnlocker unlock(MethodCompileQueue_lock);
if (timeout) {
runtime->detach_thread(thread, "releasing idle compiler thread");
} else {
runtime->repack(thread);
}
}
}
}
// Print CompileBroker compilation timers
void JVMCICompiler::print_timers() {
double code_install_time = _codeInstallTimer.seconds();

View File

@ -94,6 +94,10 @@ public:
// Compilation entry point for methods
virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci, bool install_code, DirectiveSet* directive);
virtual void stopping_compiler_thread(CompilerThread* current);
virtual void on_empty_queue(CompileQueue* queue, CompilerThread* thread);
// Print compilation timers and statistics
virtual void print_timers();

View File

@ -1089,7 +1089,6 @@ C2V_VMENTRY_NULL(jlongArray, getLineNumberTable, (JNIEnv* env, jobject, jobject
int i = 0;
jlong value;
while (stream.read_pair()) {
// FIXME: Why was this long before?
value = ((jlong) stream.bci());
JVMCIENV->put_long_at(result, i, value);
value = ((jlong) stream.line());
@ -2134,7 +2133,7 @@ C2V_VMENTRY_0(jboolean, equals, (JNIEnv* env, jobject, jobject x, jlong xHandle,
if (x == NULL || y == NULL) {
JVMCI_THROW_0(NullPointerException);
}
return JVMCIENV->resolve_handle(xHandle) == JVMCIENV->resolve_handle(yHandle);
return JVMCIENV->resolve_oop_handle(xHandle) == JVMCIENV->resolve_oop_handle(yHandle);
C2V_END
C2V_VMENTRY_NULL(jobject, getJavaMirror, (JNIEnv* env, jobject, jobject object))
@ -2224,10 +2223,9 @@ C2V_VMENTRY_0(jint, arrayIndexScale, (JNIEnv* env, jobject, jobject kind))
return type2aelembytes(type);
C2V_END
C2V_VMENTRY(void, deleteGlobalHandle, (JNIEnv* env, jobject, jlong h))
jobject handle = (jobject)(address)h;
if (handle != NULL) {
JVMCIENV->runtime()->destroy_global(handle);
C2V_VMENTRY(void, deleteGlobalHandle, (JNIEnv* env, jobject, jlong handle))
if (handle != 0) {
JVMCIENV->runtime()->destroy_oop_handle(handle);
}
}
@ -2242,13 +2240,13 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas
requireInHotSpot("registerNativeMethods", JVMCI_CHECK_NULL);
char* sl_path;
void* sl_handle;
JVMCIRuntime* runtime = JVMCI::compiler_runtime();
JVMCIRuntime* runtime;
{
// Ensure the JVMCI shared library runtime is initialized.
JVMCIEnv __peer_jvmci_env__(thread, false, __FILE__, __LINE__);
JVMCIEnv* peerEnv = &__peer_jvmci_env__;
HandleMark hm(THREAD);
JVMCIObject receiver = runtime->get_HotSpotJVMCIRuntime(peerEnv);
runtime = JVMCI::compiler_runtime(thread);
if (peerEnv->has_pending_exception()) {
peerEnv->describe_pending_exception(true);
}
@ -2323,7 +2321,7 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas
}
C2V_VMENTRY_PREFIX(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject c2vm))
if (thread == NULL) {
if (thread == nullptr || thread->libjvmci_runtime() == nullptr) {
// Called from unattached JVMCI shared library thread
return false;
}
@ -2331,8 +2329,8 @@ C2V_VMENTRY_PREFIX(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject c2vm
if (thread->jni_environment() == env) {
C2V_BLOCK(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject))
requireJVMCINativeLibrary(JVMCI_CHECK_0);
JVMCIRuntime* runtime = JVMCI::compiler_runtime();
if (runtime == NULL || !runtime->has_shared_library_javavm()) {
JVMCIRuntime* runtime = thread->libjvmci_runtime();
if (runtime == nullptr || !runtime->has_shared_library_javavm()) {
JVMCI_THROW_MSG_0(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in isCurrentThreadAttached");
}
JNIEnv* peerEnv;
@ -2350,28 +2348,46 @@ C2V_VMENTRY_PREFIX(jlong, getCurrentJavaThread, (JNIEnv* env, jobject c2vm))
return (jlong) p2i(thread);
C2V_END
C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jbyteArray name, jboolean as_daemon))
if (thread == NULL) {
// Called from unattached JVMCI shared library thread
guarantee(name != NULL, "libjvmci caller must pass non-null name");
// Attaches a thread started in a JVMCI shared library to a JavaThread and JVMCI runtime.
static void attachSharedLibraryThread(JNIEnv* env, jbyteArray name, jboolean as_daemon) {
JavaVM* javaVM = nullptr;
jint res = env->GetJavaVM(&javaVM);
if (res != JNI_OK) {
JNI_THROW("attachSharedLibraryThread", InternalError, err_msg("Error getting shared library JavaVM from shared library JNIEnv: %d", res));
}
extern struct JavaVM_ main_vm;
JNIEnv* hotspotEnv;
extern struct JavaVM_ main_vm;
JNIEnv* hotspotEnv;
int name_len = env->GetArrayLength(name);
char name_buf[64]; // Cannot use Resource heap as it requires a current thread
int to_copy = MIN2(name_len, (int) sizeof(name_buf) - 1);
env->GetByteArrayRegion(name, 0, to_copy, (jbyte*) name_buf);
name_buf[to_copy] = '\0';
JavaVMAttachArgs attach_args;
attach_args.version = JNI_VERSION_1_2;
attach_args.name = name_buf;
attach_args.group = nullptr;
res = as_daemon ? main_vm.AttachCurrentThreadAsDaemon((void**)&hotspotEnv, &attach_args) :
main_vm.AttachCurrentThread((void**)&hotspotEnv, &attach_args);
if (res != JNI_OK) {
JNI_THROW("attachSharedLibraryThread", InternalError, err_msg("Trying to attach thread returned %d", res));
}
JavaThread* thread = get_current_thread(false);
const char* attach_error;
{
// Transition to VM
JVMCI_VM_ENTRY_MARK
attach_error = JVMCIRuntime::attach_shared_library_thread(thread, javaVM);
// Transition back to Native
}
if (attach_error != nullptr) {
JNI_THROW("attachCurrentThread", InternalError, attach_error);
}
}
int name_len = env->GetArrayLength(name);
char name_buf[64]; // Cannot use Resource heap as it requires a current thread
int to_copy = MIN2(name_len, (int) sizeof(name_buf) - 1);
env->GetByteArrayRegion(name, 0, to_copy, (jbyte*) name_buf);
name_buf[to_copy] = '\0';
JavaVMAttachArgs attach_args;
attach_args.version = JNI_VERSION_1_2;
attach_args.name = name_buf;
attach_args.group = NULL;
jint res = as_daemon ? main_vm.AttachCurrentThreadAsDaemon((void**) &hotspotEnv, &attach_args) :
main_vm.AttachCurrentThread((void**) &hotspotEnv, &attach_args);
if (res != JNI_OK) {
JNI_THROW_("attachCurrentThread", InternalError, err_msg("Trying to attach thread returned %d", res), false);
}
C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jbyteArray name, jboolean as_daemon, jlongArray javaVM_info))
if (thread == nullptr) {
attachSharedLibraryThread(env, name, as_daemon);
return true;
}
JVMCITraceMark jtm("attachCurrentThread");
@ -2379,25 +2395,43 @@ C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jb
// Called from HotSpot
C2V_BLOCK(jboolean, attachCurrentThread, (JNIEnv* env, jobject, jboolean))
requireJVMCINativeLibrary(JVMCI_CHECK_0);
JVMCIRuntime* runtime = JVMCI::compiler_runtime();
if (runtime == NULL || !runtime->has_shared_library_javavm()) {
JVMCI_THROW_MSG_0(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in attachCurrentThread");
JVMCIRuntime* runtime = JVMCI::compiler_runtime(thread);
JNIEnv* peerJNIEnv;
if (runtime->has_shared_library_javavm()) {
if (runtime->GetEnv(thread, (void**)&peerJNIEnv, JNI_VERSION_1_2) == JNI_OK) {
// Already attached
runtime->init_JavaVM_info(javaVM_info, JVMCI_CHECK_0);
return false;
}
}
{
// Ensure the JVMCI shared library runtime is initialized.
JVMCIEnv __peer_jvmci_env__(thread, false, __FILE__, __LINE__);
JVMCIEnv* peerJVMCIEnv = &__peer_jvmci_env__;
HandleMark hm(thread);
JVMCIObject receiver = runtime->get_HotSpotJVMCIRuntime(peerJVMCIEnv);
if (peerJVMCIEnv->has_pending_exception()) {
peerJVMCIEnv->describe_pending_exception(true);
}
char* sl_path;
if (JVMCI::get_shared_library(sl_path, false) == nullptr) {
JVMCI_THROW_MSG_0(InternalError, "Error initializing JVMCI runtime");
}
}
JavaVMAttachArgs attach_args;
attach_args.version = JNI_VERSION_1_2;
attach_args.name = const_cast<char*>(thread->name());
attach_args.group = NULL;
JNIEnv* peerJNIEnv;
if (runtime->GetEnv(thread, (void**) &peerJNIEnv, JNI_VERSION_1_2) == JNI_OK) {
return false;
}
attach_args.group = nullptr;
jint res = as_daemon ? runtime->AttachCurrentThreadAsDaemon(thread, (void**) &peerJNIEnv, &attach_args) :
runtime->AttachCurrentThread(thread, (void**) &peerJNIEnv, &attach_args);
if (res == JNI_OK) {
guarantee(peerJNIEnv != NULL, "must be");
JVMCI_event_1("attached to JavaVM for JVMCI runtime %d", runtime->id());
guarantee(peerJNIEnv != nullptr, "must be");
runtime->init_JavaVM_info(javaVM_info, JVMCI_CHECK_0);
JVMCI_event_1("attached to JavaVM[%d] for JVMCI runtime %d", runtime->get_shared_library_javavm_id(), runtime->id());
return true;
}
JVMCI_THROW_MSG_0(InternalError, err_msg("Error %d while attaching %s", res, attach_args.name));
@ -2406,37 +2440,59 @@ C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jb
return false;
C2V_END
C2V_VMENTRY_PREFIX(void, detachCurrentThread, (JNIEnv* env, jobject c2vm))
if (thread == NULL) {
C2V_VMENTRY_PREFIX(jboolean, detachCurrentThread, (JNIEnv* env, jobject c2vm, jboolean release))
if (thread == nullptr) {
// Called from unattached JVMCI shared library thread
JNI_THROW("detachCurrentThread", IllegalStateException, "Cannot detach non-attached thread");
JNI_THROW_("detachCurrentThread", IllegalStateException, "Cannot detach non-attached thread", false);
}
JVMCITraceMark jtm("detachCurrentThread");
if (thread->jni_environment() == env) {
// Called from HotSpot
C2V_BLOCK(void, detachCurrentThread, (JNIEnv* env, jobject))
requireJVMCINativeLibrary(JVMCI_CHECK);
requireInHotSpot("detachCurrentThread", JVMCI_CHECK);
JVMCIRuntime* runtime = JVMCI::compiler_runtime();
if (runtime == NULL || !runtime->has_shared_library_javavm()) {
JVMCI_THROW_MSG(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in detachCurrentThread");
requireJVMCINativeLibrary(JVMCI_CHECK_0);
requireInHotSpot("detachCurrentThread", JVMCI_CHECK_0);
JVMCIRuntime* runtime = thread->libjvmci_runtime();
if (runtime == nullptr || !runtime->has_shared_library_javavm()) {
JVMCI_THROW_MSG_0(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in detachCurrentThread");
}
JNIEnv* peerJNIEnv;
if (runtime->GetEnv(thread, (void**) &peerJNIEnv, JNI_VERSION_1_2) != JNI_OK) {
JVMCI_THROW_MSG(IllegalStateException, err_msg("Cannot detach non-attached thread: %s", thread->name()));
JNIEnv* peerEnv;
if (runtime->GetEnv(thread, (void**) &peerEnv, JNI_VERSION_1_2) != JNI_OK) {
JVMCI_THROW_MSG_0(IllegalStateException, err_msg("Cannot detach non-attached thread: %s", thread->name()));
}
jint res = runtime->DetachCurrentThread(thread);
if (res != JNI_OK) {
JVMCI_THROW_MSG(InternalError, err_msg("Error %d while attaching %s", res, thread->name()));
JVMCI_THROW_MSG_0(InternalError, err_msg("Error %d while attaching %s", res, thread->name()));
}
JVMCI_event_1("detached from JavaVM[%d] for JVMCI runtime %d",
runtime->get_shared_library_javavm_id(), runtime->id());
if (release) {
return runtime->detach_thread(thread, "user thread detach");
}
} else {
// Called from attached JVMCI shared library thread
if (release) {
JNI_THROW_("detachCurrentThread", InternalError, "JVMCI shared library thread cannot release JVMCI shared library JavaVM", false);
}
JVMCIRuntime* runtime = thread->libjvmci_runtime();
if (runtime == nullptr) {
JNI_THROW_("detachCurrentThread", InternalError, "JVMCI shared library thread should have a JVMCI runtime", false);
}
{
// Transition to VM
C2V_BLOCK(jboolean, detachCurrentThread, (JNIEnv* env, jobject))
// Cannot destroy shared library JavaVM as we're about to return to it.
runtime->detach_thread(thread, "shared library thread detach", false);
JVMCI_event_1("detaching JVMCI shared library thread from HotSpot JavaVM");
// Transition back to Native
}
extern struct JavaVM_ main_vm;
jint res = main_vm.DetachCurrentThread();
if (res != JNI_OK) {
JNI_THROW("detachCurrentThread", InternalError, "Cannot detach non-attached thread");
JNI_THROW_("detachCurrentThread", InternalError, "Cannot detach non-attached thread", false);
}
}
return false;
C2V_END
C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jboolean callPostTranslation))
@ -2531,7 +2587,7 @@ C2V_VMENTRY_NULL(jobject, unhand, (JNIEnv* env, jobject, jlong obj_handle))
return NULL;
}
jobject global_handle = (jobject) obj_handle;
JVMCIObject global_handle_obj = JVMCIENV->wrap((jobject) obj_handle);
JVMCIObject global_handle_obj = JVMCIENV->wrap(global_handle);
jobject result = JVMCIENV->make_local(global_handle_obj).as_jobject();
JVMCIENV->destroy_global(global_handle_obj);
@ -2858,8 +2914,8 @@ JNINativeMethod CompilerToVM::methods[] = {
{CC "registerNativeMethods", CC "(" CLASS ")[J", FN_PTR(registerNativeMethods)},
{CC "isCurrentThreadAttached", CC "()Z", FN_PTR(isCurrentThreadAttached)},
{CC "getCurrentJavaThread", CC "()J", FN_PTR(getCurrentJavaThread)},
{CC "attachCurrentThread", CC "([BZ)Z", FN_PTR(attachCurrentThread)},
{CC "detachCurrentThread", CC "()V", FN_PTR(detachCurrentThread)},
{CC "attachCurrentThread", CC "([BZ[J)Z", FN_PTR(attachCurrentThread)},
{CC "detachCurrentThread", CC "(Z)Z", FN_PTR(detachCurrentThread)},
{CC "translate", CC "(" OBJECT "Z)J", FN_PTR(translate)},
{CC "unhand", CC "(J)" OBJECT, FN_PTR(unhand)},
{CC "updateHotSpotNmethod", CC "(" HS_NMETHOD ")V", FN_PTR(updateHotSpotNmethod)},

View File

@ -176,7 +176,7 @@ void JVMCIEnv::init_env_mode_runtime(JavaThread* thread, JNIEnv* parent_env) {
_runtime = JVMCI::java_runtime();
return;
}
_runtime = JVMCI::compiler_runtime();
_runtime = thread->libjvmci_runtime();
assert(_runtime != NULL, "npe");
_env = parent_env;
return;
@ -186,7 +186,7 @@ void JVMCIEnv::init_env_mode_runtime(JavaThread* thread, JNIEnv* parent_env) {
// is loaded and initialized and get a shared library JNIEnv
_is_hotspot = false;
_runtime = JVMCI::compiler_runtime();
_runtime = JVMCI::compiler_runtime(thread);
_env = _runtime->init_shared_library_javavm();
if (_env != NULL) {
@ -1383,7 +1383,7 @@ JVMCIObject JVMCIEnv::get_object_constant(oop objOop, bool compressed, bool dont
HotSpotJVMCI::HotSpotObjectConstantImpl::set_compressed(this, constant, compressed);
return wrap(constant);
} else {
jlong handle = make_handle(obj);
jlong handle = make_oop_handle(obj);
JNIAccessMark jni(this, THREAD);
jobject result = jni()->NewObject(JNIJVMCI::IndirectHotSpotObjectConstantImpl::clazz(),
JNIJVMCI::IndirectHotSpotObjectConstantImpl::constructor(),
@ -1407,7 +1407,7 @@ Handle JVMCIEnv::asConstant(JVMCIObject constant, JVMCI_TRAPS) {
if (object_handle == 0L) {
JVMCI_THROW_MSG_(NullPointerException, "Foreign object reference has been cleared", Handle());
}
oop result = resolve_handle(object_handle);
oop result = resolve_oop_handle(object_handle);
if (result == NULL) {
JVMCI_THROW_MSG_(InternalError, "Constant was unexpectedly NULL", Handle());
}
@ -1421,15 +1421,14 @@ JVMCIObject JVMCIEnv::wrap(jobject object) {
return JVMCIObject::create(object, is_hotspot());
}
jlong JVMCIEnv::make_handle(const Handle& obj) {
jlong JVMCIEnv::make_oop_handle(const Handle& obj) {
assert(!obj.is_null(), "should only create handle for non-NULL oops");
jobject handle = _runtime->make_global(obj);
return (jlong) handle;
return _runtime->make_oop_handle(obj);
}
oop JVMCIEnv::resolve_handle(jlong objectHandle) {
assert(objectHandle != 0, "should be a valid handle");
oop obj = *((oopDesc**)objectHandle);
oop JVMCIEnv::resolve_oop_handle(jlong oopHandle) {
assert(oopHandle != 0, "should be a valid handle");
oop obj = *((oopDesc**) oopHandle);
if (obj != NULL) {
oopDesc::verify(obj);
}

View File

@ -392,8 +392,11 @@ public:
JVMCIObject new_HotSpotStackFrameReference(JVMCI_TRAPS);
JVMCIObject new_JVMCIError(JVMCI_TRAPS);
jlong make_handle(const Handle& obj);
oop resolve_handle(jlong objectHandle);
// Makes a handle to a HotSpot heap object. These handles are
// individually reclaimed by JVMCIRuntime::destroy_oop_handle and
// bulk reclaimed by JVMCIRuntime::release_and_clear_globals.
jlong make_oop_handle(const Handle& obj);
oop resolve_oop_handle(jlong oopHandle);
// These are analogous to the JNI routines
JVMCIObject make_local(JVMCIObject object);

View File

@ -80,14 +80,19 @@ void HotSpotJVMCI::compute_offset(int &dest_offset, Klass* klass, const char* na
fatal("Could not find field %s.%s with signature %s", ik->external_name(), name, signature);
}
guarantee(fd.is_static() == static_field, "static/instance mismatch");
dest_offset = fd.offset();
assert(dest_offset != 0, "must be valid offset");
if (static_field) {
// Must ensure classes for static fields are initialized as the
// accessor itself does not include a class initialization check.
ik->initialize(CHECK);
assert(fd.offset() != 0, "must be valid offset");
if (dest_offset != fd.offset()) {
if (dest_offset != 0) {
fatal("offset for %s %s.%s re-initialized: %d -> %d", signature, ik->external_name(), name, dest_offset, fd.offset());
}
dest_offset = fd.offset();
if (static_field) {
// Must ensure classes for static fields are initialized as the
// accessor itself does not include a class initialization check.
ik->initialize(CHECK);
}
JVMCI_event_2(" field offset for %s %s.%s = %d", signature, ik->external_name(), name, dest_offset);
}
JVMCI_event_2(" field offset for %s %s.%s = %d", signature, ik->external_name(), name, dest_offset);
}
#ifndef PRODUCT
@ -120,11 +125,18 @@ jmethodID JNIJVMCI::_HotSpotConstantPool_fromMetaspace_method;
jmethodID JNIJVMCI::_HotSpotResolvedObjectTypeImpl_fromMetaspace_method;
jmethodID JNIJVMCI::_HotSpotResolvedPrimitiveType_fromMetaspace_method;
#define START_CLASS(className, fullClassName) { \
#define START_CLASS(className, fullClassName) { \
Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::fullClassName(), true, CHECK); \
className::_klass = InstanceKlass::cast(k); \
JVMCI_event_2(" klass for %s = " PTR_FORMAT, k->external_name(), p2i(k)); \
className::_klass->initialize(CHECK);
InstanceKlass* current = className::_klass; \
if (current != InstanceKlass::cast(k)) { \
if (current != nullptr) { \
fatal("klass for %s re-initialized: " PTR_FORMAT " -> " PTR_FORMAT, \
k->external_name(), p2i(current), p2i(k)); \
} \
JVMCI_event_2(" klass for %s = " PTR_FORMAT, k->external_name(), p2i(k)); \
className::_klass = InstanceKlass::cast(k); \
className::_klass->initialize(CHECK); \
}
#define END_CLASS }
@ -300,7 +312,16 @@ void JNIJVMCI::initialize_field_id(JNIEnv* env, jfieldID &fieldid, jclass clazz,
// Class initialization barrier
fieldid = env->GetFieldID(clazz, name, signature);
}
JVMCI_event_2(" jfieldID for %s %s.%s = " PTR_FORMAT, signature, class_name, name, p2i(fieldid));
// SVM guarantees that jfieldIDs for fields in the native image are also
// in the image and thus always have the same address.
if (current != fieldid) {
if (current != nullptr) {
fatal("jfieldID for %s %s.%s re-initialized: " PTR_FORMAT " -> " PTR_FORMAT,
signature, class_name, name, p2i(current), p2i(fieldid));
}
JVMCI_event_2(" jfieldID for %s %s.%s = " PTR_FORMAT, signature, class_name, name, p2i(fieldid));
}
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
@ -312,16 +333,26 @@ void JNIJVMCI::initialize_field_id(JNIEnv* env, jfieldID &fieldid, jclass clazz,
#define START_CLASS(className, fullClassName) { \
current_class_name = vmSymbols::fullClassName()->as_C_string(); \
if (JVMCILibDumpJNIConfig != NULL) { \
if (JVMCILibDumpJNIConfig != nullptr) { \
fileStream* st = JVMCIGlobals::get_jni_config_file(); \
st->print_cr("class %s", current_class_name); \
} else { \
jclass k = env->FindClass(current_class_name); \
JVMCI_EXCEPTION_CHECK(env, "FindClass(%s)", current_class_name); \
assert(k != NULL, #fullClassName " not initialized"); \
assert(k != nullptr, #fullClassName " not initialized"); \
k = (jclass) env->NewGlobalRef(k); \
JVMCI_event_2(" jclass for %s = " PTR_FORMAT, current_class_name, p2i(k)); \
className::_class = k; \
jclass current = className::_class; \
if (current != k) { \
JVMCI_event_2(" jclass for %s = " PTR_FORMAT, current_class_name, p2i(k)); \
/* SVM guarantees that jclass handles to classes in a native image are also */ \
/* in the image. Further calling NewGlobalRef on such a handle returns a stable */ \
/* values across all JavaVMs executing on the same native image. */ \
if (current != nullptr) { \
fatal("jclass for %s re-initialized: " PTR_FORMAT " -> " PTR_FORMAT, \
current_class_name, p2i(current), p2i(k)); \
} \
className::_class = k; \
} \
}
#define END_CLASS current_class_name = NULL; }
@ -338,17 +369,25 @@ void JNIJVMCI::initialize_field_id(JNIEnv* env, jfieldID &fieldid, jclass clazz,
#define STATIC_BOOLEAN_FIELD(className, name) FIELD(className, name, "Z", true)
#define GET_JNI_METHOD(jniGetMethod, dst, clazz, methodName, signature) \
if (JVMCILibDumpJNIConfig != NULL) { \
if (JVMCILibDumpJNIConfig != nullptr) { \
fileStream* st = JVMCIGlobals::get_jni_config_file(); \
st->print_cr("method %s %s %s", current_class_name, methodName, signature); \
} else { \
jmethodID current = dst; \
dst = env->jniGetMethod(clazz, methodName, signature); \
JVMCI_EXCEPTION_CHECK(env, #jniGetMethod "(%s.%s%s)", \
current_class_name, methodName, signature); \
assert(dst != NULL, "uninitialized"); \
JVMCI_event_2(" jmethodID for %s.%s%s = " PTR_FORMAT, \
current_class_name, methodName, signature, p2i(dst)); \
assert(dst != nullptr, "uninitialized"); \
if (current != dst) { \
JVMCI_event_2(" jmethodID for %s.%s%s = " PTR_FORMAT, \
current_class_name, methodName, signature, p2i(dst)); \
/* SVM guarantees that jmethodIDs for methods in the native image are also */ \
/* in the image and thus always have the same address. */ \
if (current != nullptr) { \
fatal("jmethod for %s.%s%s re-initialized: " PTR_FORMAT " -> " PTR_FORMAT, \
current_class_name, methodName, signature, p2i(current), p2i(dst)); \
} \
JVMCI_EXCEPTION_CHECK(env, #jniGetMethod "(%s.%s%s)", \
current_class_name, methodName, signature); \
} \
}
#define GET_JNI_CONSTRUCTOR(clazz, signature) \

View File

@ -50,6 +50,7 @@
#include "runtime/frame.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/mutex.hpp"
#include "runtime/reflectionUtils.hpp"
#include "runtime/sharedRuntime.hpp"
#if INCLUDE_G1GC
@ -827,62 +828,116 @@ void JVMCINMethodData::invalidate_nmethod_mirror(nmethod* nm) {
}
}
JVMCIRuntime::JVMCIRuntime(int id) {
_init_state = uninitialized;
_shared_library_javavm = NULL;
_id = id;
_metadata_handles = new MetadataHandles();
JVMCI_event_1("created new JVMCI runtime %d (" PTR_FORMAT ")", id, p2i(this));
}
// Handles to objects in the Hotspot heap.
static OopStorage* object_handles() {
return Universe::vm_global();
}
jobject JVMCIRuntime::make_global(const Handle& obj) {
jlong JVMCIRuntime::make_oop_handle(const Handle& obj) {
assert(!Universe::heap()->is_gc_active(), "can't extend the root set during GC");
assert(oopDesc::is_oop(obj()), "not an oop");
oop* ptr = object_handles()->allocate();
jobject res = NULL;
if (ptr != NULL) {
assert(*ptr == NULL, "invariant");
jlong res = 0;
if (ptr != nullptr) {
assert(*ptr == nullptr, "invariant");
NativeAccess<>::oop_store(ptr, obj());
res = reinterpret_cast<jobject>(ptr);
res = (jlong) ptr;
} else {
vm_exit_out_of_memory(sizeof(oop), OOM_MALLOC_ERROR,
"Cannot create JVMCI oop handle");
}
MutexLocker ml(JVMCI_lock);
MutexLocker ml(_lock);
_oop_handles.append(ptr);
return res;
}
void JVMCIRuntime::destroy_global(jobject handle) {
// Assert before nulling out, for better debugging.
assert(is_global_handle(handle), "precondition");
oop* oop_ptr = reinterpret_cast<oop*>(handle);
NativeAccess<>::oop_store(oop_ptr, (oop)NULL);
object_handles()->release(oop_ptr);
MutexLocker ml(JVMCI_lock);
bool JVMCIRuntime::probe_oop_handle(jlong handle, int index) {
oop* key = (oop*) handle;
if (key == _oop_handles.at(index)) {
_last_found_oop_handle_index = index;
return true;
}
return false;
}
bool JVMCIRuntime::is_global_handle(jobject handle) {
const oop* ptr = reinterpret_cast<oop*>(handle);
int JVMCIRuntime::find_oop_handle(jlong handle) {
int len = _oop_handles.length();
int next = _last_found_oop_handle_index + 1;
int prev = MAX2(_last_found_oop_handle_index, 0) - 1;
// Search "outwards" from the index of the last found
// entry. Experimentation shows that this significantly
// reduces the amount of searching performed.
do {
if (next < len) {
if (probe_oop_handle(handle, next)) {
return next;
}
next++;
}
if (prev >= 0) {
if (probe_oop_handle(handle, prev)) {
return prev;
}
prev--;
}
} while (next - (prev + 1) < len);
return -1;
}
int JVMCIRuntime::release_and_clear_globals() {
int released = 0;
if (_oop_handles.length() != 0) {
// Squash non-null JNI handles to front of _oop_handles for
// the bulk release operation
for (int i = 0; i < _oop_handles.length(); i++) {
oop* oop_ptr = _oop_handles.at(i);
if (oop_ptr != nullptr) {
// Satisfy OopHandles::release precondition that all
// handles being released are null.
NativeAccess<>::oop_store(oop_ptr, (oop) NULL);
_oop_handles.at_put(released++, oop_ptr);
}
}
// Do the bulk release
object_handles()->release(_oop_handles.adr_at(0), released);
}
_oop_handles.clear();
_last_found_oop_handle_index = -1;
return released;
}
void JVMCIRuntime::destroy_oop_handle(jlong handle) {
// Assert before nulling out, for better debugging.
assert(is_oop_handle(handle), "precondition");
oop* oop_ptr = (oop*) handle;
NativeAccess<>::oop_store(oop_ptr, (oop) nullptr);
object_handles()->release(oop_ptr);
MutexLocker ml(_lock);
int index = find_oop_handle(handle);
guarantee(index != -1, "global not allocated in JVMCI runtime %d: " INTPTR_FORMAT, id(), handle);
_oop_handles.at_put(index, nullptr);
}
bool JVMCIRuntime::is_oop_handle(jlong handle) {
const oop* ptr = (oop*) handle;
return object_handles()->allocation_status(ptr) == OopStorage::ALLOCATED_ENTRY;
}
jmetadata JVMCIRuntime::allocate_handle(const methodHandle& handle) {
MutexLocker ml(JVMCI_lock);
MutexLocker ml(_lock);
return _metadata_handles->allocate_handle(handle);
}
jmetadata JVMCIRuntime::allocate_handle(const constantPoolHandle& handle) {
MutexLocker ml(JVMCI_lock);
MutexLocker ml(_lock);
return _metadata_handles->allocate_handle(handle);
}
void JVMCIRuntime::release_handle(jmetadata handle) {
MutexLocker ml(JVMCI_lock);
MutexLocker ml(_lock);
_metadata_handles->chain_free_list(handle);
}
@ -904,19 +959,239 @@ static void _flush_log() {
// Function for shared library JavaVM to exit HotSpot on a fatal error
static void _fatal() {
Thread* thread = Thread::current_or_null_safe();
if (thread != nullptr && thread->is_Java_thread()) {
JavaThread* jthread = (JavaThread*) thread;
JVMCIRuntime* runtime = jthread->libjvmci_runtime();
if (runtime != nullptr) {
int javaVM_id = runtime->get_shared_library_javavm_id();
fatal("Fatal error in JVMCI shared library JavaVM[%d] owned by JVMCI runtime %d", javaVM_id, runtime->id());
}
}
intx current_thread_id = os::current_thread_id();
fatal("thread " INTX_FORMAT ": Fatal error in JVMCI shared library", current_thread_id);
}
JNIEnv* JVMCIRuntime::init_shared_library_javavm() {
JavaVM* javaVM = (JavaVM*) _shared_library_javavm;
if (javaVM == NULL) {
MutexLocker locker(JVMCI_lock);
// Check again under JVMCI_lock
javaVM = (JavaVM*) _shared_library_javavm;
if (javaVM != NULL) {
return NULL;
JVMCIRuntime::JVMCIRuntime(JVMCIRuntime* next, int id, bool for_compile_broker) :
_init_state(uninitialized),
_shared_library_javavm(nullptr),
_shared_library_javavm_id(0),
_id(id),
_next(next),
_metadata_handles(new MetadataHandles()),
_oop_handles(100, mtJVMCI),
_num_attached_threads(0),
_for_compile_broker(for_compile_broker),
_last_found_oop_handle_index(-1)
{
if (id == -1) {
_lock = JVMCIRuntime_lock;
} else {
stringStream lock_name;
lock_name.print("%s@%d", JVMCIRuntime_lock->name(), id);
Mutex::Rank lock_rank = DEBUG_ONLY(JVMCIRuntime_lock->rank()) NOT_DEBUG(Mutex::safepoint);
_lock = new PaddedMonitor(lock_rank, lock_name.as_string(/*c_heap*/true));
}
JVMCI_event_1("created new %s JVMCI runtime %d (" PTR_FORMAT ")",
id == -1 ? "Java" : for_compile_broker ? "CompileBroker" : "Compiler", id, p2i(this));
}
JVMCIRuntime* JVMCIRuntime::select_runtime_in_shutdown(JavaThread* thread) {
assert(JVMCI_lock->owner() == thread, "must be");
// When shutting down, use the first available runtime.
for (JVMCIRuntime* runtime = JVMCI::_compiler_runtimes; runtime != nullptr; runtime = runtime->_next) {
if (runtime->_num_attached_threads != cannot_be_attached) {
runtime->pre_attach_thread(thread);
JVMCI_event_1("using pre-existing JVMCI runtime %d in shutdown", runtime->id());
return runtime;
}
}
// Lazily initialize JVMCI::_shutdown_compiler_runtime. Safe to
// do here since JVMCI_lock is locked.
if (JVMCI::_shutdown_compiler_runtime == nullptr) {
JVMCI::_shutdown_compiler_runtime = new JVMCIRuntime(nullptr, -2, true);
}
JVMCIRuntime* runtime = JVMCI::_shutdown_compiler_runtime;
JVMCI_event_1("using reserved shutdown JVMCI runtime %d", runtime->id());
return runtime;
}
JVMCIRuntime* JVMCIRuntime::select_runtime(JavaThread* thread, JVMCIRuntime* skip, int* count) {
assert(JVMCI_lock->owner() == thread, "must be");
bool for_compile_broker = thread->is_Compiler_thread();
for (JVMCIRuntime* runtime = JVMCI::_compiler_runtimes; runtime != nullptr; runtime = runtime->_next) {
if (count != nullptr) {
(*count)++;
}
if (for_compile_broker == runtime->_for_compile_broker) {
int count = runtime->_num_attached_threads;
if (count == cannot_be_attached || runtime == skip) {
// Cannot attach to rt
continue;
}
// If selecting for repacking, ignore a runtime without an existing JavaVM
if (skip != nullptr && !runtime->has_shared_library_javavm()) {
continue;
}
// Select first runtime with sufficient capacity
if (count < (int) JVMCIThreadsPerNativeLibraryRuntime) {
runtime->pre_attach_thread(thread);
return runtime;
}
}
}
return nullptr;
}
JVMCIRuntime* JVMCIRuntime::select_or_create_runtime(JavaThread* thread) {
assert(JVMCI_lock->owner() == thread, "must be");
int id = 0;
JVMCIRuntime* runtime;
if (JVMCI::using_singleton_shared_library_runtime()) {
runtime = JVMCI::_compiler_runtimes;
guarantee(runtime != nullptr, "must be");
while (runtime->_num_attached_threads == cannot_be_attached) {
// Since there is only a singleton JVMCIRuntime, we
// need to wait for it to be available for attaching.
JVMCI_lock->wait();
}
runtime->pre_attach_thread(thread);
} else {
runtime = select_runtime(thread, nullptr, &id);
}
if (runtime == nullptr) {
runtime = new JVMCIRuntime(JVMCI::_compiler_runtimes, id, thread->is_Compiler_thread());
JVMCI::_compiler_runtimes = runtime;
runtime->pre_attach_thread(thread);
}
return runtime;
}
JVMCIRuntime* JVMCIRuntime::for_thread(JavaThread* thread) {
assert(thread->libjvmci_runtime() == nullptr, "must be");
// Find the runtime with fewest attached threads
JVMCIRuntime* runtime = nullptr;
{
MutexLocker locker(JVMCI_lock);
runtime = JVMCI::in_shutdown() ? select_runtime_in_shutdown(thread) : select_or_create_runtime(thread);
}
runtime->attach_thread(thread);
return runtime;
}
const char* JVMCIRuntime::attach_shared_library_thread(JavaThread* thread, JavaVM* javaVM) {
MutexLocker locker(JVMCI_lock);
for (JVMCIRuntime* runtime = JVMCI::_compiler_runtimes; runtime != nullptr; runtime = runtime->_next) {
if (runtime->_shared_library_javavm == javaVM) {
if (runtime->_num_attached_threads == cannot_be_attached) {
return "Cannot attach to JVMCI runtime that is shutting down";
}
runtime->pre_attach_thread(thread);
runtime->attach_thread(thread);
return nullptr;
}
}
return "Cannot find JVMCI runtime";
}
void JVMCIRuntime::pre_attach_thread(JavaThread* thread) {
assert(JVMCI_lock->owner() == thread, "must be");
_num_attached_threads++;
}
void JVMCIRuntime::attach_thread(JavaThread* thread) {
assert(thread->libjvmci_runtime() == nullptr, "must be");
thread->set_libjvmci_runtime(this);
guarantee(this == JVMCI::_shutdown_compiler_runtime ||
_num_attached_threads > 0,
"missing reservation in JVMCI runtime %d: _num_attached_threads=%d", _id, _num_attached_threads);
JVMCI_event_1("attached to JVMCI runtime %d%s", _id, JVMCI::in_shutdown() ? " [in JVMCI shutdown]" : "");
}
void JVMCIRuntime::repack(JavaThread* thread) {
JVMCIRuntime* new_runtime = nullptr;
{
MutexLocker locker(JVMCI_lock);
if (JVMCI::using_singleton_shared_library_runtime() || _num_attached_threads != 1 || JVMCI::in_shutdown()) {
return;
}
new_runtime = select_runtime(thread, this, nullptr);
}
if (new_runtime != nullptr) {
JVMCI_event_1("Moving thread from JVMCI runtime %d to JVMCI runtime %d (%d attached)", _id, new_runtime->_id, new_runtime->_num_attached_threads - 1);
detach_thread(thread, "moving thread to another JVMCI runtime");
new_runtime->attach_thread(thread);
}
}
bool JVMCIRuntime::detach_thread(JavaThread* thread, const char* reason, bool can_destroy_javavm) {
if (this == JVMCI::_shutdown_compiler_runtime || JVMCI::in_shutdown()) {
// Do minimal work when shutting down JVMCI
thread->set_libjvmci_runtime(nullptr);
return false;
}
bool should_shutdown;
bool destroyed_javavm = false;
{
MutexLocker locker(JVMCI_lock);
_num_attached_threads--;
JVMCI_event_1("detaching from JVMCI runtime %d: %s (%d other threads still attached)", _id, reason, _num_attached_threads);
should_shutdown = _num_attached_threads == 0 && !JVMCI::in_shutdown();
if (should_shutdown && !can_destroy_javavm) {
// If it's not possible to destroy the JavaVM on this thread then the VM must
// not be shutdown. This can happen when a shared library thread is the last
// thread to detach from a shared library JavaVM (e.g. GraalServiceThread).
JVMCI_event_1("Cancelled shut down of JVMCI runtime %d", _id);
should_shutdown = false;
}
if (should_shutdown) {
// Prevent other threads from attaching to this runtime
// while it is shutting down and destroying its JavaVM
_num_attached_threads = cannot_be_attached;
}
}
if (should_shutdown) {
// Release the JavaVM resources associated with this
// runtime once there are no threads attached to it.
shutdown();
if (can_destroy_javavm) {
destroyed_javavm = destroy_shared_library_javavm();
if (destroyed_javavm) {
// Can release all handles now that there's no code executing
// that could be using them. Handles for the Java JVMCI runtime
// are never released as we cannot guarantee all compiler threads
// using it have been stopped.
int released = release_and_clear_globals();
JVMCI_event_1("releasing handles for JVMCI runtime %d: oop handles=%d, metadata handles={total=%d, live=%d, blocks=%d}",
_id,
released,
_metadata_handles->num_handles(),
_metadata_handles->num_handles() - _metadata_handles->num_free_handles(),
_metadata_handles->num_blocks());
// No need to acquire _lock since this is the only thread accessing this runtime
_metadata_handles->clear();
}
}
// Allow other threads to attach to this runtime now
MutexLocker locker(JVMCI_lock);
_num_attached_threads = 0;
if (JVMCI::using_singleton_shared_library_runtime()) {
// Notify any thread waiting to attach to the
// singleton JVMCIRuntime
JVMCI_lock->notify();
}
}
thread->set_libjvmci_runtime(nullptr);
JVMCI_event_1("detached from JVMCI runtime %d", _id);
return destroyed_javavm;
}
JNIEnv* JVMCIRuntime::init_shared_library_javavm() {
MutexLocker locker(_lock);
JavaVM* javaVM = _shared_library_javavm;
if (javaVM == nullptr) {
char* sl_path;
void* sl_handle = JVMCI::get_shared_library(sl_path, true);
@ -924,7 +1199,7 @@ JNIEnv* JVMCIRuntime::init_shared_library_javavm() {
typedef jint (*JNI_CreateJavaVM_t)(JavaVM **pvm, void **penv, void *args);
JNI_CreateJavaVM = CAST_TO_FN_PTR(JNI_CreateJavaVM_t, os::dll_lookup(sl_handle, "JNI_CreateJavaVM"));
if (JNI_CreateJavaVM == NULL) {
if (JNI_CreateJavaVM == nullptr) {
fatal("Unable to find JNI_CreateJavaVM in %s", sl_path);
}
@ -954,10 +1229,11 @@ JNIEnv* JVMCIRuntime::init_shared_library_javavm() {
vm_args.options = options;
vm_args.nOptions = sizeof(options) / sizeof(JavaVMOption);
JNIEnv* env = NULL;
JNIEnv* env = nullptr;
int result = (*JNI_CreateJavaVM)(&javaVM, (void**) &env, &vm_args);
if (result == JNI_OK) {
guarantee(env != NULL, "missing env");
guarantee(env != nullptr, "missing env");
_shared_library_javavm_id = javaVM_id;
_shared_library_javavm = javaVM;
JVMCI_event_1("created JavaVM[%ld]@" PTR_FORMAT " for JVMCI runtime %d", javaVM_id, p2i(javaVM), _id);
return env;
@ -965,16 +1241,16 @@ JNIEnv* JVMCIRuntime::init_shared_library_javavm() {
fatal("JNI_CreateJavaVM failed with return value %d", result);
}
}
return NULL;
return nullptr;
}
void JVMCIRuntime::init_JavaVM_info(jlongArray info, JVMCI_TRAPS) {
if (info != NULL) {
if (info != nullptr) {
typeArrayOop info_oop = (typeArrayOop) JNIHandles::resolve(info);
if (info_oop->length() < 4) {
JVMCI_THROW_MSG(ArrayIndexOutOfBoundsException, err_msg("%d < 4", info_oop->length()));
}
JavaVM* javaVM = (JavaVM*) _shared_library_javavm;
JavaVM* javaVM = _shared_library_javavm;
info_oop->long_at_put(0, (jlong) (address) javaVM);
info_oop->long_at_put(1, (jlong) (address) javaVM->functions->reserved0);
info_oop->long_at_put(2, (jlong) (address) javaVM->functions->reserved1);
@ -983,9 +1259,9 @@ void JVMCIRuntime::init_JavaVM_info(jlongArray info, JVMCI_TRAPS) {
}
#define JAVAVM_CALL_BLOCK \
guarantee(thread != NULL && _shared_library_javavm != NULL, "npe"); \
guarantee(thread != nullptr && _shared_library_javavm != nullptr, "npe"); \
ThreadToNativeFromVM ttnfv(thread); \
JavaVM* javavm = (JavaVM*) _shared_library_javavm;
JavaVM* javavm = _shared_library_javavm;
jint JVMCIRuntime::AttachCurrentThread(JavaThread* thread, void **penv, void *args) {
JAVAVM_CALL_BLOCK
@ -1027,21 +1303,64 @@ void JVMCIRuntime::initialize_HotSpotJVMCIRuntime(JVMCI_TRAPS) {
JVMCI::_is_initialized = true;
}
JVMCIRuntime::InitState JVMCIRuntime::_shared_library_javavm_refs_init_state = JVMCIRuntime::uninitialized;
JVMCIRuntime::InitState JVMCIRuntime::_hotspot_javavm_refs_init_state = JVMCIRuntime::uninitialized;
class JavaVMRefsInitialization: public StackObj {
JVMCIRuntime::InitState *_state;
int _id;
public:
JavaVMRefsInitialization(JVMCIRuntime::InitState *state, int id) {
_state = state;
_id = id;
// All classes, methods and fields in the JVMCI shared library
// are in the read-only part of the image. As such, these
// values (and any global handle derived from them via NewGlobalRef)
// are the same for all JavaVM instances created in the
// shared library which means they only need to be initialized
// once. In non-product mode, we check this invariant.
// See com.oracle.svm.jni.JNIImageHeapHandles.
// The same is true for Klass* and field offsets in HotSpotJVMCI.
if (*state == JVMCIRuntime::uninitialized DEBUG_ONLY( || true)) {
*state = JVMCIRuntime::being_initialized;
JVMCI_event_1("initializing JavaVM references in JVMCI runtime %d", id);
} else {
while (*state != JVMCIRuntime::fully_initialized) {
JVMCI_event_1("waiting for JavaVM references initialization in JVMCI runtime %d", id);
JVMCI_lock->wait();
}
JVMCI_event_1("done waiting for JavaVM references initialization in JVMCI runtime %d", id);
}
}
~JavaVMRefsInitialization() {
if (*_state == JVMCIRuntime::being_initialized) {
*_state = JVMCIRuntime::fully_initialized;
JVMCI_event_1("initialized JavaVM references in JVMCI runtime %d", _id);
JVMCI_lock->notify_all();
}
}
bool should_init() {
return *_state == JVMCIRuntime::being_initialized;
}
};
void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
// Check first without JVMCI_lock
// Check first without _lock
if (_init_state == fully_initialized) {
return;
}
MutexLocker locker(JVMCI_lock);
// Check again under JVMCI_lock
MutexLocker locker(_lock);
// Check again under _lock
if (_init_state == fully_initialized) {
return;
}
while (_init_state == being_initialized) {
JVMCI_event_1("waiting for initialization of JVMCI runtime %d", _id);
JVMCI_lock->wait();
_lock->wait();
if (_init_state == fully_initialized) {
JVMCI_event_1("done waiting for initialization of JVMCI runtime %d", _id);
return;
@ -1052,20 +1371,32 @@ void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
_init_state = being_initialized;
{
MutexUnlocker unlock(JVMCI_lock);
MutexUnlocker unlock(_lock);
JavaThread* THREAD = JavaThread::current(); // For exception macros.
JavaThread* THREAD = JavaThread::current();
HandleMark hm(THREAD);
ResourceMark rm(THREAD);
if (JVMCIENV->is_hotspot()) {
HotSpotJVMCI::compute_offsets(CHECK_EXIT);
} else {
JNIAccessMark jni(JVMCIENV);
{
MutexLocker lock_jvmci(JVMCI_lock);
if (JVMCIENV->is_hotspot()) {
JavaVMRefsInitialization initialization(&_hotspot_javavm_refs_init_state, _id);
if (initialization.should_init()) {
MutexUnlocker unlock_jvmci(JVMCI_lock);
HotSpotJVMCI::compute_offsets(CHECK_EXIT);
}
} else {
JavaVMRefsInitialization initialization(&_shared_library_javavm_refs_init_state, _id);
if (initialization.should_init()) {
MutexUnlocker unlock_jvmci(JVMCI_lock);
JNIAccessMark jni(JVMCIENV, THREAD);
JNIJVMCI::initialize_ids(jni.env());
if (jni()->ExceptionCheck()) {
jni()->ExceptionDescribe();
fatal("JNI exception during init");
JNIJVMCI::initialize_ids(jni.env());
if (jni()->ExceptionCheck()) {
jni()->ExceptionDescribe();
fatal("JNI exception during init");
}
// _lock is re-locked at this point
}
}
}
@ -1090,7 +1421,7 @@ void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
_init_state = fully_initialized;
JVMCI_event_1("initialized JVMCI runtime %d", _id);
JVMCI_lock->notify_all();
_lock->notify_all();
}
JVMCIObject JVMCIRuntime::create_jvmci_primitive_type(BasicType type, JVMCI_TRAPS) {
@ -1122,6 +1453,7 @@ void JVMCIRuntime::initialize_JVMCI(JVMCI_TRAPS) {
if (!is_HotSpotJVMCIRuntime_initialized()) {
initialize(JVMCI_CHECK);
JVMCIENV->call_JVMCI_getRuntime(JVMCI_CHECK);
guarantee(_HotSpotJVMCIRuntime_instance.is_non_null(), "NPE in JVMCI runtime %d", _id);
}
}
@ -1172,10 +1504,48 @@ void JVMCIRuntime::shutdown() {
JVMCIEnv __stack_jvmci_env__(JavaThread::current(), _HotSpotJVMCIRuntime_instance.is_hotspot(), __FILE__, __LINE__);
JVMCIEnv* JVMCIENV = &__stack_jvmci_env__;
JVMCIENV->call_HotSpotJVMCIRuntime_shutdown(_HotSpotJVMCIRuntime_instance);
JVMCI_event_1("shut down HotSpotJVMCIRuntime for JVMCI runtime %d", _id);
if (_num_attached_threads == cannot_be_attached) {
// Only when no other threads are attached to this runtime
// is it safe to reset these fields.
_HotSpotJVMCIRuntime_instance = JVMCIObject();
_init_state = uninitialized;
JVMCI_event_1("shut down JVMCI runtime %d", _id);
}
}
}
bool JVMCIRuntime::destroy_shared_library_javavm() {
guarantee(_num_attached_threads == cannot_be_attached,
"cannot destroy JavaVM for JVMCI runtime %d with %d attached threads", _id, _num_attached_threads);
JavaVM* javaVM;
int javaVM_id = _shared_library_javavm_id;
{
// Exactly one thread can destroy the JavaVM
// and release the handle to it.
MutexLocker only_one(_lock);
javaVM = _shared_library_javavm;
if (javaVM != nullptr) {
_shared_library_javavm = nullptr;
_shared_library_javavm_id = 0;
}
}
if (javaVM != nullptr) {
int result;
{
// Must transition into native before calling into libjvmci
ThreadToNativeFromVM ttnfv(JavaThread::current());
result = javaVM->DestroyJavaVM();
}
if (result == JNI_OK) {
JVMCI_event_1("destroyed JavaVM[%d]@" PTR_FORMAT " for JVMCI runtime %d", javaVM_id, p2i(javaVM), _id);
} else {
warning("Non-zero result (%d) when calling JNI_DestroyJavaVM on JavaVM[%d]@" PTR_FORMAT, result, javaVM_id, p2i(javaVM));
}
return true;
}
return false;
}
void JVMCIRuntime::bootstrap_finished(TRAPS) {
if (_HotSpotJVMCIRuntime_instance.is_non_null()) {
THREAD_JVMCIENV(JavaThread::current());
@ -1587,6 +1957,12 @@ void JVMCIRuntime::compile_method(JVMCIEnv* JVMCIENV, JVMCICompiler* compiler, c
return;
}
if (JVMCI::in_shutdown()) {
if (UseJVMCINativeLibrary) {
JVMCIRuntime *runtime = JVMCI::compiler_runtime(thread, false);
if (runtime != nullptr) {
runtime->detach_thread(thread, "JVMCI shutdown pre-empted compilation");
}
}
compile_state->set_failure(false, "Avoiding compilation during shutdown");
return;
}
@ -1616,7 +1992,7 @@ void JVMCIRuntime::compile_method(JVMCIEnv* JVMCIENV, JVMCICompiler* compiler, c
bool retryable = JVMCIENV->get_HotSpotCompilationRequestResult_retry(result_object) != 0;
compile_state->set_failure(retryable, failure_reason, true);
} else {
if (compile_state->task()->code() == NULL) {
if (compile_state->task()->code() == nullptr) {
compile_state->set_failure(true, "no nmethod produced");
} else {
compile_state->task()->set_num_inlined_bytecodes(JVMCIENV->get_HotSpotCompilationRequestResult_inlinedBytecodes(result_object));
@ -1839,3 +2215,13 @@ JVMCI::CodeInstallResult JVMCIRuntime::register_method(JVMCIEnv* JVMCIENV,
return result;
}
void JVMCIRuntime::post_compile(JavaThread* thread) {
if (UseJVMCINativeLibrary && JVMCI::one_shared_library_javavm_per_compilation()) {
if (thread->libjvmci_runtime() != nullptr) {
detach_thread(thread, "single use JavaVM");
} else {
// JVMCI shutdown may have already detached the thread
}
}
}

View File

@ -103,6 +103,7 @@ public:
// There is one instance of this class per HotSpotJVMCIRuntime object.
class JVMCIRuntime: public CHeapObj<mtJVMCI> {
friend class JVMCI;
friend class JavaVMRefsInitialization;
public:
// Constants describing whether JVMCI wants to be able to adjust the compilation
// level selected for a method by the VM compilation policy and if so, based on
@ -124,6 +125,14 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
// Initialization state of this JVMCIRuntime.
InitState _init_state;
// Initialization state of the references to classes, methods
// and fields in the JVMCI shared library.
static InitState _shared_library_javavm_refs_init_state;
// Initialization state of the references to classes, methods
// and fields in HotSpot metadata.
static InitState _hotspot_javavm_refs_init_state;
// A wrapper for a VM scoped JNI global handle (i.e. JVMCIEnv::make_global)
// to a HotSpotJVMCIRuntime instance. This JNI global handle must never
// be explicitly destroyed as it can be accessed in a racy way during
@ -131,17 +140,46 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
// the VM or shared library JavaVM managing the handle dies.
JVMCIObject _HotSpotJVMCIRuntime_instance;
// Result of calling JNI_CreateJavaVM in the JVMCI shared library.
// Must only be modified under JVMCI_lock.
volatile JavaVM* _shared_library_javavm;
// Lock for operations that may be performed by
// any thread attached this runtime. To avoid deadlock,
// this lock must always be acquired before JVMCI_lock.
Monitor* _lock;
// Result of calling JNI_CreateJavaVM in the JVMCI shared library.
// Must only be mutated under _lock.
JavaVM* _shared_library_javavm;
// Id for _shared_library_javavm.
int _shared_library_javavm_id;
// Position and link in global list of JVMCI shared library runtimes.
// The HotSpot heap based runtime will have an id of -1 and the
// JVMCI shared library runtime will have an id of 0.
// runtime reserved for threads attaching during JVMCI shutdown
// will have an id of -2.
int _id;
JVMCIRuntime* _next;
// Handles to Metadata objects.
MetadataHandles* _metadata_handles;
// List of oop handles allocated via make_oop_handle. This is to support
// destroying remaining oop handles when the JavaVM associated
// with this runtime is shutdown.
GrowableArray<oop*> _oop_handles;
// Number of threads attached or about to be attached to this runtime.
// Must only be mutated under JVMCI_lock to facilitate safely moving
// threads between JVMCI runtimes. A value of -1 implies this runtime is
// not available to be attached to another thread because it is in the
// process of shutting down and destroying its JavaVM.
int _num_attached_threads;
static const int cannot_be_attached = -1;
// Is this runtime for threads managed by the CompileBroker?
// Examples of non-CompileBroker threads are CompileTheWorld threads
// or Truffle compilation threads.
bool _for_compile_broker;
JVMCIObject create_jvmci_primitive_type(BasicType type, JVMCI_TRAPS);
// Implementation methods for loading and constant pool access.
@ -168,18 +206,51 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
Bytecodes::Code bc,
constantTag tag);
// Helpers for `for_thread`.
// Selects an existing runtime (except for `skip`) that has
// fewer than JVMCI::max_threads_per_runtime() attached threads.
// If such a runtime exists, its _num_attached_threads is incremented
// and the caller must subsequently attach `thread` to it.
// JVMCI_lock must be held by current thread.
// If nullptr is returned, then `*count` contains the number of JVMCIRuntimes
// currently allocated.
static JVMCIRuntime* select_runtime(JavaThread* thread, JVMCIRuntime* skip, int* count);
// Selects an existing runtime for `thread` or creates a new one if
// no applicable runtime exists.
// JVMCI_lock must be held by current thread
static JVMCIRuntime* select_or_create_runtime(JavaThread* thread);
// Selects an existing runtime for `thread` when in JVMCI shutdown.
// JVMCI_lock must be held by current thread
static JVMCIRuntime* select_runtime_in_shutdown(JavaThread* thread);
// Helpers for destroy_oop_handle
int _last_found_oop_handle_index;
bool probe_oop_handle(jlong handle, int index);
int find_oop_handle(jlong handle);
// Releases all the non-null entries in _oop_handles and then clears
// the list. Returns the number of non-null entries prior to clearing.
int release_and_clear_globals();
public:
JVMCIRuntime(int id);
JVMCIRuntime(JVMCIRuntime* next, int id, bool for_compile_broker);
int id() const { return _id; }
Monitor* lock() const { return _lock; }
// Ensures that a JVMCI shared library JavaVM exists for this runtime.
// If the JavaVM was created by this call, then the thread-local JNI
// interface pointer for the JavaVM is returned otherwise NULL is returned.
// interface pointer for the JavaVM is returned otherwise nullptr is returned.
JNIEnv* init_shared_library_javavm();
// Determines if the JVMCI shared library JavaVM exists for this runtime.
bool has_shared_library_javavm() { return _shared_library_javavm != NULL; }
bool has_shared_library_javavm() { return _shared_library_javavm != nullptr; }
// Gets an ID for the JVMCI shared library JavaVM associated with this runtime.
int get_shared_library_javavm_id() { return _shared_library_javavm_id; }
// Copies info about the JVMCI shared library JavaVM associated with this
// runtime into `info` as follows:
@ -202,21 +273,53 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
// Compute offsets and construct any state required before executing JVMCI code.
void initialize(JVMCIEnv* jvmciEnv);
// Allocation and management of JNI global object handles
// whose lifetime is scoped by this JVMCIRuntime. The lifetime
// Allocation and management of handles to HotSpot heap objects
// whose lifetime is scoped by this JVMCIRuntime. The max lifetime
// of these handles is the same as the JVMCI shared library JavaVM
// associated with this JVMCIRuntime. These JNI handles are
// used when creating a IndirectHotSpotObjectConstantImpl in the
// used when creating an IndirectHotSpotObjectConstantImpl in the
// shared library JavaVM.
jobject make_global(const Handle& obj);
void destroy_global(jobject handle);
bool is_global_handle(jobject handle);
jlong make_oop_handle(const Handle& obj);
bool is_oop_handle(jlong handle);
// Called from IndirectHotSpotObjectConstantImpl.clear(Object)
void destroy_oop_handle(jlong handle);
// Allocation and management of metadata handles.
jmetadata allocate_handle(const methodHandle& handle);
jmetadata allocate_handle(const constantPoolHandle& handle);
void release_handle(jmetadata handle);
// Finds a JVMCI runtime for `thread`. A new JVMCI runtime is created if
// there are none currently available with JVMCI::max_threads_per_runtime()
// or fewer attached threads.
static JVMCIRuntime* for_thread(JavaThread* thread);
// Finds the JVMCI runtime owning `javavm` and attaches `thread` to it.
// Returns an error message if attaching fails.
static const char* attach_shared_library_thread(JavaThread* thread, JavaVM* javaVM);
// Reserves a slot in this runtime for `thread` to prevent it being
// shutdown before `thread` is attached. JVMCI_lock must be held
// and the caller must call `attach_thread` upon releasing it.
void pre_attach_thread(JavaThread* thread);
// Attaches `thread` to this runtime.
void attach_thread(JavaThread* thread);
// Detaches `thread` from this runtime.
// Returns whether DestroyJavaVM was called on the JavaVM associated
// with this runtime as a result of detaching.
// The `can_destroy_javavm` is false when in the scope of
// a down call from the JVMCI shared library JavaVM. Since the scope
// will return to the shared library JavaVM, the JavaVM must not be destroyed.
bool detach_thread(JavaThread* thread, const char* reason, bool can_destroy_javavm=true);
// If `thread` is the last thread attached to this runtime,
// move it to another runtime with an existing JavaVM and available capacity
// if possible, thus allowing this runtime to release its JavaVM.
void repack(JavaThread* thread);
// Gets the HotSpotJVMCIRuntime instance for this runtime,
// initializing it first if necessary.
JVMCIObject get_HotSpotJVMCIRuntime(JVMCI_TRAPS);
@ -240,8 +343,15 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
void call_getCompiler(TRAPS);
// Shuts down this runtime by calling HotSpotJVMCIRuntime.shutdown().
// If this is the last thread attached to this runtime, then
// `_HotSpotJVMCIRuntime_instance` is set to nullptr and `_init_state`
// to uninitialized.
void shutdown();
// Destroys the JVMCI shared library JavaVM attached to this runtime.
// Return true iff DestroyJavaVM was called on the JavaVM.
bool destroy_shared_library_javavm();
void bootstrap_finished(TRAPS);
// Look up a klass by name from a particular class loader (the accessor's).
@ -311,6 +421,10 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
char* speculations,
int speculations_len);
// Detach `thread` from this runtime and destroy this runtime's JavaVM
// if using one JavaVM per JVMCI compilation .
void post_compile(JavaThread* thread);
// Reports an unexpected exception and exits the VM with a fatal error.
static void fatal_exception(JVMCIEnv* JVMCIENV, const char* message);

View File

@ -112,17 +112,19 @@ bool JVMCIGlobals::check_jvmci_flags_are_consistent() {
}
JVMCI_FLAG_CHECKED(EagerJVMCI)
CHECK_NOT_SET(JVMCIEventLogLevel, EnableJVMCI)
CHECK_NOT_SET(JVMCITraceLevel, EnableJVMCI)
CHECK_NOT_SET(JVMCICounterSize, EnableJVMCI)
CHECK_NOT_SET(JVMCICountersExcludeCompiler, EnableJVMCI)
CHECK_NOT_SET(JVMCIUseFastLocking, EnableJVMCI)
CHECK_NOT_SET(JVMCINMethodSizeLimit, EnableJVMCI)
CHECK_NOT_SET(JVMCIPrintProperties, EnableJVMCI)
CHECK_NOT_SET(UseJVMCINativeLibrary, EnableJVMCI)
CHECK_NOT_SET(JVMCILibPath, EnableJVMCI)
CHECK_NOT_SET(JVMCINativeLibraryErrorFile, EnableJVMCI)
CHECK_NOT_SET(JVMCILibDumpJNIConfig, EnableJVMCI)
CHECK_NOT_SET(JVMCIEventLogLevel, EnableJVMCI)
CHECK_NOT_SET(JVMCITraceLevel, EnableJVMCI)
CHECK_NOT_SET(JVMCICounterSize, EnableJVMCI)
CHECK_NOT_SET(JVMCICountersExcludeCompiler, EnableJVMCI)
CHECK_NOT_SET(JVMCIUseFastLocking, EnableJVMCI)
CHECK_NOT_SET(JVMCINMethodSizeLimit, EnableJVMCI)
CHECK_NOT_SET(JVMCIPrintProperties, EnableJVMCI)
CHECK_NOT_SET(JVMCIThreadsPerNativeLibraryRuntime, EnableJVMCI)
CHECK_NOT_SET(JVMCICompilerIdleDelay, EnableJVMCI)
CHECK_NOT_SET(UseJVMCINativeLibrary, EnableJVMCI)
CHECK_NOT_SET(JVMCILibPath, EnableJVMCI)
CHECK_NOT_SET(JVMCINativeLibraryErrorFile, EnableJVMCI)
CHECK_NOT_SET(JVMCILibDumpJNIConfig, EnableJVMCI)
#ifndef COMPILER2
JVMCI_FLAG_CHECKED(MaxVectorSize)
@ -166,6 +168,8 @@ bool JVMCIGlobals::enable_jvmci_product_mode(JVMFlagOrigin origin) {
"EnableJVMCI",
"EnableJVMCIProduct",
"UseJVMCICompiler",
"JVMCIThreadsPerNativeLibraryRuntime",
"JVMCICompilerIdleDelay",
"JVMCIPrintProperties",
"EagerJVMCI",
"JVMCIThreads",

View File

@ -31,6 +31,7 @@
class fileStream;
#define LIBJVMCI_ERR_FILE "hs_err_pid%p_libjvmci.log"
#define DEFAULT_COMPILER_IDLE_DELAY 1000
//
// Declare all global flags used by the JVMCI compiler. Only flags that need
@ -57,6 +58,21 @@ class fileStream;
"Use JVMCI as the default compiler. Defaults to true if " \
"EnableJVMCIProduct is true.") \
\
product(uint, JVMCIThreadsPerNativeLibraryRuntime, 0, EXPERIMENTAL, \
"Max number of threads per JVMCI native runtime. " \
"Specify 0 to force use of a single JVMCI native runtime. ") \
range(0, max_jint) \
\
product(uint, JVMCICompilerIdleDelay, DEFAULT_COMPILER_IDLE_DELAY, EXPERIMENTAL, \
"Number of milliseconds a JVMCI compiler queue should wait for " \
"a compilation task before being considered idle. When a JVMCI " \
"compiler queue becomes idle, it is detached from its JVMCIRuntime. "\
"Once the last thread is detached from a JVMCIRuntime, all " \
"resources associated with the runtime are reclaimed. To use a " \
"new runtime for every JVMCI compilation, set this value to 0 " \
"and set JVMCIThreadsPerNativeLibraryRuntime to 1.") \
range(0, max_jint) \
\
product(bool, JVMCIPrintProperties, false, EXPERIMENTAL, \
"Prints properties used by the JVMCI compiler and exits") \
\

View File

@ -409,6 +409,7 @@
declare_constant(JVM_ACC_FIELD_INTERNAL) \
declare_constant(JVM_ACC_FIELD_STABLE) \
declare_constant(JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE) \
declare_constant(JVM_ACC_IS_VALUE_BASED_CLASS) \
declare_preprocessor_constant("JVM_ACC_VARARGS", JVM_ACC_VARARGS) \
declare_preprocessor_constant("JVM_ACC_BRIDGE", JVM_ACC_BRIDGE) \
declare_preprocessor_constant("JVM_ACC_ANNOTATION", JVM_ACC_ANNOTATION) \

View File

@ -431,7 +431,7 @@ void before_exit(JavaThread* thread) {
#if INCLUDE_JVMCI
if (EnableJVMCI) {
JVMCI::shutdown();
JVMCI::shutdown(thread);
}
#endif

View File

@ -156,6 +156,7 @@ Mutex* Bootclasspath_lock = NULL;
#if INCLUDE_JVMCI
Monitor* JVMCI_lock = NULL;
Monitor* JVMCIRuntime_lock = NULL;
#endif
@ -336,7 +337,8 @@ void mutex_init() {
def(Zip_lock , PaddedMonitor, nosafepoint-1); // Holds DumpTimeTable_lock
#if INCLUDE_JVMCI
def(JVMCI_lock , PaddedMonitor, safepoint, true);
// JVMCIRuntime::_lock must be acquired before JVMCI_lock to avoid deadlock
def(JVMCIRuntime_lock , PaddedMonitor, safepoint, true);
#endif
// These locks have relative rankings, and inherit safepoint checking attributes from that rank.
@ -369,6 +371,10 @@ void mutex_init() {
defl(Module_lock , PaddedMutex , ClassLoaderDataGraph_lock);
defl(SystemDictionary_lock , PaddedMonitor, Module_lock);
defl(JNICritical_lock , PaddedMonitor, MultiArray_lock); // used for JNI critical regions
#if INCLUDE_JVMCI
// JVMCIRuntime_lock must be acquired before JVMCI_lock to avoid deadlock
defl(JVMCI_lock , PaddedMonitor, JVMCIRuntime_lock);
#endif
}
GCMutexLocker::GCMutexLocker(Mutex* mutex) {

View File

@ -148,7 +148,8 @@ extern Mutex* CodeHeapStateAnalytics_lock; // lock print functions against
// Only used locally in PrintCodeCacheLayout processing.
#if INCLUDE_JVMCI
extern Monitor* JVMCI_lock; // Monitor to control initialization of JVMCI
extern Monitor* JVMCI_lock; // protects global JVMCI critical sections
extern Monitor* JVMCIRuntime_lock; // protects critical sections for a specific JVMCIRuntime object
#endif
extern Mutex* Bootclasspath_lock;

View File

@ -1034,6 +1034,7 @@ JavaThread::JavaThread() :
_in_retryable_allocation(false),
_pending_failed_speculation(0),
_jvmci{nullptr},
_libjvmci_runtime(nullptr),
_jvmci_counters(nullptr),
_jvmci_reserved0(0),
_jvmci_reserved1(0),

View File

@ -80,6 +80,8 @@ class JvmtiDeferredUpdates;
class ThreadClosure;
class ICRefillVerifier;
class JVMCIRuntime;
class Metadata;
class ResourceArea;
@ -940,6 +942,9 @@ class JavaThread: public Thread {
address _alternate_call_target;
} _jvmci;
// The JVMCIRuntime in a JVMCI shared library
JVMCIRuntime* _libjvmci_runtime;
// Support for high precision, thread sensitive counters in JVMCI compiled code.
jlong* _jvmci_counters;
@ -1219,6 +1224,12 @@ class JavaThread: public Thread {
virtual bool in_retryable_allocation() const { return _in_retryable_allocation; }
void set_in_retryable_allocation(bool b) { _in_retryable_allocation = b; }
JVMCIRuntime* libjvmci_runtime() const { return _libjvmci_runtime; }
void set_libjvmci_runtime(JVMCIRuntime* rt) {
assert((_libjvmci_runtime == nullptr && rt != nullptr) || (_libjvmci_runtime != nullptr && rt == nullptr), "must be");
_libjvmci_runtime = rt;
}
#endif // INCLUDE_JVMCI
// Exception handling for compiled methods

View File

@ -931,12 +931,12 @@ final class CompilerToVM {
* @param name name of current thread if in a native image otherwise {@code null}
* @see HotSpotJVMCIRuntime#attachCurrentThread
*/
native boolean attachCurrentThread(byte[] name, boolean asDaemon);
native boolean attachCurrentThread(byte[] name, boolean asDaemon, long[] javaVMInfo);
/**
* @see HotSpotJVMCIRuntime#detachCurrentThread()
* @see HotSpotJVMCIRuntime#detachCurrentThread
*/
native void detachCurrentThread();
native boolean detachCurrentThread(boolean release);
/**
* @see HotSpotJVMCIRuntime#exitHotSpot(int)

View File

@ -248,13 +248,9 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
}
/**
* Set of recognized {@code "jvmci.*"} system properties. Entries not associated with an
* {@link Option} have this object as their value.
* Set of recognized {@code "jvmci.*"} system properties.
*/
static final Map<String, Object> options = new HashMap<>();
static {
options.put("jvmci.class.path.append", options);
}
/**
* A list of all supported JVMCI options.
@ -1342,6 +1338,8 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
* Ensures the current thread is attached to the peer runtime.
*
* @param asDaemon if the thread is not yet attached, should it be attached as a daemon
* @param javaVMInfo if non-null, the JavaVM info as returned by {@link #registerNativeMethods}
* is returned in this array
* @return {@code true} if this call attached the current thread, {@code false} if the current
* thread was already attached
* @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
@ -1351,21 +1349,25 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
* @throws ArrayIndexOutOfBoundsException if {@code javaVMInfo} is non-null and is shorter than
* the length of the array returned by {@link #registerNativeMethods}
*/
public boolean attachCurrentThread(boolean asDaemon) {
public boolean attachCurrentThread(boolean asDaemon, long[] javaVMInfo) {
byte[] name = IS_IN_NATIVE_IMAGE ? Thread.currentThread().getName().getBytes() : null;
return compilerToVm.attachCurrentThread(name, asDaemon);
return compilerToVm.attachCurrentThread(name, asDaemon, javaVMInfo);
}
/**
* Detaches the current thread from the peer runtime.
*
* @param release if {@code true} and this is the last thread attached to the peer runtime, the
* {@code JavaVM} associated with the peer runtime is destroyed if possible
* @return {@code true} if the {@code JavaVM} associated with the peer runtime was destroyed as
* a result of this call
* @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
* {@code -XX:-UseJVMCINativeLibrary})
* @throws IllegalStateException if the peer runtime has not been initialized or if the current
* thread is not attached or if there is an error while trying to detach the thread
*/
public void detachCurrentThread() {
compilerToVm.detachCurrentThread();
public boolean detachCurrentThread(boolean release) {
return compilerToVm.detachCurrentThread(release);
}
/**