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();
|
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
|
// Print compilation timers and statistics
|
||||||
virtual void print_timers() {
|
virtual void print_timers() {
|
||||||
ShouldNotReachHere();
|
ShouldNotReachHere();
|
||||||
|
@ -422,7 +422,7 @@ void CompileQueue::free_all() {
|
|||||||
/**
|
/**
|
||||||
* Get the next CompileTask from a CompileQueue
|
* Get the next CompileTask from a CompileQueue
|
||||||
*/
|
*/
|
||||||
CompileTask* CompileQueue::get() {
|
CompileTask* CompileQueue::get(CompilerThread* thread) {
|
||||||
// save methods from RedefineClasses across safepoint
|
// save methods from RedefineClasses across safepoint
|
||||||
// across MethodCompileQueue_lock below.
|
// across MethodCompileQueue_lock below.
|
||||||
methodHandle save_method;
|
methodHandle save_method;
|
||||||
@ -440,6 +440,10 @@ CompileTask* CompileQueue::get() {
|
|||||||
return NULL;
|
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
|
// 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
|
// (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
|
// 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.
|
// We need this HandleMark to avoid leaking VM handles.
|
||||||
HandleMark hm(thread);
|
HandleMark hm(thread);
|
||||||
|
|
||||||
CompileTask* task = queue->get();
|
CompileTask* task = queue->get(thread);
|
||||||
if (task == NULL) {
|
if (task == NULL) {
|
||||||
if (UseDynamicNumberOfCompilerThreads) {
|
if (UseDynamicNumberOfCompilerThreads) {
|
||||||
// Access compiler_count under lock to enforce consistency.
|
// 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",
|
tty->print_cr("Removing compiler thread %s after " JLONG_FORMAT " ms idle time",
|
||||||
thread->name(), thread->idle_time_millis());
|
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
|
// Free buffer blob, if allocated
|
||||||
if (thread->get_buffer_blob() != NULL) {
|
if (thread->get_buffer_blob() != NULL) {
|
||||||
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
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);
|
post_compilation_event(event, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (runtime != nullptr) {
|
||||||
|
runtime->post_compile(thread);
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
#endif // INCLUDE_JVMCI
|
#endif // INCLUDE_JVMCI
|
||||||
{
|
{
|
||||||
|
@ -108,7 +108,7 @@ class CompileQueue : public CHeapObj<mtCompiler> {
|
|||||||
CompileTask* first() { return _first; }
|
CompileTask* first() { return _first; }
|
||||||
CompileTask* last() { return _last; }
|
CompileTask* last() { return _last; }
|
||||||
|
|
||||||
CompileTask* get();
|
CompileTask* get(CompilerThread* thread);
|
||||||
|
|
||||||
bool is_empty() const { return _first == NULL; }
|
bool is_empty() const { return _first == NULL; }
|
||||||
int size() const { return _size; }
|
int size() const { return _size; }
|
||||||
|
@ -37,18 +37,19 @@
|
|||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
#include "utilities/events.hpp"
|
#include "utilities/events.hpp"
|
||||||
|
|
||||||
JVMCIRuntime* JVMCI::_compiler_runtime = NULL;
|
JVMCIRuntime* JVMCI::_compiler_runtimes = nullptr;
|
||||||
JVMCIRuntime* JVMCI::_java_runtime = NULL;
|
JVMCIRuntime* JVMCI::_java_runtime = nullptr;
|
||||||
|
JVMCIRuntime* JVMCI::_shutdown_compiler_runtime = nullptr;
|
||||||
volatile bool JVMCI::_is_initialized = false;
|
volatile bool JVMCI::_is_initialized = false;
|
||||||
bool JVMCI::_box_caches_initialized = false;
|
bool JVMCI::_box_caches_initialized = false;
|
||||||
void* JVMCI::_shared_library_handle = NULL;
|
void* JVMCI::_shared_library_handle = nullptr;
|
||||||
char* JVMCI::_shared_library_path = NULL;
|
char* JVMCI::_shared_library_path = nullptr;
|
||||||
volatile bool JVMCI::_in_shutdown = false;
|
volatile bool JVMCI::_in_shutdown = false;
|
||||||
StringEventLog* JVMCI::_events = NULL;
|
StringEventLog* JVMCI::_events = nullptr;
|
||||||
StringEventLog* JVMCI::_verbose_events = NULL;
|
StringEventLog* JVMCI::_verbose_events = nullptr;
|
||||||
volatile intx JVMCI::_fatal_log_init_thread = -1;
|
volatile intx JVMCI::_fatal_log_init_thread = -1;
|
||||||
volatile int JVMCI::_fatal_log_fd = -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;
|
void jvmci_vmStructs_init() NOT_DEBUG_RETURN;
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ void* JVMCI::get_shared_library(char*& path, bool load) {
|
|||||||
path = _shared_library_path;
|
path = _shared_library_path;
|
||||||
return sl_handle;
|
return sl_handle;
|
||||||
}
|
}
|
||||||
assert(JVMCI_lock->owner() == Thread::current(), "must be");
|
MutexLocker locker(JVMCI_lock);
|
||||||
path = NULL;
|
path = NULL;
|
||||||
if (_shared_library_handle == NULL) {
|
if (_shared_library_handle == NULL) {
|
||||||
char path[JVM_MAXPATHLEN];
|
char path[JVM_MAXPATHLEN];
|
||||||
@ -103,8 +104,13 @@ void JVMCI::initialize_compiler(TRAPS) {
|
|||||||
JNIJVMCI::initialize_ids(NULL);
|
JNIJVMCI::initialize_ids(NULL);
|
||||||
ShouldNotReachHere();
|
ShouldNotReachHere();
|
||||||
}
|
}
|
||||||
|
JVMCIRuntime* runtime;
|
||||||
JVMCI::compiler_runtime()->call_getCompiler(CHECK);
|
if (UseJVMCINativeLibrary) {
|
||||||
|
runtime = JVMCI::compiler_runtime((JavaThread*) THREAD);
|
||||||
|
} else {
|
||||||
|
runtime = JVMCI::java_runtime();
|
||||||
|
}
|
||||||
|
runtime->call_getCompiler(CHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMCI::initialize_globals() {
|
void JVMCI::initialize_globals() {
|
||||||
@ -122,13 +128,9 @@ void JVMCI::initialize_globals() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (UseJVMCINativeLibrary) {
|
_java_runtime = new JVMCIRuntime(nullptr, -1, false);
|
||||||
// There are two runtimes.
|
if (using_singleton_shared_library_runtime()) {
|
||||||
_compiler_runtime = new JVMCIRuntime(0);
|
JVMCI::_compiler_runtimes = new JVMCIRuntime(nullptr, 0, true);
|
||||||
_java_runtime = new JVMCIRuntime(-1);
|
|
||||||
} else {
|
|
||||||
// There is only a single runtime
|
|
||||||
_java_runtime = _compiler_runtime = new JVMCIRuntime(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +160,16 @@ void JVMCI::ensure_box_caches_initialized(TRAPS) {
|
|||||||
_box_caches_initialized = true;
|
_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) {
|
JavaThread* JVMCI::compilation_tick(JavaThread* thread) {
|
||||||
if (thread->is_Compiler_thread()) {
|
if (thread->is_Compiler_thread()) {
|
||||||
CompileTask *task = CompilerThread::cast(thread)->task();
|
CompileTask *task = CompilerThread::cast(thread)->task();
|
||||||
@ -172,21 +184,27 @@ JavaThread* JVMCI::compilation_tick(JavaThread* thread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void JVMCI::metadata_do(void f(Metadata*)) {
|
void JVMCI::metadata_do(void f(Metadata*)) {
|
||||||
if (_java_runtime != NULL) {
|
if (_java_runtime != nullptr) {
|
||||||
_java_runtime->_metadata_handles->metadata_do(f);
|
_java_runtime->_metadata_handles->metadata_do(f);
|
||||||
}
|
}
|
||||||
if (_compiler_runtime != NULL && _compiler_runtime != _java_runtime) {
|
for (JVMCIRuntime* runtime = _compiler_runtimes; runtime != nullptr; runtime = runtime->_next) {
|
||||||
_compiler_runtime->_metadata_handles->metadata_do(f);
|
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) {
|
void JVMCI::do_unloading(bool unloading_occurred) {
|
||||||
if (unloading_occurred) {
|
if (unloading_occurred) {
|
||||||
if (_java_runtime != NULL) {
|
if (_java_runtime != nullptr) {
|
||||||
_java_runtime->_metadata_handles->do_unloading();
|
_java_runtime->_metadata_handles->do_unloading();
|
||||||
}
|
}
|
||||||
if (_compiler_runtime != NULL && _compiler_runtime != _java_runtime) {
|
for (JVMCIRuntime* runtime = _compiler_runtimes; runtime != nullptr; runtime = runtime->_next) {
|
||||||
_compiler_runtime->_metadata_handles->do_unloading();
|
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;
|
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;
|
ResourceMark rm;
|
||||||
{
|
{
|
||||||
MutexLocker locker(JVMCI_lock);
|
MutexLocker locker(JVMCI_lock);
|
||||||
@ -203,11 +258,29 @@ void JVMCI::shutdown() {
|
|||||||
JVMCI_event_1("shutting down JVMCI");
|
JVMCI_event_1("shutting down JVMCI");
|
||||||
}
|
}
|
||||||
JVMCIRuntime* java_runtime = _java_runtime;
|
JVMCIRuntime* java_runtime = _java_runtime;
|
||||||
if (java_runtime != compiler_runtime()) {
|
if (java_runtime != nullptr) {
|
||||||
java_runtime->shutdown();
|
java_runtime->shutdown();
|
||||||
}
|
}
|
||||||
if (compiler_runtime() != NULL) {
|
JVMCIRuntime* runtime = thread->libjvmci_runtime();
|
||||||
compiler_runtime()->shutdown();
|
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.write(buf, count);
|
||||||
log.flush();
|
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;
|
friend class JVMCIEnv;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Access to the HotSpotJVMCIRuntime used by the CompileBroker.
|
// List of libjvmci based JVMCIRuntimes.
|
||||||
static JVMCIRuntime* _compiler_runtime;
|
// 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()
|
// True when at least one JVMCIRuntime::initialize_HotSpotJVMCIRuntime()
|
||||||
// execution has completed successfully.
|
// execution has completed successfully.
|
||||||
@ -95,6 +102,7 @@ class JVMCI : public AllStatic {
|
|||||||
static Thread* current_thread_or_null();
|
static Thread* current_thread_or_null();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum CodeInstallResult {
|
enum CodeInstallResult {
|
||||||
ok,
|
ok,
|
||||||
dependencies_failed,
|
dependencies_failed,
|
||||||
@ -104,10 +112,19 @@ class JVMCI : public AllStatic {
|
|||||||
first_permanent_bailout = code_too_large
|
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
|
// Gets the handle to the loaded JVMCI shared library, loading it
|
||||||
// first if not yet loaded and `load` is true. The path from
|
// first if not yet loaded and `load` is true. The path from
|
||||||
// which the library is loaded is returned in `path`. If
|
// which the library is loaded is returned in `path`.
|
||||||
// `load` is true then JVMCI_lock must be locked.
|
|
||||||
static void* get_shared_library(char*& path, bool load);
|
static void* get_shared_library(char*& path, bool load);
|
||||||
|
|
||||||
// Logs the fatal crash data in `buf` to the appropriate stream.
|
// 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 metadata_do(void f(Metadata*));
|
||||||
|
|
||||||
static void shutdown();
|
static void shutdown(JavaThread* thread);
|
||||||
|
|
||||||
// Returns whether JVMCI::shutdown has been called.
|
// Returns whether JVMCI::shutdown has been called.
|
||||||
static bool in_shutdown();
|
static bool in_shutdown();
|
||||||
@ -145,11 +162,16 @@ class JVMCI : public AllStatic {
|
|||||||
// Returns `thread`.
|
// Returns `thread`.
|
||||||
static JavaThread* compilation_tick(JavaThread* 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
|
// Gets the single runtime for JVMCI on the Java heap. This is the only
|
||||||
// JVMCI runtime available when !UseJVMCINativeLibrary.
|
// JVMCI runtime available when !UseJVMCINativeLibrary.
|
||||||
static JVMCIRuntime* java_runtime() { return _java_runtime; }
|
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`
|
// 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);
|
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();
|
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
|
// Print CompileBroker compilation timers
|
||||||
void JVMCICompiler::print_timers() {
|
void JVMCICompiler::print_timers() {
|
||||||
double code_install_time = _codeInstallTimer.seconds();
|
double code_install_time = _codeInstallTimer.seconds();
|
||||||
|
@ -94,6 +94,10 @@ public:
|
|||||||
// Compilation entry point for methods
|
// Compilation entry point for methods
|
||||||
virtual void compile_method(ciEnv* env, ciMethod* target, int entry_bci, bool install_code, DirectiveSet* directive);
|
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
|
// Print compilation timers and statistics
|
||||||
virtual void print_timers();
|
virtual void print_timers();
|
||||||
|
|
||||||
|
@ -1089,7 +1089,6 @@ C2V_VMENTRY_NULL(jlongArray, getLineNumberTable, (JNIEnv* env, jobject, jobject
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
jlong value;
|
jlong value;
|
||||||
while (stream.read_pair()) {
|
while (stream.read_pair()) {
|
||||||
// FIXME: Why was this long before?
|
|
||||||
value = ((jlong) stream.bci());
|
value = ((jlong) stream.bci());
|
||||||
JVMCIENV->put_long_at(result, i, value);
|
JVMCIENV->put_long_at(result, i, value);
|
||||||
value = ((jlong) stream.line());
|
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) {
|
if (x == NULL || y == NULL) {
|
||||||
JVMCI_THROW_0(NullPointerException);
|
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_END
|
||||||
|
|
||||||
C2V_VMENTRY_NULL(jobject, getJavaMirror, (JNIEnv* env, jobject, jobject object))
|
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);
|
return type2aelembytes(type);
|
||||||
C2V_END
|
C2V_END
|
||||||
|
|
||||||
C2V_VMENTRY(void, deleteGlobalHandle, (JNIEnv* env, jobject, jlong h))
|
C2V_VMENTRY(void, deleteGlobalHandle, (JNIEnv* env, jobject, jlong handle))
|
||||||
jobject handle = (jobject)(address)h;
|
if (handle != 0) {
|
||||||
if (handle != NULL) {
|
JVMCIENV->runtime()->destroy_oop_handle(handle);
|
||||||
JVMCIENV->runtime()->destroy_global(handle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2242,13 +2240,13 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas
|
|||||||
requireInHotSpot("registerNativeMethods", JVMCI_CHECK_NULL);
|
requireInHotSpot("registerNativeMethods", JVMCI_CHECK_NULL);
|
||||||
char* sl_path;
|
char* sl_path;
|
||||||
void* sl_handle;
|
void* sl_handle;
|
||||||
JVMCIRuntime* runtime = JVMCI::compiler_runtime();
|
JVMCIRuntime* runtime;
|
||||||
{
|
{
|
||||||
// Ensure the JVMCI shared library runtime is initialized.
|
// Ensure the JVMCI shared library runtime is initialized.
|
||||||
JVMCIEnv __peer_jvmci_env__(thread, false, __FILE__, __LINE__);
|
JVMCIEnv __peer_jvmci_env__(thread, false, __FILE__, __LINE__);
|
||||||
JVMCIEnv* peerEnv = &__peer_jvmci_env__;
|
JVMCIEnv* peerEnv = &__peer_jvmci_env__;
|
||||||
HandleMark hm(THREAD);
|
HandleMark hm(THREAD);
|
||||||
JVMCIObject receiver = runtime->get_HotSpotJVMCIRuntime(peerEnv);
|
runtime = JVMCI::compiler_runtime(thread);
|
||||||
if (peerEnv->has_pending_exception()) {
|
if (peerEnv->has_pending_exception()) {
|
||||||
peerEnv->describe_pending_exception(true);
|
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))
|
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
|
// Called from unattached JVMCI shared library thread
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2331,8 +2329,8 @@ C2V_VMENTRY_PREFIX(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject c2vm
|
|||||||
if (thread->jni_environment() == env) {
|
if (thread->jni_environment() == env) {
|
||||||
C2V_BLOCK(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject))
|
C2V_BLOCK(jboolean, isCurrentThreadAttached, (JNIEnv* env, jobject))
|
||||||
requireJVMCINativeLibrary(JVMCI_CHECK_0);
|
requireJVMCINativeLibrary(JVMCI_CHECK_0);
|
||||||
JVMCIRuntime* runtime = JVMCI::compiler_runtime();
|
JVMCIRuntime* runtime = thread->libjvmci_runtime();
|
||||||
if (runtime == NULL || !runtime->has_shared_library_javavm()) {
|
if (runtime == nullptr || !runtime->has_shared_library_javavm()) {
|
||||||
JVMCI_THROW_MSG_0(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in isCurrentThreadAttached");
|
JVMCI_THROW_MSG_0(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in isCurrentThreadAttached");
|
||||||
}
|
}
|
||||||
JNIEnv* peerEnv;
|
JNIEnv* peerEnv;
|
||||||
@ -2350,11 +2348,13 @@ C2V_VMENTRY_PREFIX(jlong, getCurrentJavaThread, (JNIEnv* env, jobject c2vm))
|
|||||||
return (jlong) p2i(thread);
|
return (jlong) p2i(thread);
|
||||||
C2V_END
|
C2V_END
|
||||||
|
|
||||||
C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jbyteArray name, jboolean as_daemon))
|
// Attaches a thread started in a JVMCI shared library to a JavaThread and JVMCI runtime.
|
||||||
if (thread == NULL) {
|
static void attachSharedLibraryThread(JNIEnv* env, jbyteArray name, jboolean as_daemon) {
|
||||||
// Called from unattached JVMCI shared library thread
|
JavaVM* javaVM = nullptr;
|
||||||
guarantee(name != NULL, "libjvmci caller must pass non-null name");
|
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;
|
extern struct JavaVM_ main_vm;
|
||||||
JNIEnv* hotspotEnv;
|
JNIEnv* hotspotEnv;
|
||||||
|
|
||||||
@ -2366,12 +2366,28 @@ C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jb
|
|||||||
JavaVMAttachArgs attach_args;
|
JavaVMAttachArgs attach_args;
|
||||||
attach_args.version = JNI_VERSION_1_2;
|
attach_args.version = JNI_VERSION_1_2;
|
||||||
attach_args.name = name_buf;
|
attach_args.name = name_buf;
|
||||||
attach_args.group = NULL;
|
attach_args.group = nullptr;
|
||||||
jint res = as_daemon ? main_vm.AttachCurrentThreadAsDaemon((void**) &hotspotEnv, &attach_args) :
|
res = as_daemon ? main_vm.AttachCurrentThreadAsDaemon((void**)&hotspotEnv, &attach_args) :
|
||||||
main_vm.AttachCurrentThread((void**)&hotspotEnv, &attach_args);
|
main_vm.AttachCurrentThread((void**)&hotspotEnv, &attach_args);
|
||||||
if (res != JNI_OK) {
|
if (res != JNI_OK) {
|
||||||
JNI_THROW_("attachCurrentThread", InternalError, err_msg("Trying to attach thread returned %d", res), false);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
JVMCITraceMark jtm("attachCurrentThread");
|
JVMCITraceMark jtm("attachCurrentThread");
|
||||||
@ -2379,25 +2395,43 @@ C2V_VMENTRY_PREFIX(jboolean, attachCurrentThread, (JNIEnv* env, jobject c2vm, jb
|
|||||||
// Called from HotSpot
|
// Called from HotSpot
|
||||||
C2V_BLOCK(jboolean, attachCurrentThread, (JNIEnv* env, jobject, jboolean))
|
C2V_BLOCK(jboolean, attachCurrentThread, (JNIEnv* env, jobject, jboolean))
|
||||||
requireJVMCINativeLibrary(JVMCI_CHECK_0);
|
requireJVMCINativeLibrary(JVMCI_CHECK_0);
|
||||||
JVMCIRuntime* runtime = JVMCI::compiler_runtime();
|
|
||||||
if (runtime == NULL || !runtime->has_shared_library_javavm()) {
|
JVMCIRuntime* runtime = JVMCI::compiler_runtime(thread);
|
||||||
JVMCI_THROW_MSG_0(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in attachCurrentThread");
|
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;
|
JavaVMAttachArgs attach_args;
|
||||||
attach_args.version = JNI_VERSION_1_2;
|
attach_args.version = JNI_VERSION_1_2;
|
||||||
attach_args.name = const_cast<char*>(thread->name());
|
attach_args.name = const_cast<char*>(thread->name());
|
||||||
attach_args.group = NULL;
|
attach_args.group = nullptr;
|
||||||
JNIEnv* peerJNIEnv;
|
|
||||||
if (runtime->GetEnv(thread, (void**) &peerJNIEnv, JNI_VERSION_1_2) == JNI_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
jint res = as_daemon ? runtime->AttachCurrentThreadAsDaemon(thread, (void**) &peerJNIEnv, &attach_args) :
|
jint res = as_daemon ? runtime->AttachCurrentThreadAsDaemon(thread, (void**) &peerJNIEnv, &attach_args) :
|
||||||
runtime->AttachCurrentThread(thread, (void**) &peerJNIEnv, &attach_args);
|
runtime->AttachCurrentThread(thread, (void**) &peerJNIEnv, &attach_args);
|
||||||
|
|
||||||
if (res == JNI_OK) {
|
if (res == JNI_OK) {
|
||||||
guarantee(peerJNIEnv != NULL, "must be");
|
guarantee(peerJNIEnv != nullptr, "must be");
|
||||||
JVMCI_event_1("attached to JavaVM for JVMCI runtime %d", runtime->id());
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
JVMCI_THROW_MSG_0(InternalError, err_msg("Error %d while attaching %s", res, attach_args.name));
|
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;
|
return false;
|
||||||
C2V_END
|
C2V_END
|
||||||
|
|
||||||
C2V_VMENTRY_PREFIX(void, detachCurrentThread, (JNIEnv* env, jobject c2vm))
|
C2V_VMENTRY_PREFIX(jboolean, detachCurrentThread, (JNIEnv* env, jobject c2vm, jboolean release))
|
||||||
if (thread == NULL) {
|
if (thread == nullptr) {
|
||||||
// Called from unattached JVMCI shared library thread
|
// 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");
|
JVMCITraceMark jtm("detachCurrentThread");
|
||||||
if (thread->jni_environment() == env) {
|
if (thread->jni_environment() == env) {
|
||||||
// Called from HotSpot
|
// Called from HotSpot
|
||||||
C2V_BLOCK(void, detachCurrentThread, (JNIEnv* env, jobject))
|
C2V_BLOCK(void, detachCurrentThread, (JNIEnv* env, jobject))
|
||||||
requireJVMCINativeLibrary(JVMCI_CHECK);
|
requireJVMCINativeLibrary(JVMCI_CHECK_0);
|
||||||
requireInHotSpot("detachCurrentThread", JVMCI_CHECK);
|
requireInHotSpot("detachCurrentThread", JVMCI_CHECK_0);
|
||||||
JVMCIRuntime* runtime = JVMCI::compiler_runtime();
|
JVMCIRuntime* runtime = thread->libjvmci_runtime();
|
||||||
if (runtime == NULL || !runtime->has_shared_library_javavm()) {
|
if (runtime == nullptr || !runtime->has_shared_library_javavm()) {
|
||||||
JVMCI_THROW_MSG(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in detachCurrentThread");
|
JVMCI_THROW_MSG_0(IllegalStateException, "Require JVMCI shared library JavaVM to be initialized in detachCurrentThread");
|
||||||
}
|
}
|
||||||
JNIEnv* peerJNIEnv;
|
JNIEnv* peerEnv;
|
||||||
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()));
|
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);
|
jint res = runtime->DetachCurrentThread(thread);
|
||||||
if (res != JNI_OK) {
|
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 {
|
} else {
|
||||||
// Called from attached JVMCI shared library thread
|
// 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;
|
extern struct JavaVM_ main_vm;
|
||||||
jint res = main_vm.DetachCurrentThread();
|
jint res = main_vm.DetachCurrentThread();
|
||||||
if (res != JNI_OK) {
|
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_END
|
||||||
|
|
||||||
C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jboolean callPostTranslation))
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
jobject global_handle = (jobject) obj_handle;
|
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();
|
jobject result = JVMCIENV->make_local(global_handle_obj).as_jobject();
|
||||||
|
|
||||||
JVMCIENV->destroy_global(global_handle_obj);
|
JVMCIENV->destroy_global(global_handle_obj);
|
||||||
@ -2858,8 +2914,8 @@ JNINativeMethod CompilerToVM::methods[] = {
|
|||||||
{CC "registerNativeMethods", CC "(" CLASS ")[J", FN_PTR(registerNativeMethods)},
|
{CC "registerNativeMethods", CC "(" CLASS ")[J", FN_PTR(registerNativeMethods)},
|
||||||
{CC "isCurrentThreadAttached", CC "()Z", FN_PTR(isCurrentThreadAttached)},
|
{CC "isCurrentThreadAttached", CC "()Z", FN_PTR(isCurrentThreadAttached)},
|
||||||
{CC "getCurrentJavaThread", CC "()J", FN_PTR(getCurrentJavaThread)},
|
{CC "getCurrentJavaThread", CC "()J", FN_PTR(getCurrentJavaThread)},
|
||||||
{CC "attachCurrentThread", CC "([BZ)Z", FN_PTR(attachCurrentThread)},
|
{CC "attachCurrentThread", CC "([BZ[J)Z", FN_PTR(attachCurrentThread)},
|
||||||
{CC "detachCurrentThread", CC "()V", FN_PTR(detachCurrentThread)},
|
{CC "detachCurrentThread", CC "(Z)Z", FN_PTR(detachCurrentThread)},
|
||||||
{CC "translate", CC "(" OBJECT "Z)J", FN_PTR(translate)},
|
{CC "translate", CC "(" OBJECT "Z)J", FN_PTR(translate)},
|
||||||
{CC "unhand", CC "(J)" OBJECT, FN_PTR(unhand)},
|
{CC "unhand", CC "(J)" OBJECT, FN_PTR(unhand)},
|
||||||
{CC "updateHotSpotNmethod", CC "(" HS_NMETHOD ")V", FN_PTR(updateHotSpotNmethod)},
|
{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();
|
_runtime = JVMCI::java_runtime();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_runtime = JVMCI::compiler_runtime();
|
_runtime = thread->libjvmci_runtime();
|
||||||
assert(_runtime != NULL, "npe");
|
assert(_runtime != NULL, "npe");
|
||||||
_env = parent_env;
|
_env = parent_env;
|
||||||
return;
|
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 loaded and initialized and get a shared library JNIEnv
|
||||||
_is_hotspot = false;
|
_is_hotspot = false;
|
||||||
|
|
||||||
_runtime = JVMCI::compiler_runtime();
|
_runtime = JVMCI::compiler_runtime(thread);
|
||||||
_env = _runtime->init_shared_library_javavm();
|
_env = _runtime->init_shared_library_javavm();
|
||||||
|
|
||||||
if (_env != NULL) {
|
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);
|
HotSpotJVMCI::HotSpotObjectConstantImpl::set_compressed(this, constant, compressed);
|
||||||
return wrap(constant);
|
return wrap(constant);
|
||||||
} else {
|
} else {
|
||||||
jlong handle = make_handle(obj);
|
jlong handle = make_oop_handle(obj);
|
||||||
JNIAccessMark jni(this, THREAD);
|
JNIAccessMark jni(this, THREAD);
|
||||||
jobject result = jni()->NewObject(JNIJVMCI::IndirectHotSpotObjectConstantImpl::clazz(),
|
jobject result = jni()->NewObject(JNIJVMCI::IndirectHotSpotObjectConstantImpl::clazz(),
|
||||||
JNIJVMCI::IndirectHotSpotObjectConstantImpl::constructor(),
|
JNIJVMCI::IndirectHotSpotObjectConstantImpl::constructor(),
|
||||||
@ -1407,7 +1407,7 @@ Handle JVMCIEnv::asConstant(JVMCIObject constant, JVMCI_TRAPS) {
|
|||||||
if (object_handle == 0L) {
|
if (object_handle == 0L) {
|
||||||
JVMCI_THROW_MSG_(NullPointerException, "Foreign object reference has been cleared", Handle());
|
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) {
|
if (result == NULL) {
|
||||||
JVMCI_THROW_MSG_(InternalError, "Constant was unexpectedly NULL", Handle());
|
JVMCI_THROW_MSG_(InternalError, "Constant was unexpectedly NULL", Handle());
|
||||||
}
|
}
|
||||||
@ -1421,15 +1421,14 @@ JVMCIObject JVMCIEnv::wrap(jobject object) {
|
|||||||
return JVMCIObject::create(object, is_hotspot());
|
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");
|
assert(!obj.is_null(), "should only create handle for non-NULL oops");
|
||||||
jobject handle = _runtime->make_global(obj);
|
return _runtime->make_oop_handle(obj);
|
||||||
return (jlong) handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oop JVMCIEnv::resolve_handle(jlong objectHandle) {
|
oop JVMCIEnv::resolve_oop_handle(jlong oopHandle) {
|
||||||
assert(objectHandle != 0, "should be a valid handle");
|
assert(oopHandle != 0, "should be a valid handle");
|
||||||
oop obj = *((oopDesc**)objectHandle);
|
oop obj = *((oopDesc**) oopHandle);
|
||||||
if (obj != NULL) {
|
if (obj != NULL) {
|
||||||
oopDesc::verify(obj);
|
oopDesc::verify(obj);
|
||||||
}
|
}
|
||||||
|
@ -392,8 +392,11 @@ public:
|
|||||||
JVMCIObject new_HotSpotStackFrameReference(JVMCI_TRAPS);
|
JVMCIObject new_HotSpotStackFrameReference(JVMCI_TRAPS);
|
||||||
JVMCIObject new_JVMCIError(JVMCI_TRAPS);
|
JVMCIObject new_JVMCIError(JVMCI_TRAPS);
|
||||||
|
|
||||||
jlong make_handle(const Handle& obj);
|
// Makes a handle to a HotSpot heap object. These handles are
|
||||||
oop resolve_handle(jlong objectHandle);
|
// 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
|
// These are analogous to the JNI routines
|
||||||
JVMCIObject make_local(JVMCIObject object);
|
JVMCIObject make_local(JVMCIObject object);
|
||||||
|
@ -80,8 +80,12 @@ 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);
|
fatal("Could not find field %s.%s with signature %s", ik->external_name(), name, signature);
|
||||||
}
|
}
|
||||||
guarantee(fd.is_static() == static_field, "static/instance mismatch");
|
guarantee(fd.is_static() == static_field, "static/instance mismatch");
|
||||||
|
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();
|
dest_offset = fd.offset();
|
||||||
assert(dest_offset != 0, "must be valid offset");
|
|
||||||
if (static_field) {
|
if (static_field) {
|
||||||
// Must ensure classes for static fields are initialized as the
|
// Must ensure classes for static fields are initialized as the
|
||||||
// accessor itself does not include a class initialization check.
|
// accessor itself does not include a class initialization check.
|
||||||
@ -89,6 +93,7 @@ void HotSpotJVMCI::compute_offset(int &dest_offset, Klass* klass, const char* na
|
|||||||
}
|
}
|
||||||
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
|
#ifndef PRODUCT
|
||||||
static void check_resolve_method(const char* call_type, Klass* resolved_klass, Symbol* method_name, Symbol* method_signature, TRAPS) {
|
static void check_resolve_method(const char* call_type, Klass* resolved_klass, Symbol* method_name, Symbol* method_signature, TRAPS) {
|
||||||
@ -122,9 +127,16 @@ 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); \
|
Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::fullClassName(), true, CHECK); \
|
||||||
className::_klass = InstanceKlass::cast(k); \
|
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)); \
|
JVMCI_event_2(" klass for %s = " PTR_FORMAT, k->external_name(), p2i(k)); \
|
||||||
className::_klass->initialize(CHECK);
|
className::_klass = InstanceKlass::cast(k); \
|
||||||
|
className::_klass->initialize(CHECK); \
|
||||||
|
}
|
||||||
|
|
||||||
#define END_CLASS }
|
#define END_CLASS }
|
||||||
|
|
||||||
@ -300,7 +312,16 @@ void JNIJVMCI::initialize_field_id(JNIEnv* env, jfieldID &fieldid, jclass clazz,
|
|||||||
// Class initialization barrier
|
// Class initialization barrier
|
||||||
fieldid = env->GetFieldID(clazz, name, signature);
|
fieldid = env->GetFieldID(clazz, name, signature);
|
||||||
}
|
}
|
||||||
|
// 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));
|
JVMCI_event_2(" jfieldID for %s %s.%s = " PTR_FORMAT, signature, class_name, name, p2i(fieldid));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (env->ExceptionCheck()) {
|
if (env->ExceptionCheck()) {
|
||||||
env->ExceptionDescribe();
|
env->ExceptionDescribe();
|
||||||
@ -312,16 +333,26 @@ void JNIJVMCI::initialize_field_id(JNIEnv* env, jfieldID &fieldid, jclass clazz,
|
|||||||
|
|
||||||
#define START_CLASS(className, fullClassName) { \
|
#define START_CLASS(className, fullClassName) { \
|
||||||
current_class_name = vmSymbols::fullClassName()->as_C_string(); \
|
current_class_name = vmSymbols::fullClassName()->as_C_string(); \
|
||||||
if (JVMCILibDumpJNIConfig != NULL) { \
|
if (JVMCILibDumpJNIConfig != nullptr) { \
|
||||||
fileStream* st = JVMCIGlobals::get_jni_config_file(); \
|
fileStream* st = JVMCIGlobals::get_jni_config_file(); \
|
||||||
st->print_cr("class %s", current_class_name); \
|
st->print_cr("class %s", current_class_name); \
|
||||||
} else { \
|
} else { \
|
||||||
jclass k = env->FindClass(current_class_name); \
|
jclass k = env->FindClass(current_class_name); \
|
||||||
JVMCI_EXCEPTION_CHECK(env, "FindClass(%s)", 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); \
|
k = (jclass) env->NewGlobalRef(k); \
|
||||||
|
jclass current = className::_class; \
|
||||||
|
if (current != k) { \
|
||||||
JVMCI_event_2(" jclass for %s = " PTR_FORMAT, current_class_name, p2i(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; \
|
className::_class = k; \
|
||||||
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define END_CLASS current_class_name = NULL; }
|
#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 STATIC_BOOLEAN_FIELD(className, name) FIELD(className, name, "Z", true)
|
||||||
|
|
||||||
#define GET_JNI_METHOD(jniGetMethod, dst, clazz, methodName, signature) \
|
#define GET_JNI_METHOD(jniGetMethod, dst, clazz, methodName, signature) \
|
||||||
if (JVMCILibDumpJNIConfig != NULL) { \
|
if (JVMCILibDumpJNIConfig != nullptr) { \
|
||||||
fileStream* st = JVMCIGlobals::get_jni_config_file(); \
|
fileStream* st = JVMCIGlobals::get_jni_config_file(); \
|
||||||
st->print_cr("method %s %s %s", current_class_name, methodName, signature); \
|
st->print_cr("method %s %s %s", current_class_name, methodName, signature); \
|
||||||
} else { \
|
} else { \
|
||||||
jmethodID current = dst; \
|
jmethodID current = dst; \
|
||||||
dst = env->jniGetMethod(clazz, methodName, signature); \
|
dst = env->jniGetMethod(clazz, methodName, signature); \
|
||||||
JVMCI_EXCEPTION_CHECK(env, #jniGetMethod "(%s.%s%s)", \
|
assert(dst != nullptr, "uninitialized"); \
|
||||||
current_class_name, methodName, signature); \
|
if (current != dst) { \
|
||||||
assert(dst != NULL, "uninitialized"); \
|
|
||||||
JVMCI_event_2(" jmethodID for %s.%s%s = " PTR_FORMAT, \
|
JVMCI_event_2(" jmethodID for %s.%s%s = " PTR_FORMAT, \
|
||||||
current_class_name, methodName, signature, p2i(dst)); \
|
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) \
|
#define GET_JNI_CONSTRUCTOR(clazz, signature) \
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include "runtime/frame.inline.hpp"
|
#include "runtime/frame.inline.hpp"
|
||||||
#include "runtime/java.hpp"
|
#include "runtime/java.hpp"
|
||||||
#include "runtime/jniHandles.inline.hpp"
|
#include "runtime/jniHandles.inline.hpp"
|
||||||
|
#include "runtime/mutex.hpp"
|
||||||
#include "runtime/reflectionUtils.hpp"
|
#include "runtime/reflectionUtils.hpp"
|
||||||
#include "runtime/sharedRuntime.hpp"
|
#include "runtime/sharedRuntime.hpp"
|
||||||
#if INCLUDE_G1GC
|
#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.
|
// Handles to objects in the Hotspot heap.
|
||||||
static OopStorage* object_handles() {
|
static OopStorage* object_handles() {
|
||||||
return Universe::vm_global();
|
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(!Universe::heap()->is_gc_active(), "can't extend the root set during GC");
|
||||||
assert(oopDesc::is_oop(obj()), "not an oop");
|
assert(oopDesc::is_oop(obj()), "not an oop");
|
||||||
oop* ptr = object_handles()->allocate();
|
oop* ptr = object_handles()->allocate();
|
||||||
jobject res = NULL;
|
jlong res = 0;
|
||||||
if (ptr != NULL) {
|
if (ptr != nullptr) {
|
||||||
assert(*ptr == NULL, "invariant");
|
assert(*ptr == nullptr, "invariant");
|
||||||
NativeAccess<>::oop_store(ptr, obj());
|
NativeAccess<>::oop_store(ptr, obj());
|
||||||
res = reinterpret_cast<jobject>(ptr);
|
res = (jlong) ptr;
|
||||||
} else {
|
} else {
|
||||||
vm_exit_out_of_memory(sizeof(oop), OOM_MALLOC_ERROR,
|
vm_exit_out_of_memory(sizeof(oop), OOM_MALLOC_ERROR,
|
||||||
"Cannot create JVMCI oop handle");
|
"Cannot create JVMCI oop handle");
|
||||||
}
|
}
|
||||||
MutexLocker ml(JVMCI_lock);
|
MutexLocker ml(_lock);
|
||||||
|
_oop_handles.append(ptr);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMCIRuntime::destroy_global(jobject handle) {
|
bool JVMCIRuntime::probe_oop_handle(jlong handle, int index) {
|
||||||
// Assert before nulling out, for better debugging.
|
oop* key = (oop*) handle;
|
||||||
assert(is_global_handle(handle), "precondition");
|
if (key == _oop_handles.at(index)) {
|
||||||
oop* oop_ptr = reinterpret_cast<oop*>(handle);
|
_last_found_oop_handle_index = index;
|
||||||
NativeAccess<>::oop_store(oop_ptr, (oop)NULL);
|
return true;
|
||||||
object_handles()->release(oop_ptr);
|
}
|
||||||
MutexLocker ml(JVMCI_lock);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JVMCIRuntime::is_global_handle(jobject handle) {
|
int JVMCIRuntime::find_oop_handle(jlong handle) {
|
||||||
const oop* ptr = reinterpret_cast<oop*>(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;
|
return object_handles()->allocation_status(ptr) == OopStorage::ALLOCATED_ENTRY;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmetadata JVMCIRuntime::allocate_handle(const methodHandle& handle) {
|
jmetadata JVMCIRuntime::allocate_handle(const methodHandle& handle) {
|
||||||
MutexLocker ml(JVMCI_lock);
|
MutexLocker ml(_lock);
|
||||||
return _metadata_handles->allocate_handle(handle);
|
return _metadata_handles->allocate_handle(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
jmetadata JVMCIRuntime::allocate_handle(const constantPoolHandle& handle) {
|
jmetadata JVMCIRuntime::allocate_handle(const constantPoolHandle& handle) {
|
||||||
MutexLocker ml(JVMCI_lock);
|
MutexLocker ml(_lock);
|
||||||
return _metadata_handles->allocate_handle(handle);
|
return _metadata_handles->allocate_handle(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMCIRuntime::release_handle(jmetadata handle) {
|
void JVMCIRuntime::release_handle(jmetadata handle) {
|
||||||
MutexLocker ml(JVMCI_lock);
|
MutexLocker ml(_lock);
|
||||||
_metadata_handles->chain_free_list(handle);
|
_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
|
// Function for shared library JavaVM to exit HotSpot on a fatal error
|
||||||
static void _fatal() {
|
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();
|
intx current_thread_id = os::current_thread_id();
|
||||||
fatal("thread " INTX_FORMAT ": Fatal error in JVMCI shared library", current_thread_id);
|
fatal("thread " INTX_FORMAT ": Fatal error in JVMCI shared library", current_thread_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEnv* JVMCIRuntime::init_shared_library_javavm() {
|
JVMCIRuntime::JVMCIRuntime(JVMCIRuntime* next, int id, bool for_compile_broker) :
|
||||||
JavaVM* javaVM = (JavaVM*) _shared_library_javavm;
|
_init_state(uninitialized),
|
||||||
if (javaVM == NULL) {
|
_shared_library_javavm(nullptr),
|
||||||
MutexLocker locker(JVMCI_lock);
|
_shared_library_javavm_id(0),
|
||||||
// Check again under JVMCI_lock
|
_id(id),
|
||||||
javaVM = (JavaVM*) _shared_library_javavm;
|
_next(next),
|
||||||
if (javaVM != NULL) {
|
_metadata_handles(new MetadataHandles()),
|
||||||
return NULL;
|
_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;
|
char* sl_path;
|
||||||
void* sl_handle = JVMCI::get_shared_library(sl_path, true);
|
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);
|
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"));
|
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);
|
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.options = options;
|
||||||
vm_args.nOptions = sizeof(options) / sizeof(JavaVMOption);
|
vm_args.nOptions = sizeof(options) / sizeof(JavaVMOption);
|
||||||
|
|
||||||
JNIEnv* env = NULL;
|
JNIEnv* env = nullptr;
|
||||||
int result = (*JNI_CreateJavaVM)(&javaVM, (void**) &env, &vm_args);
|
int result = (*JNI_CreateJavaVM)(&javaVM, (void**) &env, &vm_args);
|
||||||
if (result == JNI_OK) {
|
if (result == JNI_OK) {
|
||||||
guarantee(env != NULL, "missing env");
|
guarantee(env != nullptr, "missing env");
|
||||||
|
_shared_library_javavm_id = javaVM_id;
|
||||||
_shared_library_javavm = javaVM;
|
_shared_library_javavm = javaVM;
|
||||||
JVMCI_event_1("created JavaVM[%ld]@" PTR_FORMAT " for JVMCI runtime %d", javaVM_id, p2i(javaVM), _id);
|
JVMCI_event_1("created JavaVM[%ld]@" PTR_FORMAT " for JVMCI runtime %d", javaVM_id, p2i(javaVM), _id);
|
||||||
return env;
|
return env;
|
||||||
@ -965,16 +1241,16 @@ JNIEnv* JVMCIRuntime::init_shared_library_javavm() {
|
|||||||
fatal("JNI_CreateJavaVM failed with return value %d", result);
|
fatal("JNI_CreateJavaVM failed with return value %d", result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JVMCIRuntime::init_JavaVM_info(jlongArray info, JVMCI_TRAPS) {
|
void JVMCIRuntime::init_JavaVM_info(jlongArray info, JVMCI_TRAPS) {
|
||||||
if (info != NULL) {
|
if (info != nullptr) {
|
||||||
typeArrayOop info_oop = (typeArrayOop) JNIHandles::resolve(info);
|
typeArrayOop info_oop = (typeArrayOop) JNIHandles::resolve(info);
|
||||||
if (info_oop->length() < 4) {
|
if (info_oop->length() < 4) {
|
||||||
JVMCI_THROW_MSG(ArrayIndexOutOfBoundsException, err_msg("%d < 4", info_oop->length()));
|
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(0, (jlong) (address) javaVM);
|
||||||
info_oop->long_at_put(1, (jlong) (address) javaVM->functions->reserved0);
|
info_oop->long_at_put(1, (jlong) (address) javaVM->functions->reserved0);
|
||||||
info_oop->long_at_put(2, (jlong) (address) javaVM->functions->reserved1);
|
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 \
|
#define JAVAVM_CALL_BLOCK \
|
||||||
guarantee(thread != NULL && _shared_library_javavm != NULL, "npe"); \
|
guarantee(thread != nullptr && _shared_library_javavm != nullptr, "npe"); \
|
||||||
ThreadToNativeFromVM ttnfv(thread); \
|
ThreadToNativeFromVM ttnfv(thread); \
|
||||||
JavaVM* javavm = (JavaVM*) _shared_library_javavm;
|
JavaVM* javavm = _shared_library_javavm;
|
||||||
|
|
||||||
jint JVMCIRuntime::AttachCurrentThread(JavaThread* thread, void **penv, void *args) {
|
jint JVMCIRuntime::AttachCurrentThread(JavaThread* thread, void **penv, void *args) {
|
||||||
JAVAVM_CALL_BLOCK
|
JAVAVM_CALL_BLOCK
|
||||||
@ -1027,21 +1303,64 @@ void JVMCIRuntime::initialize_HotSpotJVMCIRuntime(JVMCI_TRAPS) {
|
|||||||
JVMCI::_is_initialized = true;
|
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) {
|
void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
|
||||||
// Check first without JVMCI_lock
|
// Check first without _lock
|
||||||
if (_init_state == fully_initialized) {
|
if (_init_state == fully_initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MutexLocker locker(JVMCI_lock);
|
MutexLocker locker(_lock);
|
||||||
// Check again under JVMCI_lock
|
// Check again under _lock
|
||||||
if (_init_state == fully_initialized) {
|
if (_init_state == fully_initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (_init_state == being_initialized) {
|
while (_init_state == being_initialized) {
|
||||||
JVMCI_event_1("waiting for initialization of JVMCI runtime %d", _id);
|
JVMCI_event_1("waiting for initialization of JVMCI runtime %d", _id);
|
||||||
JVMCI_lock->wait();
|
_lock->wait();
|
||||||
if (_init_state == fully_initialized) {
|
if (_init_state == fully_initialized) {
|
||||||
JVMCI_event_1("done waiting for initialization of JVMCI runtime %d", _id);
|
JVMCI_event_1("done waiting for initialization of JVMCI runtime %d", _id);
|
||||||
return;
|
return;
|
||||||
@ -1052,21 +1371,33 @@ void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
|
|||||||
_init_state = being_initialized;
|
_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);
|
HandleMark hm(THREAD);
|
||||||
ResourceMark rm(THREAD);
|
ResourceMark rm(THREAD);
|
||||||
|
{
|
||||||
|
MutexLocker lock_jvmci(JVMCI_lock);
|
||||||
if (JVMCIENV->is_hotspot()) {
|
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);
|
HotSpotJVMCI::compute_offsets(CHECK_EXIT);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
JNIAccessMark jni(JVMCIENV);
|
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());
|
JNIJVMCI::initialize_ids(jni.env());
|
||||||
if (jni()->ExceptionCheck()) {
|
if (jni()->ExceptionCheck()) {
|
||||||
jni()->ExceptionDescribe();
|
jni()->ExceptionDescribe();
|
||||||
fatal("JNI exception during init");
|
fatal("JNI exception during init");
|
||||||
}
|
}
|
||||||
|
// _lock is re-locked at this point
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!JVMCIENV->is_hotspot()) {
|
if (!JVMCIENV->is_hotspot()) {
|
||||||
@ -1090,7 +1421,7 @@ void JVMCIRuntime::initialize(JVMCIEnv* JVMCIENV) {
|
|||||||
|
|
||||||
_init_state = fully_initialized;
|
_init_state = fully_initialized;
|
||||||
JVMCI_event_1("initialized JVMCI runtime %d", _id);
|
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) {
|
JVMCIObject JVMCIRuntime::create_jvmci_primitive_type(BasicType type, JVMCI_TRAPS) {
|
||||||
@ -1122,6 +1453,7 @@ void JVMCIRuntime::initialize_JVMCI(JVMCI_TRAPS) {
|
|||||||
if (!is_HotSpotJVMCIRuntime_initialized()) {
|
if (!is_HotSpotJVMCIRuntime_initialized()) {
|
||||||
initialize(JVMCI_CHECK);
|
initialize(JVMCI_CHECK);
|
||||||
JVMCIENV->call_JVMCI_getRuntime(JVMCI_CHECK);
|
JVMCIENV->call_JVMCI_getRuntime(JVMCI_CHECK);
|
||||||
|
guarantee(_HotSpotJVMCIRuntime_instance.is_non_null(), "NPE in JVMCI runtime %d", _id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1172,9 +1504,47 @@ void JVMCIRuntime::shutdown() {
|
|||||||
JVMCIEnv __stack_jvmci_env__(JavaThread::current(), _HotSpotJVMCIRuntime_instance.is_hotspot(), __FILE__, __LINE__);
|
JVMCIEnv __stack_jvmci_env__(JavaThread::current(), _HotSpotJVMCIRuntime_instance.is_hotspot(), __FILE__, __LINE__);
|
||||||
JVMCIEnv* JVMCIENV = &__stack_jvmci_env__;
|
JVMCIEnv* JVMCIENV = &__stack_jvmci_env__;
|
||||||
JVMCIENV->call_HotSpotJVMCIRuntime_shutdown(_HotSpotJVMCIRuntime_instance);
|
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) {
|
void JVMCIRuntime::bootstrap_finished(TRAPS) {
|
||||||
if (_HotSpotJVMCIRuntime_instance.is_non_null()) {
|
if (_HotSpotJVMCIRuntime_instance.is_non_null()) {
|
||||||
@ -1587,6 +1957,12 @@ void JVMCIRuntime::compile_method(JVMCIEnv* JVMCIENV, JVMCICompiler* compiler, c
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (JVMCI::in_shutdown()) {
|
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");
|
compile_state->set_failure(false, "Avoiding compilation during shutdown");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1616,7 +1992,7 @@ void JVMCIRuntime::compile_method(JVMCIEnv* JVMCIENV, JVMCICompiler* compiler, c
|
|||||||
bool retryable = JVMCIENV->get_HotSpotCompilationRequestResult_retry(result_object) != 0;
|
bool retryable = JVMCIENV->get_HotSpotCompilationRequestResult_retry(result_object) != 0;
|
||||||
compile_state->set_failure(retryable, failure_reason, true);
|
compile_state->set_failure(retryable, failure_reason, true);
|
||||||
} else {
|
} else {
|
||||||
if (compile_state->task()->code() == NULL) {
|
if (compile_state->task()->code() == nullptr) {
|
||||||
compile_state->set_failure(true, "no nmethod produced");
|
compile_state->set_failure(true, "no nmethod produced");
|
||||||
} else {
|
} else {
|
||||||
compile_state->task()->set_num_inlined_bytecodes(JVMCIENV->get_HotSpotCompilationRequestResult_inlinedBytecodes(result_object));
|
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;
|
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.
|
// There is one instance of this class per HotSpotJVMCIRuntime object.
|
||||||
class JVMCIRuntime: public CHeapObj<mtJVMCI> {
|
class JVMCIRuntime: public CHeapObj<mtJVMCI> {
|
||||||
friend class JVMCI;
|
friend class JVMCI;
|
||||||
|
friend class JavaVMRefsInitialization;
|
||||||
public:
|
public:
|
||||||
// Constants describing whether JVMCI wants to be able to adjust the compilation
|
// 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
|
// 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.
|
// Initialization state of this JVMCIRuntime.
|
||||||
InitState _init_state;
|
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)
|
// A wrapper for a VM scoped JNI global handle (i.e. JVMCIEnv::make_global)
|
||||||
// to a HotSpotJVMCIRuntime instance. This JNI global handle must never
|
// to a HotSpotJVMCIRuntime instance. This JNI global handle must never
|
||||||
// be explicitly destroyed as it can be accessed in a racy way during
|
// 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.
|
// the VM or shared library JavaVM managing the handle dies.
|
||||||
JVMCIObject _HotSpotJVMCIRuntime_instance;
|
JVMCIObject _HotSpotJVMCIRuntime_instance;
|
||||||
|
|
||||||
// Result of calling JNI_CreateJavaVM in the JVMCI shared library.
|
// Lock for operations that may be performed by
|
||||||
// Must only be modified under JVMCI_lock.
|
// any thread attached this runtime. To avoid deadlock,
|
||||||
volatile JavaVM* _shared_library_javavm;
|
// 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
|
// 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;
|
int _id;
|
||||||
|
JVMCIRuntime* _next;
|
||||||
|
|
||||||
// Handles to Metadata objects.
|
// Handles to Metadata objects.
|
||||||
MetadataHandles* _metadata_handles;
|
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);
|
JVMCIObject create_jvmci_primitive_type(BasicType type, JVMCI_TRAPS);
|
||||||
|
|
||||||
// Implementation methods for loading and constant pool access.
|
// Implementation methods for loading and constant pool access.
|
||||||
@ -168,18 +206,51 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
|
|||||||
Bytecodes::Code bc,
|
Bytecodes::Code bc,
|
||||||
constantTag tag);
|
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:
|
public:
|
||||||
JVMCIRuntime(int id);
|
JVMCIRuntime(JVMCIRuntime* next, int id, bool for_compile_broker);
|
||||||
|
|
||||||
int id() const { return _id; }
|
int id() const { return _id; }
|
||||||
|
Monitor* lock() const { return _lock; }
|
||||||
|
|
||||||
// Ensures that a JVMCI shared library JavaVM exists for this runtime.
|
// Ensures that a JVMCI shared library JavaVM exists for this runtime.
|
||||||
// If the JavaVM was created by this call, then the thread-local JNI
|
// 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();
|
JNIEnv* init_shared_library_javavm();
|
||||||
|
|
||||||
// Determines if the JVMCI shared library JavaVM exists for this runtime.
|
// 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
|
// Copies info about the JVMCI shared library JavaVM associated with this
|
||||||
// runtime into `info` as follows:
|
// 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.
|
// Compute offsets and construct any state required before executing JVMCI code.
|
||||||
void initialize(JVMCIEnv* jvmciEnv);
|
void initialize(JVMCIEnv* jvmciEnv);
|
||||||
|
|
||||||
// Allocation and management of JNI global object handles
|
// Allocation and management of handles to HotSpot heap objects
|
||||||
// whose lifetime is scoped by this JVMCIRuntime. The lifetime
|
// whose lifetime is scoped by this JVMCIRuntime. The max lifetime
|
||||||
// of these handles is the same as the JVMCI shared library JavaVM
|
// of these handles is the same as the JVMCI shared library JavaVM
|
||||||
// associated with this JVMCIRuntime. These JNI handles are
|
// 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.
|
// shared library JavaVM.
|
||||||
jobject make_global(const Handle& obj);
|
jlong make_oop_handle(const Handle& obj);
|
||||||
void destroy_global(jobject handle);
|
bool is_oop_handle(jlong handle);
|
||||||
bool is_global_handle(jobject handle);
|
|
||||||
|
// Called from IndirectHotSpotObjectConstantImpl.clear(Object)
|
||||||
|
void destroy_oop_handle(jlong handle);
|
||||||
|
|
||||||
// Allocation and management of metadata handles.
|
// Allocation and management of metadata handles.
|
||||||
jmetadata allocate_handle(const methodHandle& handle);
|
jmetadata allocate_handle(const methodHandle& handle);
|
||||||
jmetadata allocate_handle(const constantPoolHandle& handle);
|
jmetadata allocate_handle(const constantPoolHandle& handle);
|
||||||
void release_handle(jmetadata 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,
|
// Gets the HotSpotJVMCIRuntime instance for this runtime,
|
||||||
// initializing it first if necessary.
|
// initializing it first if necessary.
|
||||||
JVMCIObject get_HotSpotJVMCIRuntime(JVMCI_TRAPS);
|
JVMCIObject get_HotSpotJVMCIRuntime(JVMCI_TRAPS);
|
||||||
@ -240,8 +343,15 @@ class JVMCIRuntime: public CHeapObj<mtJVMCI> {
|
|||||||
void call_getCompiler(TRAPS);
|
void call_getCompiler(TRAPS);
|
||||||
|
|
||||||
// Shuts down this runtime by calling HotSpotJVMCIRuntime.shutdown().
|
// 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();
|
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);
|
void bootstrap_finished(TRAPS);
|
||||||
|
|
||||||
// Look up a klass by name from a particular class loader (the accessor's).
|
// 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,
|
char* speculations,
|
||||||
int speculations_len);
|
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.
|
// Reports an unexpected exception and exits the VM with a fatal error.
|
||||||
static void fatal_exception(JVMCIEnv* JVMCIENV, const char* message);
|
static void fatal_exception(JVMCIEnv* JVMCIENV, const char* message);
|
||||||
|
|
||||||
|
@ -119,6 +119,8 @@ bool JVMCIGlobals::check_jvmci_flags_are_consistent() {
|
|||||||
CHECK_NOT_SET(JVMCIUseFastLocking, EnableJVMCI)
|
CHECK_NOT_SET(JVMCIUseFastLocking, EnableJVMCI)
|
||||||
CHECK_NOT_SET(JVMCINMethodSizeLimit, EnableJVMCI)
|
CHECK_NOT_SET(JVMCINMethodSizeLimit, EnableJVMCI)
|
||||||
CHECK_NOT_SET(JVMCIPrintProperties, EnableJVMCI)
|
CHECK_NOT_SET(JVMCIPrintProperties, EnableJVMCI)
|
||||||
|
CHECK_NOT_SET(JVMCIThreadsPerNativeLibraryRuntime, EnableJVMCI)
|
||||||
|
CHECK_NOT_SET(JVMCICompilerIdleDelay, EnableJVMCI)
|
||||||
CHECK_NOT_SET(UseJVMCINativeLibrary, EnableJVMCI)
|
CHECK_NOT_SET(UseJVMCINativeLibrary, EnableJVMCI)
|
||||||
CHECK_NOT_SET(JVMCILibPath, EnableJVMCI)
|
CHECK_NOT_SET(JVMCILibPath, EnableJVMCI)
|
||||||
CHECK_NOT_SET(JVMCINativeLibraryErrorFile, EnableJVMCI)
|
CHECK_NOT_SET(JVMCINativeLibraryErrorFile, EnableJVMCI)
|
||||||
@ -166,6 +168,8 @@ bool JVMCIGlobals::enable_jvmci_product_mode(JVMFlagOrigin origin) {
|
|||||||
"EnableJVMCI",
|
"EnableJVMCI",
|
||||||
"EnableJVMCIProduct",
|
"EnableJVMCIProduct",
|
||||||
"UseJVMCICompiler",
|
"UseJVMCICompiler",
|
||||||
|
"JVMCIThreadsPerNativeLibraryRuntime",
|
||||||
|
"JVMCICompilerIdleDelay",
|
||||||
"JVMCIPrintProperties",
|
"JVMCIPrintProperties",
|
||||||
"EagerJVMCI",
|
"EagerJVMCI",
|
||||||
"JVMCIThreads",
|
"JVMCIThreads",
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
class fileStream;
|
class fileStream;
|
||||||
|
|
||||||
#define LIBJVMCI_ERR_FILE "hs_err_pid%p_libjvmci.log"
|
#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
|
// 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 " \
|
"Use JVMCI as the default compiler. Defaults to true if " \
|
||||||
"EnableJVMCIProduct is true.") \
|
"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, \
|
product(bool, JVMCIPrintProperties, false, EXPERIMENTAL, \
|
||||||
"Prints properties used by the JVMCI compiler and exits") \
|
"Prints properties used by the JVMCI compiler and exits") \
|
||||||
\
|
\
|
||||||
|
@ -409,6 +409,7 @@
|
|||||||
declare_constant(JVM_ACC_FIELD_INTERNAL) \
|
declare_constant(JVM_ACC_FIELD_INTERNAL) \
|
||||||
declare_constant(JVM_ACC_FIELD_STABLE) \
|
declare_constant(JVM_ACC_FIELD_STABLE) \
|
||||||
declare_constant(JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE) \
|
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_VARARGS", JVM_ACC_VARARGS) \
|
||||||
declare_preprocessor_constant("JVM_ACC_BRIDGE", JVM_ACC_BRIDGE) \
|
declare_preprocessor_constant("JVM_ACC_BRIDGE", JVM_ACC_BRIDGE) \
|
||||||
declare_preprocessor_constant("JVM_ACC_ANNOTATION", JVM_ACC_ANNOTATION) \
|
declare_preprocessor_constant("JVM_ACC_ANNOTATION", JVM_ACC_ANNOTATION) \
|
||||||
|
@ -431,7 +431,7 @@ void before_exit(JavaThread* thread) {
|
|||||||
|
|
||||||
#if INCLUDE_JVMCI
|
#if INCLUDE_JVMCI
|
||||||
if (EnableJVMCI) {
|
if (EnableJVMCI) {
|
||||||
JVMCI::shutdown();
|
JVMCI::shutdown(thread);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -156,6 +156,7 @@ Mutex* Bootclasspath_lock = NULL;
|
|||||||
|
|
||||||
#if INCLUDE_JVMCI
|
#if INCLUDE_JVMCI
|
||||||
Monitor* JVMCI_lock = NULL;
|
Monitor* JVMCI_lock = NULL;
|
||||||
|
Monitor* JVMCIRuntime_lock = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -336,7 +337,8 @@ void mutex_init() {
|
|||||||
def(Zip_lock , PaddedMonitor, nosafepoint-1); // Holds DumpTimeTable_lock
|
def(Zip_lock , PaddedMonitor, nosafepoint-1); // Holds DumpTimeTable_lock
|
||||||
|
|
||||||
#if INCLUDE_JVMCI
|
#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
|
#endif
|
||||||
|
|
||||||
// These locks have relative rankings, and inherit safepoint checking attributes from that rank.
|
// 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(Module_lock , PaddedMutex , ClassLoaderDataGraph_lock);
|
||||||
defl(SystemDictionary_lock , PaddedMonitor, Module_lock);
|
defl(SystemDictionary_lock , PaddedMonitor, Module_lock);
|
||||||
defl(JNICritical_lock , PaddedMonitor, MultiArray_lock); // used for JNI critical regions
|
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) {
|
GCMutexLocker::GCMutexLocker(Mutex* mutex) {
|
||||||
|
@ -148,7 +148,8 @@ extern Mutex* CodeHeapStateAnalytics_lock; // lock print functions against
|
|||||||
// Only used locally in PrintCodeCacheLayout processing.
|
// Only used locally in PrintCodeCacheLayout processing.
|
||||||
|
|
||||||
#if INCLUDE_JVMCI
|
#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
|
#endif
|
||||||
|
|
||||||
extern Mutex* Bootclasspath_lock;
|
extern Mutex* Bootclasspath_lock;
|
||||||
|
@ -1034,6 +1034,7 @@ JavaThread::JavaThread() :
|
|||||||
_in_retryable_allocation(false),
|
_in_retryable_allocation(false),
|
||||||
_pending_failed_speculation(0),
|
_pending_failed_speculation(0),
|
||||||
_jvmci{nullptr},
|
_jvmci{nullptr},
|
||||||
|
_libjvmci_runtime(nullptr),
|
||||||
_jvmci_counters(nullptr),
|
_jvmci_counters(nullptr),
|
||||||
_jvmci_reserved0(0),
|
_jvmci_reserved0(0),
|
||||||
_jvmci_reserved1(0),
|
_jvmci_reserved1(0),
|
||||||
|
@ -80,6 +80,8 @@ class JvmtiDeferredUpdates;
|
|||||||
class ThreadClosure;
|
class ThreadClosure;
|
||||||
class ICRefillVerifier;
|
class ICRefillVerifier;
|
||||||
|
|
||||||
|
class JVMCIRuntime;
|
||||||
|
|
||||||
class Metadata;
|
class Metadata;
|
||||||
class ResourceArea;
|
class ResourceArea;
|
||||||
|
|
||||||
@ -940,6 +942,9 @@ class JavaThread: public Thread {
|
|||||||
address _alternate_call_target;
|
address _alternate_call_target;
|
||||||
} _jvmci;
|
} _jvmci;
|
||||||
|
|
||||||
|
// The JVMCIRuntime in a JVMCI shared library
|
||||||
|
JVMCIRuntime* _libjvmci_runtime;
|
||||||
|
|
||||||
// Support for high precision, thread sensitive counters in JVMCI compiled code.
|
// Support for high precision, thread sensitive counters in JVMCI compiled code.
|
||||||
jlong* _jvmci_counters;
|
jlong* _jvmci_counters;
|
||||||
|
|
||||||
@ -1219,6 +1224,12 @@ class JavaThread: public Thread {
|
|||||||
|
|
||||||
virtual bool in_retryable_allocation() const { return _in_retryable_allocation; }
|
virtual bool in_retryable_allocation() const { return _in_retryable_allocation; }
|
||||||
void set_in_retryable_allocation(bool b) { _in_retryable_allocation = b; }
|
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
|
#endif // INCLUDE_JVMCI
|
||||||
|
|
||||||
// Exception handling for compiled methods
|
// 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}
|
* @param name name of current thread if in a native image otherwise {@code null}
|
||||||
* @see HotSpotJVMCIRuntime#attachCurrentThread
|
* @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)
|
* @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
|
* Set of recognized {@code "jvmci.*"} system properties.
|
||||||
* {@link Option} have this object as their value.
|
|
||||||
*/
|
*/
|
||||||
static final Map<String, Object> options = new HashMap<>();
|
static final Map<String, Object> options = new HashMap<>();
|
||||||
static {
|
|
||||||
options.put("jvmci.class.path.append", options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of all supported JVMCI 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.
|
* 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 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
|
* @return {@code true} if this call attached the current thread, {@code false} if the current
|
||||||
* thread was already attached
|
* thread was already attached
|
||||||
* @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
|
* @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
|
* @throws ArrayIndexOutOfBoundsException if {@code javaVMInfo} is non-null and is shorter than
|
||||||
* the length of the array returned by {@link #registerNativeMethods}
|
* 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;
|
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.
|
* 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.
|
* @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
|
||||||
* {@code -XX:-UseJVMCINativeLibrary})
|
* {@code -XX:-UseJVMCINativeLibrary})
|
||||||
* @throws IllegalStateException if the peer runtime has not been initialized or if the current
|
* @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
|
* thread is not attached or if there is an error while trying to detach the thread
|
||||||
*/
|
*/
|
||||||
public void detachCurrentThread() {
|
public boolean detachCurrentThread(boolean release) {
|
||||||
compilerToVm.detachCurrentThread();
|
return compilerToVm.detachCurrentThread(release);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user