8215754: ZGC: nmethod is not unlinked from Method before rendezvous handshake

Reviewed-by: pliden, neliasso
This commit is contained in:
Erik Österlund 2019-01-11 13:15:37 +01:00
parent 804d51618f
commit c03e894387
4 changed files with 28 additions and 13 deletions

View File

@ -1159,6 +1159,19 @@ void nmethod::log_state_change() const {
}
}
void nmethod::unlink_from_method(bool acquire_lock) {
// We need to check if both the _code and _from_compiled_code_entry_point
// refer to this nmethod because there is a race in setting these two fields
// in Method* as seen in bugid 4947125.
// If the vep() points to the zombie nmethod, the memory for the nmethod
// could be flushed and the compiler and vtable stubs could still call
// through it.
if (method() != NULL && (method()->code() == this ||
method()->from_compiled_entry() == verified_entry_point())) {
method()->clear_code(acquire_lock);
}
}
/**
* Common functionality for both make_not_entrant and make_zombie
*/
@ -1246,17 +1259,7 @@ bool nmethod::make_not_entrant_or_zombie(int state) {
JVMCI_ONLY(maybe_invalidate_installed_code());
// Remove nmethod from method.
// We need to check if both the _code and _from_compiled_code_entry_point
// refer to this nmethod because there is a race in setting these two fields
// in Method* as seen in bugid 4947125.
// If the vep() points to the zombie nmethod, the memory for the nmethod
// could be flushed and the compiler and vtable stubs could still call
// through it.
if (method() != NULL && (method()->code() == this ||
method()->from_compiled_entry() == verified_entry_point())) {
HandleMark hm;
method()->clear_code(false /* already owns Patching_lock */);
}
unlink_from_method(false /* already owns Patching_lock */);
} // leave critical region under Patching_lock
#ifdef ASSERT

View File

@ -376,6 +376,8 @@ class nmethod : public CompiledMethod {
int comp_level() const { return _comp_level; }
void unlink_from_method(bool acquire_lock);
// Support for oops in scopes and relocs:
// Note: index 0 is reserved for null.
oop oop_at(int index) const;

View File

@ -42,6 +42,11 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
}
if (nm->is_unloading()) {
// We don't need to take the lock when unlinking nmethods from
// the Method, because it is only concurrently unlinked by
// the entry barrier, which acquires the per nmethod lock.
nm->unlink_from_method(false /* acquire_lock */);
// We can end up calling nmethods that are unloading
// since we clear compiled ICs lazily. Returning false
// will re-resovle the call and update the compiled IC.

View File

@ -611,15 +611,20 @@ public:
return;
}
ZLocker<ZReentrantLock> locker(ZNMethodTable::lock_for_nmethod(nm));
if (nm->is_unloading()) {
// Unlinking of the dependencies must happen before the
// handshake separating unlink and purge.
nm->flush_dependencies(false /* delete_immediately */);
// We don't need to take the lock when unlinking nmethods from
// the Method, because it is only concurrently unlinked by
// the entry barrier, which acquires the per nmethod lock.
nm->unlink_from_method(false /* acquire_lock */);
return;
}
ZLocker<ZReentrantLock> locker(ZNMethodTable::lock_for_nmethod(nm));
// Heal oops and disarm
ZNMethodOopClosure cl;
ZNMethodTable::entry_oops_do(entry, &cl);