# HG changeset patch # User kamg # Date 1298745203 18000 # Node ID f91db74a6810559bb3995e5cea9861a030808f33 # Parent 162b6246026460b3aa513d754b162176c9171d22 7017640: Fix for 6766644 deadlocks on some NSK tests when running with -Xcomp Summary: Dynamic-code generated events should be deferred and processed by service thread Reviewed-by: dsamersoff, dcubed diff -r 162b62460264 -r f91db74a6810 src/share/vm/prims/jvmtiExport.cpp --- a/src/share/vm/prims/jvmtiExport.cpp Thu Feb 24 21:38:37 2011 -0500 +++ b/src/share/vm/prims/jvmtiExport.cpp Sat Feb 26 13:33:23 2011 -0500 @@ -1805,6 +1805,10 @@ void JvmtiExport::post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) { JavaThread* thread = JavaThread::current(); + // In theory everyone coming thru here is in_vm but we need to be certain + // because a callee will do a vm->native transition + ThreadInVMfromUnknown __tiv; + EVT_TRIG_TRACE(JVMTI_EVENT_DYNAMIC_CODE_GENERATED, ("JVMTI [%s] method dynamic code generated event triggered", JvmtiTrace::safe_get_thread_name(thread))); @@ -1826,19 +1830,18 @@ } void JvmtiExport::post_dynamic_code_generated(const char *name, const void *code_begin, const void *code_end) { - // In theory everyone coming thru here is in_vm but we need to be certain - // because a callee will do a vm->native transition - ThreadInVMfromUnknown __tiv; jvmtiPhase phase = JvmtiEnv::get_phase(); if (phase == JVMTI_PHASE_PRIMORDIAL || phase == JVMTI_PHASE_START) { post_dynamic_code_generated_internal(name, code_begin, code_end); - return; + } else { + // It may not be safe to post the event from this thread. Defer all + // postings to the service thread so that it can perform them in a safe + // context and in-order. + MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); + JvmtiDeferredEvent event = JvmtiDeferredEvent::dynamic_code_generated_event( + name, code_begin, code_end); + JvmtiDeferredEventQueue::enqueue(event); } - - // Blocks until everything now in the queue has been posted - JvmtiDeferredEventQueue::flush_queue(Thread::current()); - - post_dynamic_code_generated_internal(name, code_begin, code_end); } diff -r 162b62460264 -r f91db74a6810 src/share/vm/prims/jvmtiExport.hpp --- a/src/share/vm/prims/jvmtiExport.hpp Thu Feb 24 21:38:37 2011 -0500 +++ b/src/share/vm/prims/jvmtiExport.hpp Sat Feb 26 13:33:23 2011 -0500 @@ -140,12 +140,12 @@ char sig_type, jvalue *value); - private: // posts a DynamicCodeGenerated event (internal/private implementation). // The public post_dynamic_code_generated* functions make use of the - // internal implementation. + // internal implementation. Also called from JvmtiDeferredEvent::post() static void post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) KERNEL_RETURN; + private: // GenerateEvents support to allow posting of CompiledMethodLoad and // DynamicCodeGenerated events for a given environment. diff -r 162b62460264 -r f91db74a6810 src/share/vm/prims/jvmtiImpl.cpp --- a/src/share/vm/prims/jvmtiImpl.cpp Thu Feb 24 21:38:37 2011 -0500 +++ b/src/share/vm/prims/jvmtiImpl.cpp Sat Feb 26 13:33:23 2011 -0500 @@ -918,7 +918,7 @@ JvmtiDeferredEvent JvmtiDeferredEvent::compiled_method_load_event( nmethod* nm) { JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_COMPILED_METHOD_LOAD); - event.set_compiled_method_load(nm); + event._event_data.compiled_method_load = nm; nmethodLocker::lock_nmethod(nm); // will be unlocked when posted return event; } @@ -926,23 +926,39 @@ JvmtiDeferredEvent JvmtiDeferredEvent::compiled_method_unload_event( jmethodID id, const void* code) { JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_COMPILED_METHOD_UNLOAD); - event.set_compiled_method_unload(id, code); + event._event_data.compiled_method_unload.method_id = id; + event._event_data.compiled_method_unload.code_begin = code; + return event; +} +JvmtiDeferredEvent JvmtiDeferredEvent::dynamic_code_generated_event( + const char* name, const void* code_begin, const void* code_end) { + JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_DYNAMIC_CODE_GENERATED); + event._event_data.dynamic_code_generated.name = name; + event._event_data.dynamic_code_generated.code_begin = code_begin; + event._event_data.dynamic_code_generated.code_end = code_end; return event; } void JvmtiDeferredEvent::post() { + assert(ServiceThread::is_service_thread(Thread::current()), + "Service thread must post enqueued events"); switch(_type) { - case TYPE_COMPILED_METHOD_LOAD: - JvmtiExport::post_compiled_method_load(compiled_method_load()); - nmethodLocker::unlock_nmethod(compiled_method_load()); + case TYPE_COMPILED_METHOD_LOAD: { + nmethod* nm = _event_data.compiled_method_load; + JvmtiExport::post_compiled_method_load(nm); + nmethodLocker::unlock_nmethod(nm); break; + } case TYPE_COMPILED_METHOD_UNLOAD: JvmtiExport::post_compiled_method_unload( - compiled_method_unload_method_id(), - compiled_method_unload_code_begin()); + _event_data.compiled_method_unload.method_id, + _event_data.compiled_method_unload.code_begin); break; - case TYPE_FLUSH: - JvmtiDeferredEventQueue::flush_complete(flush_state_addr()); + case TYPE_DYNAMIC_CODE_GENERATED: + JvmtiExport::post_dynamic_code_generated_internal( + _event_data.dynamic_code_generated.name, + _event_data.dynamic_code_generated.code_begin, + _event_data.dynamic_code_generated.code_end); break; default: ShouldNotReachHere(); @@ -1065,54 +1081,4 @@ } } -enum { - // Random - used for debugging - FLUSHING = 0x50403020, - FLUSHED = 0x09080706 -}; - -void JvmtiDeferredEventQueue::flush_queue(Thread* thread) { - - volatile int flush_state = FLUSHING; - - JvmtiDeferredEvent flush(JvmtiDeferredEvent::TYPE_FLUSH); - flush.set_flush_state_addr((int*)&flush_state); - - if (ServiceThread::is_service_thread(thread)) { - // If we are the service thread we have to post all preceding events - // Use the flush event as a token to indicate when we can stop - JvmtiDeferredEvent event; - { - MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); - enqueue(flush); - event = dequeue(); - } - while (!event.is_flush_event() || - event.flush_state_addr() != &flush_state) { - event.post(); - { - MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); - event = dequeue(); - } - } - } else { - // Wake up the service thread so it will process events. When it gets - // to the flush event it will set 'flush_complete' and notify us. - MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); - enqueue(flush); - while (flush_state != FLUSHED) { - assert(flush_state == FLUSHING || flush_state == FLUSHED, - "only valid values for this"); - Service_lock->wait(Mutex::_no_safepoint_check_flag); - } - } -} - -void JvmtiDeferredEventQueue::flush_complete(int* state_addr) { - assert(state_addr != NULL && *state_addr == FLUSHING, "must be"); - MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); - *state_addr = FLUSHED; - Service_lock->notify_all(); -} - #endif // ndef KERNEL diff -r 162b62460264 -r f91db74a6810 src/share/vm/prims/jvmtiImpl.hpp --- a/src/share/vm/prims/jvmtiImpl.hpp Thu Feb 24 21:38:37 2011 -0500 +++ b/src/share/vm/prims/jvmtiImpl.hpp Sat Feb 26 13:33:23 2011 -0500 @@ -451,7 +451,7 @@ TYPE_NONE, TYPE_COMPILED_METHOD_LOAD, TYPE_COMPILED_METHOD_UNLOAD, - TYPE_FLUSH // pseudo-event used to implement flush_queue() + TYPE_DYNAMIC_CODE_GENERATED } Type; Type _type; @@ -461,49 +461,15 @@ jmethodID method_id; const void* code_begin; } compiled_method_unload; - int* flush_state_addr; + struct { + const char* name; + const void* code_begin; + const void* code_end; + } dynamic_code_generated; } _event_data; JvmtiDeferredEvent(Type t) : _type(t) {} - void set_compiled_method_load(nmethod* nm) { - assert(_type == TYPE_COMPILED_METHOD_LOAD, "must be"); - _event_data.compiled_method_load = nm; - } - - nmethod* compiled_method_load() const { - assert(_type == TYPE_COMPILED_METHOD_LOAD, "must be"); - return _event_data.compiled_method_load; - } - - void set_compiled_method_unload(jmethodID id, const void* code) { - assert(_type == TYPE_COMPILED_METHOD_UNLOAD, "must be"); - _event_data.compiled_method_unload.method_id = id; - _event_data.compiled_method_unload.code_begin = code; - } - - jmethodID compiled_method_unload_method_id() const { - assert(_type == TYPE_COMPILED_METHOD_UNLOAD, "must be"); - return _event_data.compiled_method_unload.method_id; - } - - const void* compiled_method_unload_code_begin() const { - assert(_type == TYPE_COMPILED_METHOD_UNLOAD, "must be"); - return _event_data.compiled_method_unload.code_begin; - } - - bool is_flush_event() const { return _type == TYPE_FLUSH; } - - int* flush_state_addr() const { - assert(is_flush_event(), "must be"); - return _event_data.flush_state_addr; - } - - void set_flush_state_addr(int* flag) { - assert(is_flush_event(), "must be"); - _event_data.flush_state_addr = flag; - } - public: JvmtiDeferredEvent() : _type(TYPE_NONE) {} @@ -513,6 +479,9 @@ KERNEL_RETURN_(JvmtiDeferredEvent()); static JvmtiDeferredEvent compiled_method_unload_event( jmethodID id, const void* code) KERNEL_RETURN_(JvmtiDeferredEvent()); + static JvmtiDeferredEvent dynamic_code_generated_event( + const char* name, const void* begin, const void* end) + KERNEL_RETURN_(JvmtiDeferredEvent()); // Actually posts the event. void post() KERNEL_RETURN; @@ -548,25 +517,12 @@ // Transfers events from the _pending_list to the _queue. static void process_pending_events() KERNEL_RETURN; - static void flush_complete(int* flush_state) KERNEL_RETURN; - public: // Must be holding Service_lock when calling these static bool has_events() KERNEL_RETURN_(false); static void enqueue(const JvmtiDeferredEvent& event) KERNEL_RETURN; static JvmtiDeferredEvent dequeue() KERNEL_RETURN_(JvmtiDeferredEvent()); - // This call blocks until all events enqueued prior to this call - // have been posted. The Service_lock is acquired and waited upon. - // - // Implemented by creating a "flush" event and placing it in the queue. - // When the flush event is "posted" it will call flush_complete(), which - // will release the caller. - // - // Can be called by any thread (maybe even the service thread itself). - // Not necessary for the caller to be a JavaThread. - static void flush_queue(Thread* current) KERNEL_RETURN; - // Used to enqueue events without using a lock, for times (such as during // safepoint) when we can't or don't want to lock the Service_lock. //