6699669: Hotspot server leaves synchronized block with monitor in bad state
Remove usage of _highest_lock field in Thread so that is_lock_owned won't depend on the correct update of that field. Reviewed-by: never, dice, acorn
This commit is contained in:
parent
e1b2c1c442
commit
62fa852746
@ -48,6 +48,8 @@ public class JavaThread extends Thread {
|
|||||||
private static AddressField lastJavaPCField;
|
private static AddressField lastJavaPCField;
|
||||||
private static CIntegerField threadStateField;
|
private static CIntegerField threadStateField;
|
||||||
private static AddressField osThreadField;
|
private static AddressField osThreadField;
|
||||||
|
private static AddressField stackBaseField;
|
||||||
|
private static CIntegerField stackSizeField;
|
||||||
|
|
||||||
private static JavaThreadPDAccess access;
|
private static JavaThreadPDAccess access;
|
||||||
|
|
||||||
@ -83,6 +85,8 @@ public class JavaThread extends Thread {
|
|||||||
lastJavaPCField = anchorType.getAddressField("_last_Java_pc");
|
lastJavaPCField = anchorType.getAddressField("_last_Java_pc");
|
||||||
threadStateField = type.getCIntegerField("_thread_state");
|
threadStateField = type.getCIntegerField("_thread_state");
|
||||||
osThreadField = type.getAddressField("_osthread");
|
osThreadField = type.getAddressField("_osthread");
|
||||||
|
stackBaseField = type.getAddressField("_stack_base");
|
||||||
|
stackSizeField = type.getCIntegerField("_stack_size");
|
||||||
|
|
||||||
UNINITIALIZED = db.lookupIntConstant("_thread_uninitialized").intValue();
|
UNINITIALIZED = db.lookupIntConstant("_thread_uninitialized").intValue();
|
||||||
NEW = db.lookupIntConstant("_thread_new").intValue();
|
NEW = db.lookupIntConstant("_thread_new").intValue();
|
||||||
@ -312,6 +316,14 @@ public class JavaThread extends Thread {
|
|||||||
return (OSThread) VMObjectFactory.newObject(OSThread.class, osThreadField.getValue(addr));
|
return (OSThread) VMObjectFactory.newObject(OSThread.class, osThreadField.getValue(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Address getStackBase() {
|
||||||
|
return stackBaseField.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStackSize() {
|
||||||
|
return stackSizeField.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
/** Gets the Java-side thread object for this JavaThread */
|
/** Gets the Java-side thread object for this JavaThread */
|
||||||
public Oop getThreadObj() {
|
public Oop getThreadObj() {
|
||||||
return VM.getVM().getObjectHeap().newOop(threadObjField.getValue(addr));
|
return VM.getVM().getObjectHeap().newOop(threadObjField.getValue(addr));
|
||||||
@ -345,11 +357,18 @@ public class JavaThread extends Thread {
|
|||||||
if (Assert.ASSERTS_ENABLED) {
|
if (Assert.ASSERTS_ENABLED) {
|
||||||
Assert.that(VM.getVM().isDebugging(), "Not yet implemented for non-debugging system");
|
Assert.that(VM.getVM().isDebugging(), "Not yet implemented for non-debugging system");
|
||||||
}
|
}
|
||||||
Address highest = highestLock();
|
|
||||||
Address sp = lastSPDbg();
|
Address sp = lastSPDbg();
|
||||||
|
Address stackBase = getStackBase();
|
||||||
// Be robust
|
// Be robust
|
||||||
if ((highest == null) || (sp == null)) return false;
|
if (sp == null) return false;
|
||||||
return (highest.greaterThanOrEqual(a) && sp.lessThanOrEqual(a));
|
return stackBase.greaterThanOrEqual(a) && sp.lessThanOrEqual(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLockOwned(Address a) {
|
||||||
|
Address stackBase = getStackBase();
|
||||||
|
Address stackLimit = stackBase.addOffsetTo(-getStackSize());
|
||||||
|
|
||||||
|
return stackBase.greaterThanOrEqual(a) && stackLimit.lessThanOrEqual(a);
|
||||||
|
|
||||||
// FIXME: should traverse MonitorArray/MonitorChunks as in VM
|
// FIXME: should traverse MonitorArray/MonitorChunks as in VM
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ public class Thread extends VMObject {
|
|||||||
private static int HAS_ASYNC_EXCEPTION;
|
private static int HAS_ASYNC_EXCEPTION;
|
||||||
|
|
||||||
private static AddressField activeHandlesField;
|
private static AddressField activeHandlesField;
|
||||||
private static AddressField highestLockField;
|
|
||||||
private static AddressField currentPendingMonitorField;
|
private static AddressField currentPendingMonitorField;
|
||||||
private static AddressField currentWaitingMonitorField;
|
private static AddressField currentWaitingMonitorField;
|
||||||
|
|
||||||
@ -60,7 +59,6 @@ public class Thread extends VMObject {
|
|||||||
|
|
||||||
tlabFieldOffset = type.getField("_tlab").getOffset();
|
tlabFieldOffset = type.getField("_tlab").getOffset();
|
||||||
activeHandlesField = type.getAddressField("_active_handles");
|
activeHandlesField = type.getAddressField("_active_handles");
|
||||||
highestLockField = type.getAddressField("_highest_lock");
|
|
||||||
currentPendingMonitorField = type.getAddressField("_current_pending_monitor");
|
currentPendingMonitorField = type.getAddressField("_current_pending_monitor");
|
||||||
currentWaitingMonitorField = type.getAddressField("_current_waiting_monitor");
|
currentWaitingMonitorField = type.getAddressField("_current_waiting_monitor");
|
||||||
}
|
}
|
||||||
@ -121,10 +119,6 @@ public class Thread extends VMObject {
|
|||||||
// pending exception
|
// pending exception
|
||||||
}
|
}
|
||||||
|
|
||||||
public Address highestLock() {
|
|
||||||
return highestLockField.getValue(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectMonitor getCurrentPendingMonitor() {
|
public ObjectMonitor getCurrentPendingMonitor() {
|
||||||
Address monitorAddr = currentPendingMonitorField.getValue(addr);
|
Address monitorAddr = currentPendingMonitorField.getValue(addr);
|
||||||
if (monitorAddr == null) {
|
if (monitorAddr == null) {
|
||||||
|
@ -164,20 +164,11 @@ public class Threads {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long leastDiff = 0;
|
|
||||||
boolean leastDiffInitialized = false;
|
|
||||||
JavaThread theOwner = null;
|
|
||||||
for (JavaThread thread = first(); thread != null; thread = thread.next()) {
|
for (JavaThread thread = first(); thread != null; thread = thread.next()) {
|
||||||
Address addr = thread.highestLock();
|
if (thread.isLockOwned(o))
|
||||||
if (addr == null || addr.lessThan(o)) continue;
|
return thread;
|
||||||
long diff = addr.minus(o);
|
|
||||||
if (!leastDiffInitialized || diff < leastDiff) {
|
|
||||||
leastDiffInitialized = true;
|
|
||||||
leastDiff = diff;
|
|
||||||
theOwner = thread;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return theOwner;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) {
|
public JavaThread owningThreadFromMonitor(ObjectMonitor monitor) {
|
||||||
|
@ -37,11 +37,6 @@ JavaCallWrapper::JavaCallWrapper(methodHandle callee_method, Handle receiver, Ja
|
|||||||
guarantee(!thread->is_Compiler_thread(), "cannot make java calls from the compiler");
|
guarantee(!thread->is_Compiler_thread(), "cannot make java calls from the compiler");
|
||||||
_result = result;
|
_result = result;
|
||||||
|
|
||||||
// Make sure that that the value of the higest_lock is at least the same as the current stackpointer,
|
|
||||||
// since, the Java code is highly likely to use locks.
|
|
||||||
// Use '(address)this' to guarantee that highest_lock address is conservative and inside our thread
|
|
||||||
thread->update_highest_lock((address)this);
|
|
||||||
|
|
||||||
// Allocate handle block for Java code. This must be done before we change thread_state to _thread_in_Java_or_stub,
|
// Allocate handle block for Java code. This must be done before we change thread_state to _thread_in_Java_or_stub,
|
||||||
// since it can potentially block.
|
// since it can potentially block.
|
||||||
JNIHandleBlock* new_handles = JNIHandleBlock::allocate_block(thread);
|
JNIHandleBlock* new_handles = JNIHandleBlock::allocate_block(thread);
|
||||||
|
@ -1117,10 +1117,10 @@ ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {
|
|||||||
|
|
||||||
// Optimization: if the mark->locker stack address is associated
|
// Optimization: if the mark->locker stack address is associated
|
||||||
// with this thread we could simply set m->_owner = Self and
|
// with this thread we could simply set m->_owner = Self and
|
||||||
// m->OwnerIsThread = 1. Note that a thread can inflate an object
|
// m->OwnerIsThread = 1. Note that a thread can inflate an object
|
||||||
// that it has stack-locked -- as might happen in wait() -- directly
|
// that it has stack-locked -- as might happen in wait() -- directly
|
||||||
// with CAS. That is, we can avoid the xchg-NULL .... ST idiom.
|
// with CAS. That is, we can avoid the xchg-NULL .... ST idiom.
|
||||||
m->set_owner (mark->locker());
|
m->set_owner(mark->locker());
|
||||||
m->set_object(object);
|
m->set_object(object);
|
||||||
// TODO-FIXME: assert BasicLock->dhw != 0.
|
// TODO-FIXME: assert BasicLock->dhw != 0.
|
||||||
|
|
||||||
@ -1214,10 +1214,9 @@ void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_re
|
|||||||
BiasedLocking::revoke_at_safepoint(obj);
|
BiasedLocking::revoke_at_safepoint(obj);
|
||||||
}
|
}
|
||||||
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
|
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
|
||||||
}
|
}
|
||||||
|
|
||||||
THREAD->update_highest_lock((address)lock);
|
slow_enter (obj, lock, THREAD) ;
|
||||||
slow_enter (obj, lock, THREAD) ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
|
void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
|
||||||
|
@ -128,7 +128,6 @@ Thread::Thread() {
|
|||||||
debug_only(_allow_allocation_count = 0;)
|
debug_only(_allow_allocation_count = 0;)
|
||||||
NOT_PRODUCT(_allow_safepoint_count = 0;)
|
NOT_PRODUCT(_allow_safepoint_count = 0;)
|
||||||
CHECK_UNHANDLED_OOPS_ONLY(_gc_locked_out_count = 0;)
|
CHECK_UNHANDLED_OOPS_ONLY(_gc_locked_out_count = 0;)
|
||||||
_highest_lock = NULL;
|
|
||||||
_jvmti_env_iteration_count = 0;
|
_jvmti_env_iteration_count = 0;
|
||||||
_vm_operation_started_count = 0;
|
_vm_operation_started_count = 0;
|
||||||
_vm_operation_completed_count = 0;
|
_vm_operation_completed_count = 0;
|
||||||
@ -790,19 +789,6 @@ void Thread::check_for_valid_safepoint_state(bool potential_vm_operation) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool Thread::lock_is_in_stack(address adr) const {
|
|
||||||
assert(Thread::current() == this, "lock_is_in_stack can only be called from current thread");
|
|
||||||
// High limit: highest_lock is set during thread execution
|
|
||||||
// Low limit: address of the local variable dummy, rounded to 4K boundary.
|
|
||||||
// (The rounding helps finding threads in unsafe mode, even if the particular stack
|
|
||||||
// frame has been popped already. Correct as long as stacks are at least 4K long and aligned.)
|
|
||||||
address end = os::current_stack_pointer();
|
|
||||||
if (_highest_lock >= adr && adr >= end) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Thread::is_in_stack(address adr) const {
|
bool Thread::is_in_stack(address adr) const {
|
||||||
assert(Thread::current() == this, "is_in_stack can only be called from current thread");
|
assert(Thread::current() == this, "is_in_stack can only be called from current thread");
|
||||||
address end = os::current_stack_pointer();
|
address end = os::current_stack_pointer();
|
||||||
@ -818,8 +804,7 @@ bool Thread::is_in_stack(address adr) const {
|
|||||||
// should be revisited, and they should be removed if possible.
|
// should be revisited, and they should be removed if possible.
|
||||||
|
|
||||||
bool Thread::is_lock_owned(address adr) const {
|
bool Thread::is_lock_owned(address adr) const {
|
||||||
if (lock_is_in_stack(adr) ) return true;
|
return (_stack_base >= adr && adr >= (_stack_base - _stack_size));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::set_as_starting_thread() {
|
bool Thread::set_as_starting_thread() {
|
||||||
@ -1664,7 +1649,7 @@ JavaThread* JavaThread::active() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool JavaThread::is_lock_owned(address adr) const {
|
bool JavaThread::is_lock_owned(address adr) const {
|
||||||
if (lock_is_in_stack(adr)) return true;
|
if (Thread::is_lock_owned(adr)) return true;
|
||||||
|
|
||||||
for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) {
|
for (MonitorChunk* chunk = monitor_chunks(); chunk != NULL; chunk = chunk->next()) {
|
||||||
if (chunk->contains(adr)) return true;
|
if (chunk->contains(adr)) return true;
|
||||||
@ -2443,7 +2428,7 @@ void JavaThread::print_on(outputStream *st) const {
|
|||||||
if (thread_oop != NULL && java_lang_Thread::is_daemon(thread_oop)) st->print("daemon ");
|
if (thread_oop != NULL && java_lang_Thread::is_daemon(thread_oop)) st->print("daemon ");
|
||||||
Thread::print_on(st);
|
Thread::print_on(st);
|
||||||
// print guess for valid stack memory region (assume 4K pages); helps lock debugging
|
// print guess for valid stack memory region (assume 4K pages); helps lock debugging
|
||||||
st->print_cr("[" INTPTR_FORMAT ".." INTPTR_FORMAT "]", (intptr_t)last_Java_sp() & ~right_n_bits(12), highest_lock());
|
st->print_cr("[" INTPTR_FORMAT "]", (intptr_t)last_Java_sp() & ~right_n_bits(12));
|
||||||
if (thread_oop != NULL && JDK_Version::is_gte_jdk15x_version()) {
|
if (thread_oop != NULL && JDK_Version::is_gte_jdk15x_version()) {
|
||||||
st->print_cr(" java.lang.Thread.State: %s", java_lang_Thread::thread_status_name(thread_oop));
|
st->print_cr(" java.lang.Thread.State: %s", java_lang_Thread::thread_status_name(thread_oop));
|
||||||
}
|
}
|
||||||
@ -3733,25 +3718,13 @@ JavaThread *Threads::owning_thread_from_monitor_owner(address owner, bool doLock
|
|||||||
// heavyweight monitors, then the owner is the stack address of the
|
// heavyweight monitors, then the owner is the stack address of the
|
||||||
// Lock Word in the owning Java thread's stack.
|
// Lock Word in the owning Java thread's stack.
|
||||||
//
|
//
|
||||||
// We can't use Thread::is_lock_owned() or Thread::lock_is_in_stack() because
|
|
||||||
// those routines rely on the "current" stack pointer. That would be our
|
|
||||||
// stack pointer which is not relevant to the question. Instead we use the
|
|
||||||
// highest lock ever entered by the thread and find the thread that is
|
|
||||||
// higher than and closest to our target stack address.
|
|
||||||
//
|
|
||||||
address least_diff = 0;
|
|
||||||
bool least_diff_initialized = false;
|
|
||||||
JavaThread* the_owner = NULL;
|
JavaThread* the_owner = NULL;
|
||||||
{
|
{
|
||||||
MutexLockerEx ml(doLock ? Threads_lock : NULL);
|
MutexLockerEx ml(doLock ? Threads_lock : NULL);
|
||||||
ALL_JAVA_THREADS(q) {
|
ALL_JAVA_THREADS(q) {
|
||||||
address addr = q->highest_lock();
|
if (q->is_lock_owned(owner)) {
|
||||||
if (addr == NULL || addr < owner) continue; // thread has entered no monitors or is too low
|
|
||||||
address diff = (address)(addr - owner);
|
|
||||||
if (!least_diff_initialized || diff < least_diff) {
|
|
||||||
least_diff_initialized = true;
|
|
||||||
least_diff = diff;
|
|
||||||
the_owner = q;
|
the_owner = q;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,14 +200,6 @@ class Thread: public ThreadShadow {
|
|||||||
friend class ThreadLocalStorage;
|
friend class ThreadLocalStorage;
|
||||||
friend class GC_locker;
|
friend class GC_locker;
|
||||||
|
|
||||||
// In order for all threads to be able to use fast locking, we need to know the highest stack
|
|
||||||
// address of where a lock is on the stack (stacks normally grow towards lower addresses). This
|
|
||||||
// variable is initially set to NULL, indicating no locks are used by the thread. During the thread's
|
|
||||||
// execution, it will be set whenever locking can happen, i.e., when we call out to Java code or use
|
|
||||||
// an ObjectLocker. The value is never decreased, hence, it will over the lifetime of a thread
|
|
||||||
// approximate the real stackbase.
|
|
||||||
address _highest_lock; // Highest stack address where a JavaLock exist
|
|
||||||
|
|
||||||
ThreadLocalAllocBuffer _tlab; // Thread-local eden
|
ThreadLocalAllocBuffer _tlab; // Thread-local eden
|
||||||
|
|
||||||
int _vm_operation_started_count; // VM_Operation support
|
int _vm_operation_started_count; // VM_Operation support
|
||||||
@ -400,18 +392,14 @@ public:
|
|||||||
// Sweeper support
|
// Sweeper support
|
||||||
void nmethods_do();
|
void nmethods_do();
|
||||||
|
|
||||||
// Fast-locking support
|
|
||||||
address highest_lock() const { return _highest_lock; }
|
|
||||||
void update_highest_lock(address base) { if (base > _highest_lock) _highest_lock = base; }
|
|
||||||
|
|
||||||
// Tells if adr belong to this thread. This is used
|
// Tells if adr belong to this thread. This is used
|
||||||
// for checking if a lock is owned by the running thread.
|
// for checking if a lock is owned by the running thread.
|
||||||
// Warning: the method can only be used on the running thread
|
|
||||||
// Fast lock support uses these methods
|
// Used by fast lock support
|
||||||
virtual bool lock_is_in_stack(address adr) const;
|
|
||||||
virtual bool is_lock_owned(address adr) const;
|
virtual bool is_lock_owned(address adr) const;
|
||||||
|
|
||||||
// Check if address is in the stack of the thread (not just for locks).
|
// Check if address is in the stack of the thread (not just for locks).
|
||||||
|
// Warning: the method can only be used on the running thread
|
||||||
bool is_in_stack(address adr) const;
|
bool is_in_stack(address adr) const;
|
||||||
|
|
||||||
// Sets this thread as starting thread. Returns failure if thread
|
// Sets this thread as starting thread. Returns failure if thread
|
||||||
|
@ -656,7 +656,6 @@ static inline uint64_t cast_uint64_t(size_t x)
|
|||||||
\
|
\
|
||||||
volatile_nonstatic_field(Thread, _suspend_flags, uint32_t) \
|
volatile_nonstatic_field(Thread, _suspend_flags, uint32_t) \
|
||||||
nonstatic_field(Thread, _active_handles, JNIHandleBlock*) \
|
nonstatic_field(Thread, _active_handles, JNIHandleBlock*) \
|
||||||
nonstatic_field(Thread, _highest_lock, address) \
|
|
||||||
nonstatic_field(Thread, _tlab, ThreadLocalAllocBuffer) \
|
nonstatic_field(Thread, _tlab, ThreadLocalAllocBuffer) \
|
||||||
nonstatic_field(Thread, _current_pending_monitor, ObjectMonitor*) \
|
nonstatic_field(Thread, _current_pending_monitor, ObjectMonitor*) \
|
||||||
nonstatic_field(Thread, _current_pending_monitor_is_from_java, bool) \
|
nonstatic_field(Thread, _current_pending_monitor_is_from_java, bool) \
|
||||||
|
Loading…
Reference in New Issue
Block a user