# HG changeset patch # User mgronlun # Date 1397033448 -7200 # Node ID 7832777b1ee4804c5f02942b32265a0f3eecbf16 # Parent b60835354c41e55b90d39408c5ed5c36da4649eb 8039458: Ensure consistency of Monitor/Mutex lock acquisitions in relation to safepoint protocol Reviewed-by: diff --git a/src/share/vm/runtime/mutex.cpp b/src/share/vm/runtime/mutex.cpp --- a/src/share/vm/runtime/mutex.cpp +++ b/src/share/vm/runtime/mutex.cpp @@ -900,6 +900,7 @@ Exeunt: assert (ILocked(), "invariant") ; assert (owner() == NULL, "invariant"); + debug_only(_acquired_with_no_safepoint_check = false;) set_owner (Self); return ; } @@ -945,6 +946,7 @@ assert (_owner != Self, "invariant") ; ILock (Self) ; assert (_owner == NULL, "invariant"); + debug_only(_acquired_with_no_safepoint_check = true;) set_owner (Self); } @@ -983,6 +985,7 @@ void Monitor::unlock() { assert (_owner == Thread::current(), "invariant") ; assert (_OnDeck != Thread::current()->_MutexEvent , "invariant") ; + debug_only(_acquired_with_no_safepoint_check = false); set_owner (NULL) ; if (_snuck) { assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(), "sneak"); @@ -1152,6 +1155,7 @@ m->_OnDeck = NULL ; m->_WaitSet = NULL ; m->_WaitLock[0] = 0 ; + debug_only(m->_acquired_with_no_safepoint_check = false); } Monitor::Monitor() { ClearMonitor(this); } @@ -1308,6 +1312,8 @@ locks->name(), locks->rank())); } + debug_only(ensure_lock_acquisition_consistency(new_owner);) + this->_next = new_owner->_owned_locks; new_owner->_owned_locks = this; #endif @@ -1347,6 +1353,53 @@ } } +/* + * Ensure consistency of Monitor/Mutex lock acquisitions + * for Java Threads running inside the VM. + * + * If a thread has already acquired lock(s) using + * "Mutex::_no_safepoint_check_flag" (effectively going outside the + * safepoint protocol), the thread should be disallowed to acquire any + * additional lock which _is_ participating in the safepoint protocol. + * + * If a "safepoint protocol aware" lock is contended, and the thread entering + * is unable to "fast acquire" the lock using cas/try_spin, + * it will need to block/park. Blocking on a contended lock involves a + * state transition and a potential SafepointSynchronize::block() call. + * Transitioning to a blocked state still holding "Mutex::_no_safepoint_check_flag" + * acquired locks is allowed, but is *very* deadlock prone. + * + * The effect of allowing this state to happen without checking is subtle + * and hard to debug since a problem might only appear under heavy load and + * only in situations with increased concurrency levels (product builds). + * + * This function will preempt this problematic state for earlier deadlock detection. + */ +void Monitor::ensure_lock_acquisition_consistency(const Thread* new_owner) const { + if (new_owner != NULL && new_owner->is_Java_thread()) { + const Monitor* const locks = new_owner->owned_locks(); + if (locks != NULL) { + if (!acquired_with_no_safepoint_check()) { + if (locks->acquired_with_no_safepoint_check()) { + new_owner->print_owned_locks(); + fatal(err_msg("Acquiring lock (%s) might " + "cause the thread to block.\n" + "The thread is also the owner " + "of a lock previously taken using " + "Mutex::_no_safepoint_check_flag (%s).\n" + "This pattern is not allowed since " + "it is *very* deadlock prone!", + this->name(), + locks->name())); + } + } + } + } +} + +bool Monitor::acquired_with_no_safepoint_check(void) const { + return _acquired_with_no_safepoint_check; +} // Factored out common sanity checks for locking mutex'es. Used by lock() and try_lock() void Monitor::check_prelock_state(Thread *thread) { diff --git a/src/share/vm/runtime/mutex.hpp b/src/share/vm/runtime/mutex.hpp --- a/src/share/vm/runtime/mutex.hpp +++ b/src/share/vm/runtime/mutex.hpp @@ -140,6 +140,9 @@ debug_only(static bool contains(Monitor * locks, Monitor * lock);) debug_only(static Monitor * get_least_ranked_lock(Monitor * locks);) debug_only(Monitor * get_least_ranked_lock_besides_this(Monitor * locks);) + debug_only(void ensure_lock_acquisition_consistency(const Thread* new_owner) const;) + debug_only(bool acquired_with_no_safepoint_check(void) const;) + debug_only(bool _acquired_with_no_safepoint_check;) #endif void set_owner_implementation(Thread* owner) PRODUCT_RETURN;