# HG changeset patch # User never # Date 1275513803 25200 # Node ID a78767273dc17cd704e0de51e7a3c5524197feb7 # Parent c864b4cae5fc556c97a14923df2a96cf699da44d 6956931: assert(SafepointSynchronize::is_at_safepoint()) failed: must be executed at a safepoint Reviewed-by: kvn, dcubed diff -r c864b4cae5fc -r a78767273dc1 hotspot/src/share/vm/code/nmethod.cpp --- a/hotspot/src/share/vm/code/nmethod.cpp Wed Jun 02 12:02:49 2010 -0700 +++ b/hotspot/src/share/vm/code/nmethod.cpp Wed Jun 02 14:23:23 2010 -0700 @@ -1342,19 +1342,7 @@ // 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) { - - DTRACE_METHOD_UNLOAD_PROBE(method()); - - if (JvmtiExport::should_post_compiled_method_unload() && - !unload_reported()) { - assert(method() != NULL, "checking"); - { - HandleMark hm; - JvmtiExport::post_compiled_method_unload_at_safepoint( - method()->jmethod_id(), code_begin()); - } - set_unload_reported(); - } + post_compiled_method_unload(); } @@ -1506,6 +1494,12 @@ } void nmethod::post_compiled_method_unload() { + if (unload_reported()) { + // During unloading we transition to unloaded and then to zombie + // and the unloading is reported during the first transition. + return; + } + assert(_method != NULL && !is_unloaded(), "just checking"); DTRACE_METHOD_UNLOAD_PROBE(method()); @@ -1515,8 +1509,7 @@ if (JvmtiExport::should_post_compiled_method_unload()) { assert(!unload_reported(), "already unloaded"); HandleMark hm; - JvmtiExport::post_compiled_method_unload_at_safepoint( - method()->jmethod_id(), code_begin()); + JvmtiExport::post_compiled_method_unload(method()->jmethod_id(), code_begin()); } // The JVMTI CompiledMethodUnload event can be enabled or disabled at diff -r c864b4cae5fc -r a78767273dc1 hotspot/src/share/vm/prims/jvmtiExport.cpp --- a/hotspot/src/share/vm/prims/jvmtiExport.cpp Wed Jun 02 12:02:49 2010 -0700 +++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp Wed Jun 02 14:23:23 2010 -0700 @@ -726,6 +726,32 @@ GrowableArray* JvmtiExport::_pending_compiled_method_unload_code_begins; JavaThread* JvmtiExport::_current_poster; +void JvmtiExport::post_compiled_method_unload_internal(JavaThread* self, jmethodID method, const void *code_begin) { + EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD, + ("JVMTI [%s] method compile unload event triggered", + JvmtiTrace::safe_get_thread_name(self))); + + // post the event for each environment that has this event enabled. + JvmtiEnvIterator it; + for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { + if (env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_UNLOAD)) { + + EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD, + ("JVMTI [%s] class compile method unload event sent jmethodID " PTR_FORMAT, + JvmtiTrace::safe_get_thread_name(self), method)); + + ResourceMark rm(self); + + JvmtiEventMark jem(self); + JvmtiJavaThreadEventTransition jet(self); + jvmtiEventCompiledMethodUnload callback = env->callbacks()->CompiledMethodUnload; + if (callback != NULL) { + (*callback)(env->jvmti_external(), method, code_begin); + } + } + } +} + // post any pending CompiledMethodUnload events void JvmtiExport::post_pending_compiled_method_unload_events() { @@ -788,26 +814,7 @@ // flag, cleanup _current_poster to indicate that no thread is now servicing the // pending events list, and finally notify any thread that might be waiting. for (;;) { - EVT_TRIG_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD, - ("JVMTI [%s] method compile unload event triggered", - JvmtiTrace::safe_get_thread_name(self))); - - // post the event for each environment that has this event enabled. - JvmtiEnvIterator it; - for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) { - if (env->is_enabled(JVMTI_EVENT_COMPILED_METHOD_UNLOAD)) { - EVT_TRACE(JVMTI_EVENT_COMPILED_METHOD_UNLOAD, - ("JVMTI [%s] class compile method unload event sent jmethodID " PTR_FORMAT, - JvmtiTrace::safe_get_thread_name(self), method)); - - JvmtiEventMark jem(self); - JvmtiJavaThreadEventTransition jet(self); - jvmtiEventCompiledMethodUnload callback = env->callbacks()->CompiledMethodUnload; - if (callback != NULL) { - (*callback)(env->jvmti_external(), method, code_begin); - } - } - } + post_compiled_method_unload_internal(self, method, code_begin); // event posted, now re-grab monitor and get the next event // If there's no next event then we are done. If this is the first @@ -1864,17 +1871,25 @@ } // used at a safepoint to post a CompiledMethodUnload event -void JvmtiExport::post_compiled_method_unload_at_safepoint(jmethodID mid, const void *code_begin) { - assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); - - // create list lazily - if (_pending_compiled_method_unload_method_ids == NULL) { - _pending_compiled_method_unload_method_ids = new (ResourceObj::C_HEAP) GrowableArray(10,true); - _pending_compiled_method_unload_code_begins = new (ResourceObj::C_HEAP) GrowableArray(10,true); +void JvmtiExport::post_compiled_method_unload(jmethodID mid, const void *code_begin) { + if (SafepointSynchronize::is_at_safepoint()) { + // Class unloading can cause nmethod unloading which is reported + // by the VMThread. These must be batched to be processed later. + if (_pending_compiled_method_unload_method_ids == NULL) { + // create list lazily + _pending_compiled_method_unload_method_ids = new (ResourceObj::C_HEAP) GrowableArray(10,true); + _pending_compiled_method_unload_code_begins = new (ResourceObj::C_HEAP) GrowableArray(10,true); + } + _pending_compiled_method_unload_method_ids->append(mid); + _pending_compiled_method_unload_code_begins->append(code_begin); + _have_pending_compiled_method_unload_events = true; + } else { + // Unloading caused by the sweeper can be reported synchronously. + if (have_pending_compiled_method_unload_events()) { + post_pending_compiled_method_unload_events(); + } + post_compiled_method_unload_internal(JavaThread::current(), mid, code_begin); } - _pending_compiled_method_unload_method_ids->append(mid); - _pending_compiled_method_unload_code_begins->append(code_begin); - _have_pending_compiled_method_unload_events = true; } void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) { diff -r c864b4cae5fc -r a78767273dc1 hotspot/src/share/vm/prims/jvmtiExport.hpp --- a/hotspot/src/share/vm/prims/jvmtiExport.hpp Wed Jun 02 12:02:49 2010 -0700 +++ b/hotspot/src/share/vm/prims/jvmtiExport.hpp Wed Jun 02 14:23:23 2010 -0700 @@ -144,6 +144,9 @@ // posts any pending CompiledMethodUnload events. static void post_pending_compiled_method_unload_events(); + // Perform the actual notification to interested JvmtiEnvs. + static void post_compiled_method_unload_internal(JavaThread* self, jmethodID mid, const void* code_begin); + // posts a DynamicCodeGenerated event (internal/private implementation). // The public post_dynamic_code_generated* functions make use of the // internal implementation. @@ -299,8 +302,8 @@ static void post_compiled_method_load(nmethod *nm) KERNEL_RETURN; static void post_dynamic_code_generated(const char *name, const void *code_begin, const void *code_end) KERNEL_RETURN; - // used at a safepoint to post a CompiledMethodUnload event - static void post_compiled_method_unload_at_safepoint(jmethodID mid, const void *code_begin) KERNEL_RETURN; + // used to post a CompiledMethodUnload event + static void post_compiled_method_unload(jmethodID mid, const void *code_begin) KERNEL_RETURN; // similiar to post_dynamic_code_generated except that it can be used to // post a DynamicCodeGenerated event while holding locks in the VM. Any event