mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-25 05:45:11 +02:00
6965184: possible races in make_not_entrant_or_zombie
Reviewed-by: kvn
This commit is contained in:
parent
ae3ddc1514
commit
7533d0caf7
22 changed files with 226 additions and 740 deletions
|
@ -397,11 +397,6 @@ void nmethod::add_handler_for_exception_and_pc(Handle exception, address pc, add
|
|||
//-------------end of code for ExceptionCache--------------
|
||||
|
||||
|
||||
void nmFlags::clear() {
|
||||
assert(sizeof(nmFlags) == sizeof(int), "using more than one word for nmFlags");
|
||||
*(jint*)this = 0;
|
||||
}
|
||||
|
||||
int nmethod::total_size() const {
|
||||
return
|
||||
code_size() +
|
||||
|
@ -419,8 +414,32 @@ const char* nmethod::compile_kind() const {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// %%% This variable is no longer used?
|
||||
int nmethod::_zombie_instruction_size = NativeJump::instruction_size;
|
||||
// Fill in default values for various flag fields
|
||||
void nmethod::init_defaults() {
|
||||
_state = alive;
|
||||
_marked_for_reclamation = 0;
|
||||
_has_flushed_dependencies = 0;
|
||||
_speculatively_disconnected = 0;
|
||||
_has_unsafe_access = 0;
|
||||
_has_method_handle_invokes = 0;
|
||||
_marked_for_deoptimization = 0;
|
||||
_lock_count = 0;
|
||||
_stack_traversal_mark = 0;
|
||||
_unload_reported = false; // jvmti state
|
||||
|
||||
NOT_PRODUCT(_has_debug_info = false);
|
||||
_oops_do_mark_link = NULL;
|
||||
_jmethod_id = NULL;
|
||||
_osr_link = NULL;
|
||||
_scavenge_root_link = NULL;
|
||||
_scavenge_root_state = 0;
|
||||
_saved_nmethod_link = NULL;
|
||||
_compiler = NULL;
|
||||
|
||||
#ifdef HAVE_DTRACE_H
|
||||
_trap_offset = 0;
|
||||
#endif // def HAVE_DTRACE_H
|
||||
}
|
||||
|
||||
|
||||
nmethod* nmethod::new_native_nmethod(methodHandle method,
|
||||
|
@ -580,25 +599,16 @@ nmethod::nmethod(
|
|||
debug_only(No_Safepoint_Verifier nsv;)
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
|
||||
NOT_PRODUCT(_has_debug_info = false);
|
||||
_oops_do_mark_link = NULL;
|
||||
init_defaults();
|
||||
_method = method;
|
||||
_entry_bci = InvocationEntryBci;
|
||||
_jmethod_id = NULL;
|
||||
_osr_link = NULL;
|
||||
_scavenge_root_link = NULL;
|
||||
_scavenge_root_state = 0;
|
||||
_saved_nmethod_link = NULL;
|
||||
_compiler = NULL;
|
||||
// We have no exception handler or deopt handler make the
|
||||
// values something that will never match a pc like the nmethod vtable entry
|
||||
_exception_offset = 0;
|
||||
_deoptimize_offset = 0;
|
||||
_deoptimize_mh_offset = 0;
|
||||
_orig_pc_offset = 0;
|
||||
#ifdef HAVE_DTRACE_H
|
||||
_trap_offset = 0;
|
||||
#endif // def HAVE_DTRACE_H
|
||||
|
||||
_stub_offset = data_offset();
|
||||
_consts_offset = data_offset();
|
||||
_oops_offset = data_offset();
|
||||
|
@ -616,17 +626,9 @@ nmethod::nmethod(
|
|||
_exception_cache = NULL;
|
||||
_pc_desc_cache.reset_to(NULL);
|
||||
|
||||
flags.clear();
|
||||
flags.state = alive;
|
||||
_markedForDeoptimization = 0;
|
||||
|
||||
_lock_count = 0;
|
||||
_stack_traversal_mark = 0;
|
||||
|
||||
code_buffer->copy_oops_to(this);
|
||||
debug_only(verify_scavenge_root_oops());
|
||||
CodeCache::commit(this);
|
||||
VTune::create_nmethod(this);
|
||||
}
|
||||
|
||||
if (PrintNativeNMethods || PrintDebugInfo || PrintRelocations || PrintDependencies) {
|
||||
|
@ -674,15 +676,9 @@ nmethod::nmethod(
|
|||
debug_only(No_Safepoint_Verifier nsv;)
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
|
||||
NOT_PRODUCT(_has_debug_info = false);
|
||||
_oops_do_mark_link = NULL;
|
||||
init_defaults();
|
||||
_method = method;
|
||||
_entry_bci = InvocationEntryBci;
|
||||
_jmethod_id = NULL;
|
||||
_osr_link = NULL;
|
||||
_scavenge_root_link = NULL;
|
||||
_scavenge_root_state = 0;
|
||||
_compiler = NULL;
|
||||
// We have no exception handler or deopt handler make the
|
||||
// values something that will never match a pc like the nmethod vtable entry
|
||||
_exception_offset = 0;
|
||||
|
@ -708,17 +704,9 @@ nmethod::nmethod(
|
|||
_exception_cache = NULL;
|
||||
_pc_desc_cache.reset_to(NULL);
|
||||
|
||||
flags.clear();
|
||||
flags.state = alive;
|
||||
_markedForDeoptimization = 0;
|
||||
|
||||
_lock_count = 0;
|
||||
_stack_traversal_mark = 0;
|
||||
|
||||
code_buffer->copy_oops_to(this);
|
||||
debug_only(verify_scavenge_root_oops());
|
||||
CodeCache::commit(this);
|
||||
VTune::create_nmethod(this);
|
||||
}
|
||||
|
||||
if (PrintNMethods || PrintDebugInfo || PrintRelocations || PrintDependencies) {
|
||||
|
@ -783,21 +771,13 @@ nmethod::nmethod(
|
|||
debug_only(No_Safepoint_Verifier nsv;)
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
|
||||
NOT_PRODUCT(_has_debug_info = false);
|
||||
_oops_do_mark_link = NULL;
|
||||
init_defaults();
|
||||
_method = method;
|
||||
_jmethod_id = NULL;
|
||||
_entry_bci = entry_bci;
|
||||
_compile_id = compile_id;
|
||||
_comp_level = comp_level;
|
||||
_entry_bci = entry_bci;
|
||||
_osr_link = NULL;
|
||||
_scavenge_root_link = NULL;
|
||||
_scavenge_root_state = 0;
|
||||
_compiler = compiler;
|
||||
_orig_pc_offset = orig_pc_offset;
|
||||
#ifdef HAVE_DTRACE_H
|
||||
_trap_offset = 0;
|
||||
#endif // def HAVE_DTRACE_H
|
||||
_stub_offset = instructions_offset() + code_buffer->total_offset_of(code_buffer->stubs()->start());
|
||||
|
||||
// Exception handler and deopt handler are in the stub section
|
||||
|
@ -824,15 +804,6 @@ nmethod::nmethod(
|
|||
_exception_cache = NULL;
|
||||
_pc_desc_cache.reset_to(scopes_pcs_begin());
|
||||
|
||||
flags.clear();
|
||||
flags.state = alive;
|
||||
_markedForDeoptimization = 0;
|
||||
|
||||
_unload_reported = false; // jvmti state
|
||||
|
||||
_lock_count = 0;
|
||||
_stack_traversal_mark = 0;
|
||||
|
||||
// Copy contents of ScopeDescRecorder to nmethod
|
||||
code_buffer->copy_oops_to(this);
|
||||
debug_info->copy_to(this);
|
||||
|
@ -844,8 +815,6 @@ nmethod::nmethod(
|
|||
|
||||
CodeCache::commit(this);
|
||||
|
||||
VTune::create_nmethod(this);
|
||||
|
||||
// Copy contents of ExceptionHandlerTable to nmethod
|
||||
handler_table->copy_to(this);
|
||||
nul_chk_table->copy_to(this);
|
||||
|
@ -991,11 +960,6 @@ void nmethod::print_nmethod(bool printmethod) {
|
|||
}
|
||||
|
||||
|
||||
void nmethod::set_version(int v) {
|
||||
flags.version = v;
|
||||
}
|
||||
|
||||
|
||||
// Promote one word from an assembly-time handle to a live embedded oop.
|
||||
inline void nmethod::initialize_immediate_oop(oop* dest, jobject handle) {
|
||||
if (handle == NULL ||
|
||||
|
@ -1142,6 +1106,8 @@ void nmethod::cleanup_inline_caches() {
|
|||
// This is a private interface with the sweeper.
|
||||
void nmethod::mark_as_seen_on_stack() {
|
||||
assert(is_not_entrant(), "must be a non-entrant method");
|
||||
// Set the traversal mark to ensure that the sweeper does 2
|
||||
// cleaning passes before moving to zombie.
|
||||
set_stack_traversal_mark(NMethodSweeper::traversal_count());
|
||||
}
|
||||
|
||||
|
@ -1210,7 +1176,7 @@ void nmethod::make_unloaded(BoolObjectClosure* is_alive, oop cause) {
|
|||
// for later on.
|
||||
CodeCache::set_needs_cache_clean(true);
|
||||
}
|
||||
flags.state = unloaded;
|
||||
_state = unloaded;
|
||||
|
||||
// Log the unloading.
|
||||
log_state_change();
|
||||
|
@ -1236,21 +1202,21 @@ void nmethod::log_state_change() const {
|
|||
if (LogCompilation) {
|
||||
if (xtty != NULL) {
|
||||
ttyLocker ttyl; // keep the following output all in one block
|
||||
if (flags.state == unloaded) {
|
||||
if (_state == unloaded) {
|
||||
xtty->begin_elem("make_unloaded thread='" UINTX_FORMAT "'",
|
||||
os::current_thread_id());
|
||||
} else {
|
||||
xtty->begin_elem("make_not_entrant thread='" UINTX_FORMAT "'%s",
|
||||
os::current_thread_id(),
|
||||
(flags.state == zombie ? " zombie='1'" : ""));
|
||||
(_state == zombie ? " zombie='1'" : ""));
|
||||
}
|
||||
log_identity(xtty);
|
||||
xtty->stamp();
|
||||
xtty->end_elem();
|
||||
}
|
||||
}
|
||||
if (PrintCompilation && flags.state != unloaded) {
|
||||
print_on(tty, flags.state == zombie ? "made zombie " : "made not entrant ");
|
||||
if (PrintCompilation && _state != unloaded) {
|
||||
print_on(tty, _state == zombie ? "made zombie " : "made not entrant ");
|
||||
tty->cr();
|
||||
}
|
||||
}
|
||||
|
@ -1261,8 +1227,9 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
|
|||
|
||||
bool was_alive = false;
|
||||
|
||||
// Make sure the nmethod is not flushed in case of a safepoint in code below.
|
||||
// Make sure neither the nmethod nor the method is flushed in case of a safepoint in code below.
|
||||
nmethodLocker nml(this);
|
||||
methodHandle the_method(method());
|
||||
|
||||
{
|
||||
// If the method is already zombie there is nothing to do
|
||||
|
@ -1282,7 +1249,7 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
|
|||
// Enter critical section. Does not block for safepoint.
|
||||
MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag);
|
||||
|
||||
if (flags.state == state) {
|
||||
if (_state == state) {
|
||||
// another thread already performed this transition so nothing
|
||||
// to do, but return false to indicate this.
|
||||
return false;
|
||||
|
@ -1293,17 +1260,37 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
|
|||
if (!is_osr_method() && !is_not_entrant()) {
|
||||
NativeJump::patch_verified_entry(entry_point(), verified_entry_point(),
|
||||
SharedRuntime::get_handle_wrong_method_stub());
|
||||
assert (NativeJump::instruction_size == nmethod::_zombie_instruction_size, "");
|
||||
}
|
||||
|
||||
was_alive = is_in_use(); // Read state under lock
|
||||
if (is_in_use()) {
|
||||
// It's a true state change, so mark the method as decompiled.
|
||||
// Do it only for transition from alive.
|
||||
inc_decompile_count();
|
||||
}
|
||||
|
||||
// Change state
|
||||
flags.state = state;
|
||||
_state = state;
|
||||
|
||||
// Log the transition once
|
||||
log_state_change();
|
||||
|
||||
// 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 methodOop 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();
|
||||
}
|
||||
|
||||
if (state == not_entrant) {
|
||||
mark_as_seen_on_stack();
|
||||
}
|
||||
|
||||
} // leave critical region under Patching_lock
|
||||
|
||||
// When the nmethod becomes zombie it is no longer alive so the
|
||||
|
@ -1311,18 +1298,17 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
|
|||
// state will be flushed later when the transition to zombie
|
||||
// happens or they get unloaded.
|
||||
if (state == zombie) {
|
||||
// zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event
|
||||
// and it hasn't already been reported for this nmethod then report it now.
|
||||
// (the event may have been reported earilier if the GC marked it for unloading).
|
||||
post_compiled_method_unload();
|
||||
|
||||
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
flush_dependencies(NULL);
|
||||
} else {
|
||||
assert(state == not_entrant, "other cases may need to be handled differently");
|
||||
}
|
||||
|
||||
if (state == not_entrant) {
|
||||
Events::log("Make nmethod not entrant " INTPTR_FORMAT, this);
|
||||
} else {
|
||||
Events::log("Make nmethod zombie " INTPTR_FORMAT, this);
|
||||
}
|
||||
|
||||
if (TraceCreateZombies) {
|
||||
tty->print_cr("nmethod <" INTPTR_FORMAT "> code made %s", this, (state == not_entrant) ? "not entrant" : "zombie");
|
||||
}
|
||||
|
@ -1330,47 +1316,6 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
|
|||
// Make sweeper aware that there is a zombie method that needs to be removed
|
||||
NMethodSweeper::notify(this);
|
||||
|
||||
// not_entrant only stuff
|
||||
if (state == not_entrant) {
|
||||
mark_as_seen_on_stack();
|
||||
}
|
||||
|
||||
if (was_alive) {
|
||||
// It's a true state change, so mark the method as decompiled.
|
||||
// Do it only for transition from alive.
|
||||
inc_decompile_count();
|
||||
}
|
||||
|
||||
// zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event
|
||||
// and it hasn't already been reported for this nmethod then report it now.
|
||||
// (the event may have been reported earilier if the GC marked it for unloading).
|
||||
if (state == zombie) {
|
||||
post_compiled_method_unload();
|
||||
}
|
||||
|
||||
|
||||
// Zombie only stuff
|
||||
if (state == zombie) {
|
||||
VTune::delete_nmethod(this);
|
||||
}
|
||||
|
||||
// Check whether method got unloaded at a safepoint before this,
|
||||
// if so we can skip the flushing steps below
|
||||
if (method() == NULL) return true;
|
||||
|
||||
// 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 methodOop 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()->code() == this ||
|
||||
method()->from_compiled_entry() == verified_entry_point()) {
|
||||
HandleMark hm;
|
||||
method()->clear_code();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2109,7 +2054,6 @@ address nmethod::continuation_for_implicit_exception(address pc) {
|
|||
|
||||
void nmethod_init() {
|
||||
// make sure you didn't forget to adjust the filler fields
|
||||
assert(sizeof(nmFlags) <= 4, "nmFlags occupies more than a word");
|
||||
assert(sizeof(nmethod) % oopSize == 0, "nmethod size must be multiple of a word");
|
||||
}
|
||||
|
||||
|
@ -2345,7 +2289,6 @@ void nmethod::print() const {
|
|||
tty->print("((nmethod*) "INTPTR_FORMAT ") ", this);
|
||||
tty->print(" for method " INTPTR_FORMAT , (address)method());
|
||||
tty->print(" { ");
|
||||
if (version()) tty->print("v%d ", version());
|
||||
if (is_in_use()) tty->print("in_use ");
|
||||
if (is_not_entrant()) tty->print("not_entrant ");
|
||||
if (is_zombie()) tty->print("zombie ");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue