mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8230876: baseline cleanups from Async Monitor Deflation v2.0[789]
Reviewed-by: dholmes, kvn
This commit is contained in:
parent
13ce4cdf2a
commit
b10495d436
9 changed files with 113 additions and 53 deletions
|
@ -1532,7 +1532,7 @@ void MacroAssembler::rtm_inflated_locking(Register objReg, Register boxReg, Regi
|
||||||
Label L_rtm_retry, L_decrement_retry, L_on_abort;
|
Label L_rtm_retry, L_decrement_retry, L_on_abort;
|
||||||
int owner_offset = OM_OFFSET_NO_MONITOR_VALUE_TAG(owner);
|
int owner_offset = OM_OFFSET_NO_MONITOR_VALUE_TAG(owner);
|
||||||
|
|
||||||
// Without cast to int32_t a movptr will destroy r10 which is typically obj
|
// Without cast to int32_t this style of movptr will destroy r10 which is typically obj.
|
||||||
movptr(Address(boxReg, 0), (int32_t)intptr_t(markWord::unused_mark().value()));
|
movptr(Address(boxReg, 0), (int32_t)intptr_t(markWord::unused_mark().value()));
|
||||||
movptr(boxReg, tmpReg); // Save ObjectMonitor address
|
movptr(boxReg, tmpReg); // Save ObjectMonitor address
|
||||||
|
|
||||||
|
@ -1602,11 +1602,11 @@ void MacroAssembler::rtm_inflated_locking(Register objReg, Register boxReg, Regi
|
||||||
|
|
||||||
#endif // INCLUDE_RTM_OPT
|
#endif // INCLUDE_RTM_OPT
|
||||||
|
|
||||||
// Fast_Lock and Fast_Unlock used by C2
|
// fast_lock and fast_unlock used by C2
|
||||||
|
|
||||||
// Because the transitions from emitted code to the runtime
|
// Because the transitions from emitted code to the runtime
|
||||||
// monitorenter/exit helper stubs are so slow it's critical that
|
// monitorenter/exit helper stubs are so slow it's critical that
|
||||||
// we inline both the stack-locking fast-path and the inflated fast path.
|
// we inline both the stack-locking fast path and the inflated fast path.
|
||||||
//
|
//
|
||||||
// See also: cmpFastLock and cmpFastUnlock.
|
// See also: cmpFastLock and cmpFastUnlock.
|
||||||
//
|
//
|
||||||
|
@ -1615,7 +1615,7 @@ void MacroAssembler::rtm_inflated_locking(Register objReg, Register boxReg, Regi
|
||||||
// option would be to emit TrySlowEnter and TrySlowExit methods
|
// option would be to emit TrySlowEnter and TrySlowExit methods
|
||||||
// at startup-time. These methods would accept arguments as
|
// at startup-time. These methods would accept arguments as
|
||||||
// (rax,=Obj, rbx=Self, rcx=box, rdx=Scratch) and return success-failure
|
// (rax,=Obj, rbx=Self, rcx=box, rdx=Scratch) and return success-failure
|
||||||
// indications in the icc.ZFlag. Fast_Lock and Fast_Unlock would simply
|
// indications in the icc.ZFlag. fast_lock and fast_unlock would simply
|
||||||
// marshal the arguments and emit calls to TrySlowEnter and TrySlowExit.
|
// marshal the arguments and emit calls to TrySlowEnter and TrySlowExit.
|
||||||
// In practice, however, the # of lock sites is bounded and is usually small.
|
// In practice, however, the # of lock sites is bounded and is usually small.
|
||||||
// Besides the call overhead, TrySlowEnter and TrySlowExit might suffer
|
// Besides the call overhead, TrySlowEnter and TrySlowExit might suffer
|
||||||
|
@ -1634,8 +1634,8 @@ void MacroAssembler::rtm_inflated_locking(Register objReg, Register boxReg, Regi
|
||||||
//
|
//
|
||||||
// TODO:
|
// TODO:
|
||||||
//
|
//
|
||||||
// * Arrange for C2 to pass "Self" into Fast_Lock and Fast_Unlock in one of the registers (scr).
|
// * Arrange for C2 to pass "Self" into fast_lock and fast_unlock in one of the registers (scr).
|
||||||
// This avoids manifesting the Self pointer in the Fast_Lock and Fast_Unlock terminals.
|
// This avoids manifesting the Self pointer in the fast_lock and fast_unlock terminals.
|
||||||
// Given TLAB allocation, Self is usually manifested in a register, so passing it into
|
// Given TLAB allocation, Self is usually manifested in a register, so passing it into
|
||||||
// the lock operators would typically be faster than reifying Self.
|
// the lock operators would typically be faster than reifying Self.
|
||||||
//
|
//
|
||||||
|
@ -1661,14 +1661,14 @@ void MacroAssembler::rtm_inflated_locking(Register objReg, Register boxReg, Regi
|
||||||
// * use jccb and jmpb instead of jcc and jmp to improve code density.
|
// * use jccb and jmpb instead of jcc and jmp to improve code density.
|
||||||
// But beware of excessive branch density on AMD Opterons.
|
// But beware of excessive branch density on AMD Opterons.
|
||||||
//
|
//
|
||||||
// * Both Fast_Lock and Fast_Unlock set the ICC.ZF to indicate success
|
// * Both fast_lock and fast_unlock set the ICC.ZF to indicate success
|
||||||
// or failure of the fast-path. If the fast-path fails then we pass
|
// or failure of the fast path. If the fast path fails then we pass
|
||||||
// control to the slow-path, typically in C. In Fast_Lock and
|
// control to the slow path, typically in C. In fast_lock and
|
||||||
// Fast_Unlock we often branch to DONE_LABEL, just to find that C2
|
// fast_unlock we often branch to DONE_LABEL, just to find that C2
|
||||||
// will emit a conditional branch immediately after the node.
|
// will emit a conditional branch immediately after the node.
|
||||||
// So we have branches to branches and lots of ICC.ZF games.
|
// So we have branches to branches and lots of ICC.ZF games.
|
||||||
// Instead, it might be better to have C2 pass a "FailureLabel"
|
// Instead, it might be better to have C2 pass a "FailureLabel"
|
||||||
// into Fast_Lock and Fast_Unlock. In the case of success, control
|
// into fast_lock and fast_unlock. In the case of success, control
|
||||||
// will drop through the node. ICC.ZF is undefined at exit.
|
// will drop through the node. ICC.ZF is undefined at exit.
|
||||||
// In the case of failure, the node will branch directly to the
|
// In the case of failure, the node will branch directly to the
|
||||||
// FailureLabel
|
// FailureLabel
|
||||||
|
@ -1813,7 +1813,7 @@ void MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg
|
||||||
movptr(Address(boxReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), scrReg);
|
movptr(Address(boxReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), scrReg);
|
||||||
xorptr(boxReg, boxReg); // set icc.ZFlag = 1 to indicate success
|
xorptr(boxReg, boxReg); // set icc.ZFlag = 1 to indicate success
|
||||||
|
|
||||||
// If the CAS fails we can either retry or pass control to the slow-path.
|
// If the CAS fails we can either retry or pass control to the slow path.
|
||||||
// We use the latter tactic.
|
// We use the latter tactic.
|
||||||
// Pass the CAS result in the icc.ZFlag into DONE_LABEL
|
// Pass the CAS result in the icc.ZFlag into DONE_LABEL
|
||||||
// If the CAS was successful ...
|
// If the CAS was successful ...
|
||||||
|
@ -1821,14 +1821,13 @@ void MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg
|
||||||
// Invariant: m->_recursions should already be 0, so we don't need to explicitly set it.
|
// Invariant: m->_recursions should already be 0, so we don't need to explicitly set it.
|
||||||
// Intentional fall-through into DONE_LABEL ...
|
// Intentional fall-through into DONE_LABEL ...
|
||||||
#else // _LP64
|
#else // _LP64
|
||||||
// It's inflated
|
// It's inflated and we use scrReg for ObjectMonitor* in this section.
|
||||||
movq(scrReg, tmpReg);
|
movq(scrReg, tmpReg);
|
||||||
xorq(tmpReg, tmpReg);
|
xorq(tmpReg, tmpReg);
|
||||||
|
|
||||||
lock();
|
lock();
|
||||||
cmpxchgptr(r15_thread, Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)));
|
cmpxchgptr(r15_thread, Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)));
|
||||||
// Unconditionally set box->_displaced_header = markWord::unused_mark().
|
// Unconditionally set box->_displaced_header = markWord::unused_mark().
|
||||||
// Without cast to int32_t movptr will destroy r10 which is typically obj.
|
// Without cast to int32_t this style of movptr will destroy r10 which is typically obj.
|
||||||
movptr(Address(boxReg, 0), (int32_t)intptr_t(markWord::unused_mark().value()));
|
movptr(Address(boxReg, 0), (int32_t)intptr_t(markWord::unused_mark().value()));
|
||||||
// Intentional fall-through into DONE_LABEL ...
|
// Intentional fall-through into DONE_LABEL ...
|
||||||
// Propagate ICC.ZF from CAS above into DONE_LABEL.
|
// Propagate ICC.ZF from CAS above into DONE_LABEL.
|
||||||
|
@ -1844,9 +1843,9 @@ void MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg
|
||||||
bind(DONE_LABEL);
|
bind(DONE_LABEL);
|
||||||
|
|
||||||
// At DONE_LABEL the icc ZFlag is set as follows ...
|
// At DONE_LABEL the icc ZFlag is set as follows ...
|
||||||
// Fast_Unlock uses the same protocol.
|
// fast_unlock uses the same protocol.
|
||||||
// ZFlag == 1 -> Success
|
// ZFlag == 1 -> Success
|
||||||
// ZFlag == 0 -> Failure - force control through the slow-path
|
// ZFlag == 0 -> Failure - force control through the slow path
|
||||||
}
|
}
|
||||||
|
|
||||||
// obj: object to unlock
|
// obj: object to unlock
|
||||||
|
@ -1855,7 +1854,7 @@ void MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg
|
||||||
//
|
//
|
||||||
// Some commentary on balanced locking:
|
// Some commentary on balanced locking:
|
||||||
//
|
//
|
||||||
// Fast_Lock and Fast_Unlock are emitted only for provably balanced lock sites.
|
// fast_lock and fast_unlock are emitted only for provably balanced lock sites.
|
||||||
// Methods that don't have provably balanced locking are forced to run in the
|
// Methods that don't have provably balanced locking are forced to run in the
|
||||||
// interpreter - such methods won't be compiled to use fast_lock and fast_unlock.
|
// interpreter - such methods won't be compiled to use fast_lock and fast_unlock.
|
||||||
// The interpreter provides two properties:
|
// The interpreter provides two properties:
|
||||||
|
@ -1876,7 +1875,7 @@ void MacroAssembler::fast_lock(Register objReg, Register boxReg, Register tmpReg
|
||||||
// should not be unlocked by "normal" java-level locking and vice-versa. The specification
|
// should not be unlocked by "normal" java-level locking and vice-versa. The specification
|
||||||
// doesn't specify what will occur if a program engages in such mixed-mode locking, however.
|
// doesn't specify what will occur if a program engages in such mixed-mode locking, however.
|
||||||
// Arguably given that the spec legislates the JNI case as undefined our implementation
|
// Arguably given that the spec legislates the JNI case as undefined our implementation
|
||||||
// could reasonably *avoid* checking owner in Fast_Unlock().
|
// could reasonably *avoid* checking owner in fast_unlock().
|
||||||
// In the interest of performance we elide m->Owner==Self check in unlock.
|
// In the interest of performance we elide m->Owner==Self check in unlock.
|
||||||
// A perfectly viable alternative is to elide the owner check except when
|
// A perfectly viable alternative is to elide the owner check except when
|
||||||
// Xcheck:jni is enabled.
|
// Xcheck:jni is enabled.
|
||||||
|
@ -1941,7 +1940,7 @@ void MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpR
|
||||||
// a costly MEMBAR or CAS. See synchronizer.cpp for details on how
|
// a costly MEMBAR or CAS. See synchronizer.cpp for details on how
|
||||||
// we detect and recover from the race that the 1-0 exit admits.
|
// we detect and recover from the race that the 1-0 exit admits.
|
||||||
//
|
//
|
||||||
// Conceptually Fast_Unlock() must execute a STST|LDST "release" barrier
|
// Conceptually fast_unlock() must execute a STST|LDST "release" barrier
|
||||||
// before it STs null into _owner, releasing the lock. Updates
|
// before it STs null into _owner, releasing the lock. Updates
|
||||||
// to data protected by the critical section must be visible before
|
// to data protected by the critical section must be visible before
|
||||||
// we drop the lock (and thus before any other thread could acquire
|
// we drop the lock (and thus before any other thread could acquire
|
||||||
|
@ -1990,6 +1989,7 @@ void MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpR
|
||||||
movptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
|
movptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
|
||||||
orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
|
orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
|
||||||
jccb (Assembler::notZero, CheckSucc);
|
jccb (Assembler::notZero, CheckSucc);
|
||||||
|
// Without cast to int32_t this style of movptr will destroy r10 which is typically obj.
|
||||||
movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), (int32_t)NULL_WORD);
|
movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), (int32_t)NULL_WORD);
|
||||||
jmpb (DONE_LABEL);
|
jmpb (DONE_LABEL);
|
||||||
|
|
||||||
|
@ -1998,13 +1998,14 @@ void MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpR
|
||||||
bind (CheckSucc);
|
bind (CheckSucc);
|
||||||
|
|
||||||
// The following optional optimization can be elided if necessary
|
// The following optional optimization can be elided if necessary
|
||||||
// Effectively: if (succ == null) goto SlowPath
|
// Effectively: if (succ == null) goto slow path
|
||||||
// The code reduces the window for a race, however,
|
// The code reduces the window for a race, however,
|
||||||
// and thus benefits performance.
|
// and thus benefits performance.
|
||||||
cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), (int32_t)NULL_WORD);
|
cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), (int32_t)NULL_WORD);
|
||||||
jccb (Assembler::zero, LGoSlowPath);
|
jccb (Assembler::zero, LGoSlowPath);
|
||||||
|
|
||||||
xorptr(boxReg, boxReg);
|
xorptr(boxReg, boxReg);
|
||||||
|
// Without cast to int32_t this style of movptr will destroy r10 which is typically obj.
|
||||||
movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), (int32_t)NULL_WORD);
|
movptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), (int32_t)NULL_WORD);
|
||||||
|
|
||||||
// Memory barrier/fence
|
// Memory barrier/fence
|
||||||
|
@ -2039,7 +2040,7 @@ void MacroAssembler::fast_unlock(Register objReg, Register boxReg, Register tmpR
|
||||||
// If that didn't work, then another thread grabbed the
|
// If that didn't work, then another thread grabbed the
|
||||||
// lock so we're done (and exit was a success).
|
// lock so we're done (and exit was a success).
|
||||||
jccb (Assembler::notEqual, LSuccess);
|
jccb (Assembler::notEqual, LSuccess);
|
||||||
// Intentional fall-through into slow-path
|
// Intentional fall-through into slow path
|
||||||
|
|
||||||
bind (LGoSlowPath);
|
bind (LGoSlowPath);
|
||||||
orl (boxReg, 1); // set ICC.ZF=0 to indicate failure
|
orl (boxReg, 1); // set ICC.ZF=0 to indicate failure
|
||||||
|
|
|
@ -540,7 +540,7 @@ void SystemDictionary::double_lock_wait(Handle lockObject, TRAPS) {
|
||||||
assert(calledholdinglock,"must hold lock for notify");
|
assert(calledholdinglock,"must hold lock for notify");
|
||||||
assert((lockObject() != _system_loader_lock_obj && !is_parallelCapable(lockObject)), "unexpected double_lock_wait");
|
assert((lockObject() != _system_loader_lock_obj && !is_parallelCapable(lockObject)), "unexpected double_lock_wait");
|
||||||
ObjectSynchronizer::notifyall(lockObject, THREAD);
|
ObjectSynchronizer::notifyall(lockObject, THREAD);
|
||||||
intptr_t recursions = ObjectSynchronizer::complete_exit(lockObject, THREAD);
|
intx recursions = ObjectSynchronizer::complete_exit(lockObject, THREAD);
|
||||||
SystemDictionary_lock->wait();
|
SystemDictionary_lock->wait();
|
||||||
SystemDictionary_lock->unlock();
|
SystemDictionary_lock->unlock();
|
||||||
ObjectSynchronizer::reenter(lockObject, recursions, THREAD);
|
ObjectSynchronizer::reenter(lockObject, recursions, THREAD);
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
#include "classfile/vmSymbols.hpp"
|
#include "classfile/vmSymbols.hpp"
|
||||||
#include "jfr/jfrEvents.hpp"
|
#include "jfr/jfrEvents.hpp"
|
||||||
#include "jfr/support/jfrThreadId.hpp"
|
#include "jfr/support/jfrThreadId.hpp"
|
||||||
|
#include "logging/log.hpp"
|
||||||
|
#include "logging/logStream.hpp"
|
||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
#include "oops/markWord.hpp"
|
#include "oops/markWord.hpp"
|
||||||
|
@ -255,7 +257,7 @@ void ObjectMonitor::enter(TRAPS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Self->is_lock_owned ((address)cur)) {
|
if (Self->is_lock_owned((address)cur)) {
|
||||||
assert(_recursions == 0, "internal state error");
|
assert(_recursions == 0, "internal state error");
|
||||||
_recursions = 1;
|
_recursions = 1;
|
||||||
// Commute owner from a thread-specific on-stack BasicLockObject address to
|
// Commute owner from a thread-specific on-stack BasicLockObject address to
|
||||||
|
@ -275,8 +277,7 @@ void ObjectMonitor::enter(TRAPS) {
|
||||||
// we forgo posting JVMTI events and firing DTRACE probes.
|
// we forgo posting JVMTI events and firing DTRACE probes.
|
||||||
if (TrySpin(Self) > 0) {
|
if (TrySpin(Self) > 0) {
|
||||||
assert(_owner == Self, "must be Self: owner=" INTPTR_FORMAT, p2i(_owner));
|
assert(_owner == Self, "must be Self: owner=" INTPTR_FORMAT, p2i(_owner));
|
||||||
assert(_recursions == 0, "must be 0: recursions=" INTPTR_FORMAT,
|
assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions);
|
||||||
_recursions);
|
|
||||||
assert(((oop)object())->mark() == markWord::encode(this),
|
assert(((oop)object())->mark() == markWord::encode(this),
|
||||||
"object mark must match encoded this: mark=" INTPTR_FORMAT
|
"object mark must match encoded this: mark=" INTPTR_FORMAT
|
||||||
", encoded this=" INTPTR_FORMAT, ((oop)object())->mark().value(),
|
", encoded this=" INTPTR_FORMAT, ((oop)object())->mark().value(),
|
||||||
|
@ -881,7 +882,14 @@ void ObjectMonitor::exit(bool not_suspended, TRAPS) {
|
||||||
// way we should encounter this situation is in the presence of
|
// way we should encounter this situation is in the presence of
|
||||||
// unbalanced JNI locking. TODO: CheckJNICalls.
|
// unbalanced JNI locking. TODO: CheckJNICalls.
|
||||||
// See also: CR4414101
|
// See also: CR4414101
|
||||||
assert(false, "Non-balanced monitor enter/exit! Likely JNI locking");
|
#ifdef ASSERT
|
||||||
|
LogStreamHandle(Error, monitorinflation) lsh;
|
||||||
|
lsh.print_cr("ERROR: ObjectMonitor::exit(): thread=" INTPTR_FORMAT
|
||||||
|
" is exiting an ObjectMonitor it does not own.", p2i(THREAD));
|
||||||
|
lsh.print_cr("The imbalance is possibly caused by JNI locking.");
|
||||||
|
print_debug_style_on(&lsh);
|
||||||
|
#endif
|
||||||
|
assert(false, "Non-balanced monitor enter/exit!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -908,8 +916,6 @@ void ObjectMonitor::exit(bool not_suspended, TRAPS) {
|
||||||
|
|
||||||
// release semantics: prior loads and stores from within the critical section
|
// release semantics: prior loads and stores from within the critical section
|
||||||
// must not float (reorder) past the following store that drops the lock.
|
// must not float (reorder) past the following store that drops the lock.
|
||||||
// On SPARC that requires MEMBAR #loadstore|#storestore.
|
|
||||||
// But of course in TSO #loadstore|#storestore is not required.
|
|
||||||
OrderAccess::release_store(&_owner, (void*)NULL); // drop the lock
|
OrderAccess::release_store(&_owner, (void*)NULL); // drop the lock
|
||||||
OrderAccess::storeload(); // See if we need to wake a successor
|
OrderAccess::storeload(); // See if we need to wake a successor
|
||||||
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
|
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
|
||||||
|
@ -1106,7 +1112,7 @@ void ObjectMonitor::ExitEpilog(Thread * Self, ObjectWaiter * Wakee) {
|
||||||
// The _owner field is not always the Thread addr even with an
|
// The _owner field is not always the Thread addr even with an
|
||||||
// inflated monitor, e.g. the monitor can be inflated by a non-owning
|
// inflated monitor, e.g. the monitor can be inflated by a non-owning
|
||||||
// thread due to contention.
|
// thread due to contention.
|
||||||
intptr_t ObjectMonitor::complete_exit(TRAPS) {
|
intx ObjectMonitor::complete_exit(TRAPS) {
|
||||||
Thread * const Self = THREAD;
|
Thread * const Self = THREAD;
|
||||||
assert(Self->is_Java_thread(), "Must be Java thread!");
|
assert(Self->is_Java_thread(), "Must be Java thread!");
|
||||||
JavaThread *jt = (JavaThread *)THREAD;
|
JavaThread *jt = (JavaThread *)THREAD;
|
||||||
|
@ -1122,7 +1128,7 @@ intptr_t ObjectMonitor::complete_exit(TRAPS) {
|
||||||
}
|
}
|
||||||
|
|
||||||
guarantee(Self == _owner, "complete_exit not owner");
|
guarantee(Self == _owner, "complete_exit not owner");
|
||||||
intptr_t save = _recursions; // record the old recursion count
|
intx save = _recursions; // record the old recursion count
|
||||||
_recursions = 0; // set the recursion level to be 0
|
_recursions = 0; // set the recursion level to be 0
|
||||||
exit(true, Self); // exit the monitor
|
exit(true, Self); // exit the monitor
|
||||||
guarantee(_owner != Self, "invariant");
|
guarantee(_owner != Self, "invariant");
|
||||||
|
@ -1131,7 +1137,7 @@ intptr_t ObjectMonitor::complete_exit(TRAPS) {
|
||||||
|
|
||||||
// reenter() enters a lock and sets recursion count
|
// reenter() enters a lock and sets recursion count
|
||||||
// complete_exit/reenter operate as a wait without waiting
|
// complete_exit/reenter operate as a wait without waiting
|
||||||
void ObjectMonitor::reenter(intptr_t recursions, TRAPS) {
|
void ObjectMonitor::reenter(intx recursions, TRAPS) {
|
||||||
Thread * const Self = THREAD;
|
Thread * const Self = THREAD;
|
||||||
assert(Self->is_Java_thread(), "Must be Java thread!");
|
assert(Self->is_Java_thread(), "Must be Java thread!");
|
||||||
JavaThread *jt = (JavaThread *)THREAD;
|
JavaThread *jt = (JavaThread *)THREAD;
|
||||||
|
@ -1252,7 +1258,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
||||||
|
|
||||||
_Responsible = NULL;
|
_Responsible = NULL;
|
||||||
|
|
||||||
intptr_t save = _recursions; // record the old recursion count
|
intx save = _recursions; // record the old recursion count
|
||||||
_waiters++; // increment the number of waiters
|
_waiters++; // increment the number of waiters
|
||||||
_recursions = 0; // set the recursion level to be 1
|
_recursions = 0; // set the recursion level to be 1
|
||||||
exit(true, Self); // exit the monitor
|
exit(true, Self); // exit the monitor
|
||||||
|
@ -1941,8 +1947,62 @@ void ObjectMonitor::Initialize() {
|
||||||
void ObjectMonitor::print_on(outputStream* st) const {
|
void ObjectMonitor::print_on(outputStream* st) const {
|
||||||
// The minimal things to print for markWord printing, more can be added for debugging and logging.
|
// The minimal things to print for markWord printing, more can be added for debugging and logging.
|
||||||
st->print("{contentions=0x%08x,waiters=0x%08x"
|
st->print("{contentions=0x%08x,waiters=0x%08x"
|
||||||
",recursions=" INTPTR_FORMAT ",owner=" INTPTR_FORMAT "}",
|
",recursions=" INTX_FORMAT ",owner=" INTPTR_FORMAT "}",
|
||||||
contentions(), waiters(), recursions(),
|
contentions(), waiters(), recursions(),
|
||||||
p2i(owner()));
|
p2i(owner()));
|
||||||
}
|
}
|
||||||
void ObjectMonitor::print() const { print_on(tty); }
|
void ObjectMonitor::print() const { print_on(tty); }
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
// Print the ObjectMonitor like a debugger would:
|
||||||
|
//
|
||||||
|
// (ObjectMonitor) 0x00007fdfb6012e40 = {
|
||||||
|
// _header = 0x0000000000000001
|
||||||
|
// _object = 0x000000070ff45fd0
|
||||||
|
// _next_om = 0x0000000000000000
|
||||||
|
// _pad_buf0 = {
|
||||||
|
// [0] = '\0'
|
||||||
|
// ...
|
||||||
|
// [103] = '\0'
|
||||||
|
// }
|
||||||
|
// _owner = 0x0000000000000000
|
||||||
|
// _previous_owner_tid = 0
|
||||||
|
// _recursions = 0
|
||||||
|
// _EntryList = 0x0000000000000000
|
||||||
|
// _cxq = 0x0000000000000000
|
||||||
|
// _succ = 0x0000000000000000
|
||||||
|
// _Responsible = 0x0000000000000000
|
||||||
|
// _Spinner = 0
|
||||||
|
// _SpinDuration = 5000
|
||||||
|
// _contentions = 0
|
||||||
|
// _WaitSet = 0x0000700009756248
|
||||||
|
// _waiters = 1
|
||||||
|
// _WaitSetLock = 0
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
void ObjectMonitor::print_debug_style_on(outputStream* st) const {
|
||||||
|
st->print_cr("(ObjectMonitor*) " INTPTR_FORMAT " = {", p2i(this));
|
||||||
|
st->print_cr(" _header = " INTPTR_FORMAT, header().value());
|
||||||
|
st->print_cr(" _object = " INTPTR_FORMAT, p2i(_object));
|
||||||
|
st->print_cr(" _next_om = " INTPTR_FORMAT, p2i(_next_om));
|
||||||
|
st->print_cr(" _pad_buf0 = {");
|
||||||
|
st->print_cr(" [0] = '\\0'");
|
||||||
|
st->print_cr(" ...");
|
||||||
|
st->print_cr(" [%d] = '\\0'", (int)sizeof(_pad_buf0) - 1);
|
||||||
|
st->print_cr(" }");
|
||||||
|
st->print_cr(" _owner = " INTPTR_FORMAT, p2i(_owner));
|
||||||
|
st->print_cr(" _previous_owner_tid = " JLONG_FORMAT, _previous_owner_tid);
|
||||||
|
st->print_cr(" _recursions = " INTX_FORMAT, _recursions);
|
||||||
|
st->print_cr(" _EntryList = " INTPTR_FORMAT, p2i(_EntryList));
|
||||||
|
st->print_cr(" _cxq = " INTPTR_FORMAT, p2i(_cxq));
|
||||||
|
st->print_cr(" _succ = " INTPTR_FORMAT, p2i(_succ));
|
||||||
|
st->print_cr(" _Responsible = " INTPTR_FORMAT, p2i(_Responsible));
|
||||||
|
st->print_cr(" _Spinner = %d", _Spinner);
|
||||||
|
st->print_cr(" _SpinDuration = %d", _SpinDuration);
|
||||||
|
st->print_cr(" _contentions = %d", _contentions);
|
||||||
|
st->print_cr(" _WaitSet = " INTPTR_FORMAT, p2i(_WaitSet));
|
||||||
|
st->print_cr(" _waiters = %d", _waiters);
|
||||||
|
st->print_cr(" _WaitSetLock = %d", _WaitSetLock);
|
||||||
|
st->print_cr("}");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -144,7 +144,7 @@ class ObjectMonitor {
|
||||||
sizeof(ObjectMonitor *));
|
sizeof(ObjectMonitor *));
|
||||||
void* volatile _owner; // pointer to owning thread OR BasicLock
|
void* volatile _owner; // pointer to owning thread OR BasicLock
|
||||||
volatile jlong _previous_owner_tid; // thread id of the previous owner of the monitor
|
volatile jlong _previous_owner_tid; // thread id of the previous owner of the monitor
|
||||||
volatile intptr_t _recursions; // recursion count, 0 for first entry
|
volatile intx _recursions; // recursion count, 0 for first entry
|
||||||
ObjectWaiter* volatile _EntryList; // Threads blocked on entry or reentry.
|
ObjectWaiter* volatile _EntryList; // Threads blocked on entry or reentry.
|
||||||
// The list is actually composed of WaitNodes,
|
// The list is actually composed of WaitNodes,
|
||||||
// acting as proxies for Threads.
|
// acting as proxies for Threads.
|
||||||
|
@ -237,7 +237,7 @@ class ObjectMonitor {
|
||||||
jint waiters() const;
|
jint waiters() const;
|
||||||
|
|
||||||
jint contentions() const;
|
jint contentions() const;
|
||||||
intptr_t recursions() const { return _recursions; }
|
intx recursions() const { return _recursions; }
|
||||||
|
|
||||||
// JVM/TI GetObjectMonitorUsage() needs this:
|
// JVM/TI GetObjectMonitorUsage() needs this:
|
||||||
ObjectWaiter* first_waiter() { return _WaitSet; }
|
ObjectWaiter* first_waiter() { return _WaitSet; }
|
||||||
|
@ -263,7 +263,7 @@ class ObjectMonitor {
|
||||||
// _recursions == 0 _WaitSet == NULL
|
// _recursions == 0 _WaitSet == NULL
|
||||||
DEBUG_ONLY(stringStream ss;)
|
DEBUG_ONLY(stringStream ss;)
|
||||||
assert((is_busy() | _recursions) == 0, "freeing in-use monitor: %s, "
|
assert((is_busy() | _recursions) == 0, "freeing in-use monitor: %s, "
|
||||||
"recursions=" INTPTR_FORMAT, is_busy_to_string(&ss), _recursions);
|
"recursions=" INTX_FORMAT, is_busy_to_string(&ss), _recursions);
|
||||||
_succ = NULL;
|
_succ = NULL;
|
||||||
_EntryList = NULL;
|
_EntryList = NULL;
|
||||||
_cxq = NULL;
|
_cxq = NULL;
|
||||||
|
@ -289,11 +289,14 @@ class ObjectMonitor {
|
||||||
void notifyAll(TRAPS);
|
void notifyAll(TRAPS);
|
||||||
|
|
||||||
void print() const;
|
void print() const;
|
||||||
|
#ifdef ASSERT
|
||||||
|
void print_debug_style_on(outputStream* st) const;
|
||||||
|
#endif
|
||||||
void print_on(outputStream* st) const;
|
void print_on(outputStream* st) const;
|
||||||
|
|
||||||
// Use the following at your own risk
|
// Use the following at your own risk
|
||||||
intptr_t complete_exit(TRAPS);
|
intx complete_exit(TRAPS);
|
||||||
void reenter(intptr_t recursions, TRAPS);
|
void reenter(intx recursions, TRAPS);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddWaiter(ObjectWaiter* waiter);
|
void AddWaiter(ObjectWaiter* waiter);
|
||||||
|
|
|
@ -59,7 +59,7 @@ inline void ObjectMonitor::clear() {
|
||||||
assert(Atomic::load(&_header).value() != 0, "must be non-zero");
|
assert(Atomic::load(&_header).value() != 0, "must be non-zero");
|
||||||
assert(_contentions == 0, "must be 0: contentions=%d", _contentions);
|
assert(_contentions == 0, "must be 0: contentions=%d", _contentions);
|
||||||
assert(_waiters == 0, "must be 0: waiters=%d", _waiters);
|
assert(_waiters == 0, "must be 0: waiters=%d", _waiters);
|
||||||
assert(_recursions == 0, "must be 0: recursions=" INTPTR_FORMAT, _recursions);
|
assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions);
|
||||||
assert(_object != NULL, "must be non-NULL");
|
assert(_object != NULL, "must be non-NULL");
|
||||||
assert(_owner == NULL, "must be NULL: owner=" INTPTR_FORMAT, p2i(_owner));
|
assert(_owner == NULL, "must be NULL: owner=" INTPTR_FORMAT, p2i(_owner));
|
||||||
|
|
||||||
|
|
|
@ -359,7 +359,7 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, TRAPS) {
|
||||||
// 4) reenter lock1 with original recursion count
|
// 4) reenter lock1 with original recursion count
|
||||||
// 5) lock lock2
|
// 5) lock lock2
|
||||||
// NOTE: must use heavy weight monitor to handle complete_exit/reenter()
|
// NOTE: must use heavy weight monitor to handle complete_exit/reenter()
|
||||||
intptr_t ObjectSynchronizer::complete_exit(Handle obj, TRAPS) {
|
intx ObjectSynchronizer::complete_exit(Handle obj, TRAPS) {
|
||||||
if (UseBiasedLocking) {
|
if (UseBiasedLocking) {
|
||||||
BiasedLocking::revoke(obj, THREAD);
|
BiasedLocking::revoke(obj, THREAD);
|
||||||
assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
|
assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
|
||||||
|
@ -371,7 +371,7 @@ intptr_t ObjectSynchronizer::complete_exit(Handle obj, TRAPS) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: must use heavy weight monitor to handle complete_exit/reenter()
|
// NOTE: must use heavy weight monitor to handle complete_exit/reenter()
|
||||||
void ObjectSynchronizer::reenter(Handle obj, intptr_t recursion, TRAPS) {
|
void ObjectSynchronizer::reenter(Handle obj, intx recursions, TRAPS) {
|
||||||
if (UseBiasedLocking) {
|
if (UseBiasedLocking) {
|
||||||
BiasedLocking::revoke(obj, THREAD);
|
BiasedLocking::revoke(obj, THREAD);
|
||||||
assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
|
assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
|
||||||
|
@ -379,7 +379,7 @@ void ObjectSynchronizer::reenter(Handle obj, intptr_t recursion, TRAPS) {
|
||||||
|
|
||||||
ObjectMonitor* monitor = inflate(THREAD, obj(), inflate_cause_vm_internal);
|
ObjectMonitor* monitor = inflate(THREAD, obj(), inflate_cause_vm_internal);
|
||||||
|
|
||||||
monitor->reenter(recursion, THREAD);
|
monitor->reenter(recursions, THREAD);
|
||||||
}
|
}
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// JNI locks on java objects
|
// JNI locks on java objects
|
||||||
|
@ -1140,7 +1140,7 @@ void ObjectSynchronizer::om_release(Thread* self, ObjectMonitor* m,
|
||||||
guarantee(m->object() == NULL, "invariant");
|
guarantee(m->object() == NULL, "invariant");
|
||||||
stringStream ss;
|
stringStream ss;
|
||||||
guarantee((m->is_busy() | m->_recursions) == 0, "freeing in-use monitor: "
|
guarantee((m->is_busy() | m->_recursions) == 0, "freeing in-use monitor: "
|
||||||
"%s, recursions=" INTPTR_FORMAT, m->is_busy_to_string(&ss),
|
"%s, recursions=" INTX_FORMAT, m->is_busy_to_string(&ss),
|
||||||
m->_recursions);
|
m->_recursions);
|
||||||
// _next_om is used for both per-thread in-use and free lists so
|
// _next_om is used for both per-thread in-use and free lists so
|
||||||
// we have to remove 'm' from the in-use list first (as needed).
|
// we have to remove 'm' from the in-use list first (as needed).
|
||||||
|
|
|
@ -92,8 +92,8 @@ class ObjectSynchronizer : AllStatic {
|
||||||
// used by classloading to free classloader object lock,
|
// used by classloading to free classloader object lock,
|
||||||
// wait on an internal lock, and reclaim original lock
|
// wait on an internal lock, and reclaim original lock
|
||||||
// with original recursion count
|
// with original recursion count
|
||||||
static intptr_t complete_exit(Handle obj, TRAPS);
|
static intx complete_exit(Handle obj, TRAPS);
|
||||||
static void reenter (Handle obj, intptr_t recursion, TRAPS);
|
static void reenter (Handle obj, intx recursions, TRAPS);
|
||||||
|
|
||||||
// thread-specific and global ObjectMonitor free list accessors
|
// thread-specific and global ObjectMonitor free list accessors
|
||||||
static ObjectMonitor* om_alloc(Thread* self);
|
static ObjectMonitor* om_alloc(Thread* self);
|
||||||
|
@ -209,8 +209,8 @@ class ObjectLocker : public StackObj {
|
||||||
void wait_uninterruptibly(TRAPS) { ObjectSynchronizer::wait_uninterruptibly(_obj, 0, CHECK); }
|
void wait_uninterruptibly(TRAPS) { ObjectSynchronizer::wait_uninterruptibly(_obj, 0, CHECK); }
|
||||||
// complete_exit gives up lock completely, returning recursion count
|
// complete_exit gives up lock completely, returning recursion count
|
||||||
// reenter reclaims lock with original recursion count
|
// reenter reclaims lock with original recursion count
|
||||||
intptr_t complete_exit(TRAPS) { return ObjectSynchronizer::complete_exit(_obj, THREAD); }
|
intx complete_exit(TRAPS) { return ObjectSynchronizer::complete_exit(_obj, THREAD); }
|
||||||
void reenter(intptr_t recursion, TRAPS) { ObjectSynchronizer::reenter(_obj, recursion, CHECK); }
|
void reenter(intx recursions, TRAPS) { ObjectSynchronizer::reenter(_obj, recursions, CHECK); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_RUNTIME_SYNCHRONIZER_HPP
|
#endif // SHARE_RUNTIME_SYNCHRONIZER_HPP
|
||||||
|
|
|
@ -601,8 +601,6 @@ ThreadsList *ThreadsList::add_thread(ThreadsList *list, JavaThread *java_thread)
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadsList::dec_nested_handle_cnt() {
|
void ThreadsList::dec_nested_handle_cnt() {
|
||||||
// The decrement only needs to be MO_ACQ_REL since the reference
|
|
||||||
// counter is volatile (and the hazard ptr is already NULL).
|
|
||||||
Atomic::dec(&_nested_handle_cnt);
|
Atomic::dec(&_nested_handle_cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,8 +644,6 @@ JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadsList::inc_nested_handle_cnt() {
|
void ThreadsList::inc_nested_handle_cnt() {
|
||||||
// The increment needs to be MO_SEQ_CST so that the reference counter
|
|
||||||
// update is seen before the subsequent hazard ptr update.
|
|
||||||
Atomic::inc(&_nested_handle_cnt);
|
Atomic::inc(&_nested_handle_cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -911,7 +911,7 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
|
||||||
unchecked_nonstatic_field(ObjectMonitor, _owner, sizeof(void *)) /* NOTE: no type */ \
|
unchecked_nonstatic_field(ObjectMonitor, _owner, sizeof(void *)) /* NOTE: no type */ \
|
||||||
volatile_nonstatic_field(ObjectMonitor, _contentions, jint) \
|
volatile_nonstatic_field(ObjectMonitor, _contentions, jint) \
|
||||||
volatile_nonstatic_field(ObjectMonitor, _waiters, jint) \
|
volatile_nonstatic_field(ObjectMonitor, _waiters, jint) \
|
||||||
volatile_nonstatic_field(ObjectMonitor, _recursions, intptr_t) \
|
volatile_nonstatic_field(ObjectMonitor, _recursions, intx) \
|
||||||
nonstatic_field(ObjectMonitor, _next_om, ObjectMonitor*) \
|
nonstatic_field(ObjectMonitor, _next_om, ObjectMonitor*) \
|
||||||
volatile_nonstatic_field(BasicLock, _displaced_header, markWord) \
|
volatile_nonstatic_field(BasicLock, _displaced_header, markWord) \
|
||||||
nonstatic_field(BasicObjectLock, _lock, BasicLock) \
|
nonstatic_field(BasicObjectLock, _lock, BasicLock) \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue