8255678: Add Mutex::try_lock version without rank checks

Reviewed-by: dcubed, dholmes, coleenp
This commit is contained in:
Patricio Chilano Mateo 2020-11-23 15:41:19 +00:00
parent 884b9ff24f
commit aabc9ca266
3 changed files with 273 additions and 82 deletions
src/hotspot/share/runtime
test/hotspot/gtest/runtime

@ -100,10 +100,11 @@ void Mutex::lock_contended(Thread* self) {
}
void Mutex::lock(Thread* self) {
check_safepoint_state(self);
assert(_owner != self, "invariant");
check_safepoint_state(self);
check_rank(self);
if (!_lock.try_lock()) {
// The lock is contended, use contended slow-path function to lock
lock_contended(self);
@ -124,8 +125,11 @@ void Mutex::lock() {
// in the wrong way this can lead to a deadlock with the safepoint code.
void Mutex::lock_without_safepoint_check(Thread * self) {
check_no_safepoint_state(self);
assert(_owner != self, "invariant");
check_no_safepoint_state(self);
check_rank(self);
_lock.lock();
assert_owner(NULL);
set_owner(self);
@ -137,14 +141,22 @@ void Mutex::lock_without_safepoint_check() {
// Returns true if thread succeeds in grabbing the lock, otherwise false.
bool Mutex::try_lock() {
bool Mutex::try_lock_inner(bool do_rank_checks) {
Thread * const self = Thread::current();
// Checking the owner hides the potential difference in recursive locking behaviour
// on some platforms.
if (_owner == self) {
return false;
}
if (do_rank_checks) {
check_rank(self);
}
// Some safepoint_check_always locks use try_lock, so cannot check
// safepoint state, but can check blocking state.
check_block_state(self);
// Checking the owner hides the potential difference in recursive locking behaviour
// on some platforms.
if (_owner != self && _lock.try_lock()) {
if (_lock.try_lock()) {
assert_owner(NULL);
set_owner(self);
return true;
@ -152,6 +164,16 @@ bool Mutex::try_lock() {
return false;
}
bool Mutex::try_lock() {
return try_lock_inner(true /* do_rank_checks */);
}
bool Mutex::try_lock_without_rank_check() {
bool res = try_lock_inner(false /* do_rank_checks */);
DEBUG_ONLY(if (res) _skip_rank_check = true;)
return res;
}
void Mutex::release_for_safepoint() {
assert_owner(NULL);
_lock.unlock();
@ -173,31 +195,18 @@ void Monitor::notify_all() {
_lock.notify_all();
}
#ifdef ASSERT
void Monitor::assert_wait_lock_state(Thread* self) {
Mutex* least = get_least_ranked_lock_besides_this(self->owned_locks());
assert(least != this, "Specification of get_least_... call above");
if (least != NULL && least->rank() <= special) {
::tty->print("Attempting to wait on monitor %s/%d while holding"
" lock %s/%d -- possible deadlock",
name(), rank(), least->name(), least->rank());
assert(false, "Shouldn't block(wait) while holding a lock of rank special");
}
}
#endif // ASSERT
bool Monitor::wait_without_safepoint_check(int64_t timeout) {
Thread* const self = Thread::current();
// timeout is in milliseconds - with zero meaning never timeout
assert(timeout >= 0, "negative timeout");
assert_owner(self);
assert_wait_lock_state(self);
check_rank(self);
// conceptually set the owner to NULL in anticipation of
// abdicating the lock in wait
set_owner(NULL);
// Check safepoint state after resetting owner and possible NSV.
check_no_safepoint_state(self);
@ -208,23 +217,22 @@ bool Monitor::wait_without_safepoint_check(int64_t timeout) {
bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
JavaThread* const self = JavaThread::current();
// Safepoint checking logically implies an active JavaThread.
assert(self->is_active_Java_thread(), "invariant");
// timeout is in milliseconds - with zero meaning never timeout
assert(timeout >= 0, "negative timeout");
assert_owner(self);
check_rank(self);
// Safepoint checking logically implies an active JavaThread.
guarantee(self->is_active_Java_thread(), "invariant");
assert_wait_lock_state(self);
int wait_status;
// conceptually set the owner to NULL in anticipation of
// abdicating the lock in wait
set_owner(NULL);
// Check safepoint state after resetting owner and possible NSV.
check_safepoint_state(self);
int wait_status;
Mutex* in_flight_mutex = NULL;
{
@ -285,6 +293,7 @@ Mutex::Mutex(int Rank, const char * name, bool allow_vm_block,
_allow_vm_block = allow_vm_block;
_rank = Rank;
_safepoint_check_required = safepoint_check_required;
_skip_rank_check = false;
assert(_safepoint_check_required != _safepoint_check_sometimes || is_sometimes_ok(name),
"Lock has _safepoint_check_sometimes %s", name);
@ -330,7 +339,7 @@ void Mutex::print_on(outputStream* st) const {
st->print(" %s", print_safepoint_check(_safepoint_check_required));
st->cr();
}
#endif
#endif // PRODUCT
#ifdef ASSERT
void Mutex::assert_owner(Thread * expected) {
@ -353,16 +362,6 @@ Mutex* Mutex::get_least_ranked_lock(Mutex* locks) {
res = tmp;
}
}
if (!SafepointSynchronize::is_at_safepoint()) {
// In this case, we expect the held locks to be
// in increasing rank order (modulo any native ranks)
for (tmp = locks; tmp != NULL; tmp = tmp->next()) {
if (tmp->next() != NULL) {
assert(tmp->rank() == Mutex::native ||
tmp->rank() <= tmp->next()->rank(), "mutex rank anomaly?");
}
}
}
return res;
}
@ -373,17 +372,56 @@ Mutex* Mutex::get_least_ranked_lock_besides_this(Mutex* locks) {
res = tmp;
}
}
assert(res != this, "invariant");
return res;
}
// Tests for rank violations that might indicate exposure to deadlock.
void Mutex::check_rank(Thread* thread) {
assert(this->rank() >= 0, "bad lock rank");
Mutex* locks_owned = thread->owned_locks();
if (!SafepointSynchronize::is_at_safepoint()) {
// In this case, we expect the held locks to be
// in increasing rank order (modulo any native ranks)
for (tmp = locks; tmp != NULL; tmp = tmp->next()) {
// We expect the locks already acquired to be in increasing rank order,
// modulo locks of native rank or acquired in try_lock_without_rank_check()
for (Mutex* tmp = locks_owned; tmp != NULL; tmp = tmp->next()) {
if (tmp->next() != NULL) {
assert(tmp->rank() == Mutex::native ||
tmp->rank() <= tmp->next()->rank(), "mutex rank anomaly?");
assert(tmp->rank() == Mutex::native || tmp->rank() < tmp->next()->rank()
|| tmp->skip_rank_check(), "mutex rank anomaly?");
}
}
}
return res;
// Locks with rank native or suspend_resume are an exception and are not
// subject to the verification rules.
bool check_can_be_skipped = this->rank() == Mutex::native || this->rank() == Mutex::suspend_resume
|| SafepointSynchronize::is_at_safepoint();
if (owned_by_self()) {
// wait() case
Mutex* least = get_least_ranked_lock_besides_this(locks_owned);
// We enforce not holding locks of rank special or lower while waiting.
// Also "this" should be the monitor with lowest rank owned by this thread.
if (least != NULL && (least->rank() <= special ||
(least->rank() <= this->rank() && !check_can_be_skipped))) {
assert(false, "Attempting to wait on monitor %s/%d while holding lock %s/%d -- "
"possible deadlock. %s", name(), rank(), least->name(), least->rank(),
least->rank() <= this->rank() ? "Should wait on the least ranked monitor from "
"all owned locks." : "Should not block(wait) while holding a lock of rank special.");
}
} else if (!check_can_be_skipped) {
// lock()/lock_without_safepoint_check()/try_lock() case
Mutex* least = get_least_ranked_lock(locks_owned);
// Deadlock prevention rules require us to acquire Mutexes only in
// a global total order. For example, if m1 is the lowest ranked mutex
// that the thread holds and m2 is the mutex the thread is trying
// to acquire, then deadlock prevention rules require that the rank
// of m2 be less than the rank of m1. This prevents circular waits.
if (least != NULL && least->rank() <= this->rank()) {
thread->print_owned_locks();
assert(false, "Attempting to acquire lock %s/%d out of order with lock %s/%d -- "
"possible deadlock", this->name(), this->rank(), least->name(), least->rank());
}
}
}
bool Mutex::contains(Mutex* locks, Mutex* lock) {
@ -413,8 +451,7 @@ void Mutex::no_safepoint_verifier(Thread* thread, bool enable) {
}
// Called immediately after lock acquisition or release as a diagnostic
// to track the lock-set of the thread and test for rank violations that
// might indicate exposure to deadlock.
// to track the lock-set of the thread.
// Rather like an EventListener for _owner (:>).
void Mutex::set_owner_implementation(Thread *new_owner) {
@ -436,29 +473,6 @@ void Mutex::set_owner_implementation(Thread *new_owner) {
_owner = new_owner; // set the owner
// link "this" into the owned locks list
Mutex* locks = get_least_ranked_lock(new_owner->owned_locks());
// Mutex::set_owner_implementation is a friend of Thread
assert(this->rank() >= 0, "bad lock rank");
// Deadlock avoidance rules require us to acquire Mutexes only in
// a global total order. For example m1 is the lowest ranked mutex
// that the thread holds and m2 is the mutex the thread is trying
// to acquire, then deadlock avoidance rules require that the rank
// of m2 be less than the rank of m1.
// The rank Mutex::native is an exception in that it is not subject
// to the verification rules.
if (this->rank() != Mutex::native &&
this->rank() != Mutex::suspend_resume &&
locks != NULL && locks->rank() <= this->rank() &&
!SafepointSynchronize::is_at_safepoint()) {
new_owner->print_owned_locks();
fatal("acquiring lock %s/%d out of order with lock %s/%d -- "
"possible deadlock", this->name(), this->rank(),
locks->name(), locks->rank());
}
this->_next = new_owner->_owned_locks;
new_owner->_owned_locks = this;
@ -470,6 +484,7 @@ void Mutex::set_owner_implementation(Thread *new_owner) {
Thread* old_owner = _owner;
_last_owner = old_owner;
_skip_rank_check = false;
assert(old_owner != NULL, "removing the owner thread of an unowned mutex");
assert(old_owner == Thread::current(), "removing the owner thread of an unowned mutex");

@ -89,18 +89,33 @@ class Mutex : public CHeapObj<mtSynchronizer> {
// Debugging fields for naming, deadlock detection, etc. (some only used in debug mode)
#ifndef PRODUCT
bool _allow_vm_block;
#endif
#ifdef ASSERT
int _rank; // rank (to avoid/detect potential deadlocks)
Mutex* _next; // Used by a Thread to link up owned locks
Thread* _last_owner; // the last thread to own the lock
bool _skip_rank_check; // read only by owner when doing rank checks
static bool contains(Mutex* locks, Mutex* lock);
static Mutex* get_least_ranked_lock(Mutex* locks);
Mutex* get_least_ranked_lock_besides_this(Mutex* locks);
#endif // ASSERT
bool skip_rank_check() {
assert(owned_by_self(), "only the owner should call this");
return _skip_rank_check;
}
public:
int rank() const { return _rank; }
Mutex* next() const { return _next; }
void set_next(Mutex *next) { _next = next; }
#endif // ASSERT
protected:
void set_owner_implementation(Thread* owner) NOT_DEBUG({ _owner = owner;});
void check_block_state (Thread* thread) NOT_DEBUG_RETURN;
void check_safepoint_state (Thread* thread) NOT_DEBUG_RETURN;
void check_no_safepoint_state(Thread* thread) NOT_DEBUG_RETURN;
void check_rank (Thread* thread) NOT_DEBUG_RETURN;
void assert_owner (Thread* expected) NOT_DEBUG_RETURN;
void no_safepoint_verifier (Thread* thread, bool enable) NOT_DEBUG_RETURN;
@ -170,6 +185,7 @@ class Mutex : public CHeapObj<mtSynchronizer> {
bool try_lock(); // Like lock(), but unblocking. It returns false instead
private:
void lock_contended(Thread *thread); // contended slow-path
bool try_lock_inner(bool do_rank_checks);
public:
void release_for_safepoint();
@ -178,33 +194,25 @@ class Mutex : public CHeapObj<mtSynchronizer> {
// that is guaranteed not to block while running inside the VM.
void lock_without_safepoint_check();
void lock_without_safepoint_check(Thread* self);
// A thread should not call this if failure to acquire ownership will blocks its progress
bool try_lock_without_rank_check();
// Current owner - not not MT-safe. Can only be used to guarantee that
// the current running thread owns the lock
Thread* owner() const { return _owner; }
void set_owner(Thread* owner) { set_owner_implementation(owner); }
bool owned_by_self() const;
const char *name() const { return _name; }
void print_on_error(outputStream* st) const;
#ifndef PRODUCT
void print_on(outputStream* st) const;
void print() const { print_on(::tty); }
#endif
#ifdef ASSERT
int rank() const { return _rank; }
bool allow_vm_block() { return _allow_vm_block; }
Mutex *next() const { return _next; }
void set_next(Mutex *next) { _next = next; }
#endif // ASSERT
void set_owner(Thread* owner) { set_owner_implementation(owner); }
};
class Monitor : public Mutex {
void assert_wait_lock_state (Thread* self) NOT_DEBUG_RETURN;
public:
Monitor(int rank, const char *name, bool allow_vm_block = false,
SafepointCheckRequired safepoint_check_required = _safepoint_check_always);

@ -0,0 +1,168 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/mutex.hpp"
#include "runtime/mutexLocker.hpp"
#include "unittest.hpp"
#ifdef ASSERT
const int rankA = 50;
TEST_OTHER_VM(MutexRank, mutex_lock_rank_in_order) {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA", false, Mutex::_safepoint_check_always);
Mutex* mutex_rankA_plus_one = new Mutex(rankA + 1, "mutex_rankA_plus_one", false, Mutex::_safepoint_check_always);
mutex_rankA_plus_one->lock();
mutex_rankA->lock();
mutex_rankA->unlock();
mutex_rankA_plus_one->unlock();
}
TEST_VM_ASSERT_MSG(MutexRank, mutex_lock_rank_out_of_orderA,
"Attempting to acquire lock mutex_rankA_plus_one/51 out of order with lock mutex_rankA/50 -- possible deadlock") {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA", false, Mutex::_safepoint_check_always);
Mutex* mutex_rankA_plus_one = new Mutex(rankA + 1, "mutex_rankA_plus_one", false, Mutex::_safepoint_check_always);
mutex_rankA->lock();
mutex_rankA_plus_one->lock();
mutex_rankA_plus_one->unlock();
mutex_rankA->unlock();
}
TEST_VM_ASSERT_MSG(MutexRank, mutex_lock_rank_out_of_orderB,
"Attempting to acquire lock mutex_rankB/50 out of order with lock mutex_rankA/50 -- possible deadlock") {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA", false, Mutex::_safepoint_check_always);
Mutex* mutex_rankB = new Mutex(rankA, "mutex_rankB", false, Mutex::_safepoint_check_always);
mutex_rankA->lock();
mutex_rankB->lock();
mutex_rankB->unlock();
mutex_rankA->unlock();
}
TEST_OTHER_VM(MutexRank, mutex_trylock_rank_out_of_orderA) {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA", false, Mutex::_safepoint_check_always);
Mutex* mutex_rankA_plus_one = new Mutex(rankA + 1, "mutex_rankA_plus_one", false, Mutex::_safepoint_check_always);
Mutex* mutex_rankA_plus_two = new Mutex(rankA + 2, "mutex_rankA_plus_two", false, Mutex::_safepoint_check_always);
mutex_rankA_plus_one->lock();
mutex_rankA_plus_two->try_lock_without_rank_check();
mutex_rankA->lock();
mutex_rankA->unlock();
mutex_rankA_plus_two->unlock();
mutex_rankA_plus_one->unlock();
}
TEST_VM_ASSERT_MSG(MutexRank, mutex_trylock_rank_out_of_orderB,
"Attempting to acquire lock mutex_rankA_plus_one/51 out of order with lock mutex_rankA/50 -- possible deadlock") {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Mutex* mutex_rankA = new Mutex(rankA, "mutex_rankA", false, Mutex::_safepoint_check_always);
Mutex* mutex_rankA_plus_one = new Mutex(rankA + 1, "mutex_rankA_plus_one", false, Mutex::_safepoint_check_always);
mutex_rankA->lock();
mutex_rankA_plus_one->try_lock_without_rank_check();
mutex_rankA_plus_one->unlock();
mutex_rankA_plus_one->try_lock();
mutex_rankA_plus_one->unlock();
mutex_rankA->unlock();
}
TEST_OTHER_VM(MutexRank, monitor_wait_rank_in_order) {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Monitor* monitor_rankA = new Monitor(rankA, "monitor_rankA", false, Mutex::_safepoint_check_always);
Monitor* monitor_rankA_plus_one = new Monitor(rankA + 1, "monitor_rankA_plus_one", false, Mutex::_safepoint_check_always);
monitor_rankA_plus_one->lock();
monitor_rankA->lock();
monitor_rankA->wait(1);
monitor_rankA->unlock();
monitor_rankA_plus_one->unlock();
}
TEST_VM_ASSERT_MSG(MutexRank, monitor_wait_rank_out_of_order,
"Attempting to wait on monitor monitor_rankA_plus_one/51 while holding lock monitor_rankA/50 "
"-- possible deadlock. Should wait on the least ranked monitor from all owned locks.") {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Monitor* monitor_rankA = new Monitor(rankA, "monitor_rankA", false, Mutex::_safepoint_check_always);
Monitor* monitor_rankA_plus_one = new Monitor(rankA + 1, "monitor_rankA_plus_one", false, Mutex::_safepoint_check_always);
monitor_rankA_plus_one->lock();
monitor_rankA->lock();
monitor_rankA_plus_one->wait(1);
monitor_rankA_plus_one->unlock();
monitor_rankA->unlock();
}
TEST_VM_ASSERT_MSG(MutexRank, monitor_wait_rank_out_of_order_trylock,
"Attempting to wait on monitor monitor_rankA_plus_one/51 while holding lock monitor_rankA/50 "
"-- possible deadlock. Should wait on the least ranked monitor from all owned locks.") {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Monitor* monitor_rankA = new Monitor(rankA, "monitor_rankA", false, Mutex::_safepoint_check_always);
Monitor* monitor_rankA_plus_one = new Monitor(rankA + 1, "monitor_rankA_plus_one", false, Mutex::_safepoint_check_always);
monitor_rankA->lock();
monitor_rankA_plus_one->try_lock_without_rank_check();
monitor_rankA_plus_one->wait();
monitor_rankA_plus_one->unlock();
monitor_rankA->unlock();
}
TEST_VM_ASSERT_MSG(MutexRank, monitor_wait_rank_special,
"Attempting to wait on monitor monitor_rank_special_minus_one/5 while holding lock monitor_rank_special/6 "
"-- possible deadlock. Should not block\\(wait\\) while holding a lock of rank special.") {
JavaThread* THREAD = JavaThread::current();
ThreadInVMfromNative invm(THREAD);
Monitor* monitor_rank_special = new Monitor(Mutex::special, "monitor_rank_special", false, Mutex::_safepoint_check_never);
Monitor* monitor_rank_special_minus_one = new Monitor(Mutex::special - 1, "monitor_rank_special_minus_one", false, Mutex::_safepoint_check_never);
monitor_rank_special->lock_without_safepoint_check();
monitor_rank_special_minus_one->lock_without_safepoint_check();
monitor_rank_special_minus_one->wait_without_safepoint_check(1);
monitor_rank_special_minus_one->unlock();
monitor_rank_special->unlock();
}
#endif // ASSERT