Mercurial > hg > graal-compiler
diff src/share/vm/code/nmethod.cpp @ 1644:2a47bd84841f
6965184: possible races in make_not_entrant_or_zombie
Reviewed-by: kvn
author | never |
---|---|
date | Thu, 08 Jul 2010 14:29:44 -0700 |
parents | 38e8278318ca |
children | 4a665be40fd3 |
line wrap: on
line diff
--- a/src/share/vm/code/nmethod.cpp Wed Jul 07 12:40:01 2010 -0700 +++ b/src/share/vm/code/nmethod.cpp Thu Jul 08 14:29:44 2010 -0700 @@ -397,11 +397,6 @@ //-------------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 @@ 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 @@ 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 @@ _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 @@ 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 @@ _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 @@ 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 @@ _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 @@ 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::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 @@ // 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 @@ // for later on. CodeCache::set_needs_cache_clean(true); } - flags.state = unloaded; + _state = unloaded; // Log the unloading. log_state_change(); @@ -1236,21 +1202,21 @@ 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 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 @@ // 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 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ 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 ");