8242440: use separate, destroyable JavaVM instances per libgraal compiler thread
Reviewed-by: kvn, thartmann
This commit is contained in:
parent
b10833bbf3
commit
357b1b18c2
@ -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();
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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; }
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)},
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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") \
|
||||
\
|
||||
|
@ -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) \
|
||||
|
@ -431,7 +431,7 @@ void before_exit(JavaThread* thread) {
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
if (EnableJVMCI) {
|
||||
JVMCI::shutdown();
|
||||
JVMCI::shutdown(thread);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user