8305252: make_method_handle_intrinsic may call java code under a lock
Reviewed-by: dholmes, matsaave, iklam
This commit is contained in:
parent
f239695b56
commit
7400aff3b8
@ -1583,11 +1583,13 @@ void SystemDictionary::methods_do(void f(Method*)) {
|
||||
}
|
||||
|
||||
auto doit = [&] (InvokeMethodKey key, Method* method) {
|
||||
f(method);
|
||||
if (method != nullptr) {
|
||||
f(method);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
MutexLocker ml(InvokeMethodTable_lock);
|
||||
MutexLocker ml(InvokeMethodIntrinsicTable_lock);
|
||||
_invoke_method_intrinsic_table.iterate_all(doit);
|
||||
}
|
||||
|
||||
@ -1939,40 +1941,62 @@ Method* SystemDictionary::find_method_handle_intrinsic(vmIntrinsicID iid,
|
||||
iid != vmIntrinsics::_invokeGeneric,
|
||||
"must be a known MH intrinsic iid=%d: %s", iid_as_int, vmIntrinsics::name_at(iid));
|
||||
|
||||
InvokeMethodKey key(signature, iid_as_int);
|
||||
Method** met = nullptr;
|
||||
|
||||
// We only want one entry in the table for this (signature/id, method) pair but the code
|
||||
// to create the intrinsic method needs to be outside the lock.
|
||||
// The first thread claims the entry by adding the key and the other threads wait, until the
|
||||
// Method has been added as the value.
|
||||
{
|
||||
MutexLocker ml(THREAD, InvokeMethodTable_lock);
|
||||
InvokeMethodKey key(signature, iid_as_int);
|
||||
Method** met = _invoke_method_intrinsic_table.get(key);
|
||||
if (met != nullptr) {
|
||||
return *met;
|
||||
MonitorLocker ml(THREAD, InvokeMethodIntrinsicTable_lock);
|
||||
while (met == nullptr) {
|
||||
bool created;
|
||||
met = _invoke_method_intrinsic_table.put_if_absent(key, &created);
|
||||
if (met != nullptr && *met != nullptr) {
|
||||
return *met;
|
||||
} else if (!created) {
|
||||
// Second thread waits for first to actually create the entry and returns
|
||||
// it after notify. Loop until method return is non-null.
|
||||
ml.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool throw_error = false;
|
||||
// This function could get an OOM but it is safe to call inside of a lock because
|
||||
// throwing OutOfMemoryError doesn't call Java code.
|
||||
methodHandle m = Method::make_method_handle_intrinsic(iid, signature, CHECK_NULL);
|
||||
if (!Arguments::is_interpreter_only() || iid == vmIntrinsics::_linkToNative) {
|
||||
// Generate a compiled form of the MH intrinsic
|
||||
// linkToNative doesn't have interpreter-specific implementation, so always has to go through compiled version.
|
||||
AdapterHandlerLibrary::create_native_wrapper(m);
|
||||
// Check if have the compiled code.
|
||||
throw_error = (!m->has_compiled_code());
|
||||
}
|
||||
methodHandle m = Method::make_method_handle_intrinsic(iid, signature, THREAD);
|
||||
bool throw_error = HAS_PENDING_EXCEPTION;
|
||||
if (!throw_error && (!Arguments::is_interpreter_only() || iid == vmIntrinsics::_linkToNative)) {
|
||||
// Generate a compiled form of the MH intrinsic
|
||||
// linkToNative doesn't have interpreter-specific implementation, so always has to go through compiled version.
|
||||
AdapterHandlerLibrary::create_native_wrapper(m);
|
||||
// Check if have the compiled code.
|
||||
throw_error = (!m->has_compiled_code());
|
||||
}
|
||||
|
||||
if (!throw_error) {
|
||||
{
|
||||
MonitorLocker ml(THREAD, InvokeMethodIntrinsicTable_lock);
|
||||
if (throw_error) {
|
||||
// Remove the entry and let another thread try, or get the same exception.
|
||||
bool removed = _invoke_method_intrinsic_table.remove(key);
|
||||
assert(removed, "must be the owner");
|
||||
ml.notify_all();
|
||||
} else {
|
||||
signature->make_permanent(); // The signature is never unloaded.
|
||||
bool created = _invoke_method_intrinsic_table.put(key, m());
|
||||
assert(created, "must be since we still hold the lock");
|
||||
assert(Arguments::is_interpreter_only() || (m->has_compiled_code() &&
|
||||
m->code()->entry_point() == m->from_compiled_entry()),
|
||||
"MH intrinsic invariant");
|
||||
*met = m(); // insert the element
|
||||
ml.notify_all();
|
||||
return m();
|
||||
}
|
||||
}
|
||||
|
||||
// Throw error outside of the lock.
|
||||
THROW_MSG_NULL(vmSymbols::java_lang_VirtualMachineError(),
|
||||
"Out of space in CodeCache for method handle intrinsic");
|
||||
// Throw VirtualMachineError or the pending exception in the JavaThread
|
||||
if (throw_error && !HAS_PENDING_EXCEPTION) {
|
||||
THROW_MSG_NULL(vmSymbols::java_lang_VirtualMachineError(),
|
||||
"Out of space in CodeCache for method handle intrinsic");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Helper for unpacking the return value from linkMethod and linkCallSite.
|
||||
@ -2115,7 +2139,7 @@ Handle SystemDictionary::find_method_handle_type(Symbol* signature,
|
||||
Handle empty;
|
||||
OopHandle* o;
|
||||
{
|
||||
MutexLocker ml(THREAD, InvokeMethodTable_lock);
|
||||
MutexLocker ml(THREAD, InvokeMethodTypeTable_lock);
|
||||
o = _invoke_method_type_table.get(signature);
|
||||
}
|
||||
|
||||
@ -2184,7 +2208,7 @@ Handle SystemDictionary::find_method_handle_type(Symbol* signature,
|
||||
|
||||
if (can_be_cached) {
|
||||
// We can cache this MethodType inside the JVM.
|
||||
MutexLocker ml(THREAD, InvokeMethodTable_lock);
|
||||
MutexLocker ml(THREAD, InvokeMethodTypeTable_lock);
|
||||
bool created = false;
|
||||
assert(method_type != nullptr, "unexpected null");
|
||||
OopHandle* h = _invoke_method_type_table.get(signature);
|
||||
|
@ -39,7 +39,8 @@
|
||||
Mutex* Patching_lock = nullptr;
|
||||
Mutex* CompiledMethod_lock = nullptr;
|
||||
Monitor* SystemDictionary_lock = nullptr;
|
||||
Mutex* InvokeMethodTable_lock = nullptr;
|
||||
Mutex* InvokeMethodTypeTable_lock = nullptr;
|
||||
Monitor* InvokeMethodIntrinsicTable_lock = nullptr;
|
||||
Mutex* SharedDictionary_lock = nullptr;
|
||||
Monitor* ClassInitError_lock = nullptr;
|
||||
Mutex* Module_lock = nullptr;
|
||||
@ -254,7 +255,9 @@ void mutex_init() {
|
||||
}
|
||||
|
||||
MUTEX_DEFN(JmethodIdCreation_lock , PaddedMutex , nosafepoint-2); // used for creating jmethodIDs.
|
||||
MUTEX_DEFN(InvokeMethodTable_lock , PaddedMutex , safepoint);
|
||||
MUTEX_DEFN(InvokeMethodTypeTable_lock , PaddedMutex , safepoint);
|
||||
MUTEX_DEFN(InvokeMethodIntrinsicTable_lock , PaddedMonitor, safepoint);
|
||||
MUTEX_DEFN(AdapterHandlerLibrary_lock , PaddedMutex , safepoint);
|
||||
MUTEX_DEFN(SharedDictionary_lock , PaddedMutex , safepoint);
|
||||
MUTEX_DEFN(VMStatistic_lock , PaddedMutex , safepoint);
|
||||
MUTEX_DEFN(SignatureHandlerLibrary_lock , PaddedMutex , safepoint);
|
||||
@ -344,7 +347,6 @@ void mutex_init() {
|
||||
|
||||
MUTEX_DEFL(Threads_lock , PaddedMonitor, CompileThread_lock, true);
|
||||
MUTEX_DEFL(Compile_lock , PaddedMutex , MethodCompileQueue_lock);
|
||||
MUTEX_DEFL(AdapterHandlerLibrary_lock , PaddedMutex , InvokeMethodTable_lock);
|
||||
MUTEX_DEFL(Heap_lock , PaddedMonitor, AdapterHandlerLibrary_lock);
|
||||
|
||||
MUTEX_DEFL(PerfDataMemAlloc_lock , PaddedMutex , Heap_lock);
|
||||
|
@ -34,7 +34,8 @@
|
||||
extern Mutex* Patching_lock; // a lock used to guard code patching of compiled code
|
||||
extern Mutex* CompiledMethod_lock; // a lock used to guard a compiled method and OSR queues
|
||||
extern Monitor* SystemDictionary_lock; // a lock on the system dictionary
|
||||
extern Mutex* InvokeMethodTable_lock;
|
||||
extern Mutex* InvokeMethodTypeTable_lock;
|
||||
extern Monitor* InvokeMethodIntrinsicTable_lock;
|
||||
extern Mutex* SharedDictionary_lock; // a lock on the CDS shared dictionary
|
||||
extern Monitor* ClassInitError_lock; // a lock on the class initialization error table
|
||||
extern Mutex* Module_lock; // a lock on module and package related data structures
|
||||
|
Loading…
Reference in New Issue
Block a user