8315884: New Object to ObjectMonitor mapping
Co-authored-by: Erik Österlund <eosterlund@openjdk.org> Co-authored-by: Stefan Karlsson <stefank@openjdk.org> Co-authored-by: Coleen Phillimore <coleenp@openjdk.org> Reviewed-by: rkennke, coleenp, dcubed
This commit is contained in:
parent
74066bcca8
commit
bd4160cea8
@ -15793,7 +15793,7 @@ instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp
|
||||
format %{ "fastlock $object,$box\t! kills $tmp,$tmp2,$tmp3" %}
|
||||
|
||||
ins_encode %{
|
||||
__ fast_lock_lightweight($object$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
|
||||
__ fast_lock_lightweight($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
|
||||
%}
|
||||
|
||||
ins_pipe(pipe_serial);
|
||||
@ -15809,7 +15809,7 @@ instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNo
|
||||
format %{ "fastunlock $object,$box\t! kills $tmp, $tmp2, $tmp3" %}
|
||||
|
||||
ins_encode %{
|
||||
__ fast_unlock_lightweight($object$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
|
||||
__ fast_unlock_lightweight($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
|
||||
%}
|
||||
|
||||
ins_pipe(pipe_serial);
|
||||
|
@ -81,7 +81,7 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr
|
||||
}
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
lightweight_lock(obj, hdr, temp, rscratch2, slow_case);
|
||||
lightweight_lock(disp_hdr, obj, hdr, temp, rscratch2, slow_case);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
Label done;
|
||||
// Load object header
|
||||
|
@ -224,10 +224,10 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe
|
||||
bind(no_count);
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
|
||||
void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register t1,
|
||||
Register t2, Register t3) {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
assert_different_registers(obj, t1, t2, t3);
|
||||
assert_different_registers(obj, box, t1, t2, t3);
|
||||
|
||||
// Handle inflated monitor.
|
||||
Label inflated;
|
||||
@ -236,6 +236,11 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
|
||||
// Finish fast lock unsuccessfully. MUST branch to with flag == NE
|
||||
Label slow_path;
|
||||
|
||||
if (UseObjectMonitorTable) {
|
||||
// Clear cache in case fast locking succeeds.
|
||||
str(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
|
||||
}
|
||||
|
||||
if (DiagnoseSyncOnValueBasedClasses != 0) {
|
||||
load_klass(t1, obj);
|
||||
ldrw(t1, Address(t1, Klass::access_flags_offset()));
|
||||
@ -244,6 +249,7 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
|
||||
}
|
||||
|
||||
const Register t1_mark = t1;
|
||||
const Register t3_t = t3;
|
||||
|
||||
{ // Lightweight locking
|
||||
|
||||
@ -251,7 +257,6 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
|
||||
Label push;
|
||||
|
||||
const Register t2_top = t2;
|
||||
const Register t3_t = t3;
|
||||
|
||||
// Check if lock-stack is full.
|
||||
ldrw(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
|
||||
@ -289,26 +294,71 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
|
||||
{ // Handle inflated monitor.
|
||||
bind(inflated);
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register t1_tagged_monitor = t1_mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
const Register t1_monitor = t1;
|
||||
|
||||
if (!UseObjectMonitorTable) {
|
||||
assert(t1_monitor == t1_mark, "should be the same here");
|
||||
} else {
|
||||
Label monitor_found;
|
||||
|
||||
// Load cache address
|
||||
lea(t3_t, Address(rthread, JavaThread::om_cache_oops_offset()));
|
||||
|
||||
const int num_unrolled = 2;
|
||||
for (int i = 0; i < num_unrolled; i++) {
|
||||
ldr(t1, Address(t3_t));
|
||||
cmp(obj, t1);
|
||||
br(Assembler::EQ, monitor_found);
|
||||
increment(t3_t, in_bytes(OMCache::oop_to_oop_difference()));
|
||||
}
|
||||
|
||||
Label loop;
|
||||
|
||||
// Search for obj in cache.
|
||||
bind(loop);
|
||||
|
||||
// Check for match.
|
||||
ldr(t1, Address(t3_t));
|
||||
cmp(obj, t1);
|
||||
br(Assembler::EQ, monitor_found);
|
||||
|
||||
// Search until null encountered, guaranteed _null_sentinel at end.
|
||||
increment(t3_t, in_bytes(OMCache::oop_to_oop_difference()));
|
||||
cbnz(t1, loop);
|
||||
// Cache Miss, NE set from cmp above, cbnz does not set flags
|
||||
b(slow_path);
|
||||
|
||||
bind(monitor_found);
|
||||
ldr(t1_monitor, Address(t3_t, OMCache::oop_to_monitor_difference()));
|
||||
}
|
||||
|
||||
const Register t2_owner_addr = t2;
|
||||
const Register t3_owner = t3;
|
||||
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast<int>(markWord::monitor_value));
|
||||
const Address owner_address(t1_monitor, ObjectMonitor::owner_offset() - monitor_tag);
|
||||
const Address recursions_address(t1_monitor, ObjectMonitor::recursions_offset() - monitor_tag);
|
||||
|
||||
Label monitor_locked;
|
||||
|
||||
// Compute owner address.
|
||||
lea(t2_owner_addr, Address(t1_tagged_monitor, (in_bytes(ObjectMonitor::owner_offset()) - monitor_tag)));
|
||||
lea(t2_owner_addr, owner_address);
|
||||
|
||||
// CAS owner (null => current thread).
|
||||
cmpxchg(t2_owner_addr, zr, rthread, Assembler::xword, /*acquire*/ true,
|
||||
/*release*/ false, /*weak*/ false, t3_owner);
|
||||
br(Assembler::EQ, locked);
|
||||
br(Assembler::EQ, monitor_locked);
|
||||
|
||||
// Check if recursive.
|
||||
cmp(t3_owner, rthread);
|
||||
br(Assembler::NE, slow_path);
|
||||
|
||||
// Recursive.
|
||||
increment(Address(t1_tagged_monitor, in_bytes(ObjectMonitor::recursions_offset()) - monitor_tag), 1);
|
||||
increment(recursions_address, 1);
|
||||
|
||||
bind(monitor_locked);
|
||||
if (UseObjectMonitorTable) {
|
||||
str(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
|
||||
}
|
||||
}
|
||||
|
||||
bind(locked);
|
||||
@ -331,13 +381,13 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
|
||||
// C2 uses the value of Flags (NE vs EQ) to determine the continuation.
|
||||
}
|
||||
|
||||
void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Register t2,
|
||||
Register t3) {
|
||||
void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Register t1,
|
||||
Register t2, Register t3) {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
assert_different_registers(obj, t1, t2, t3);
|
||||
assert_different_registers(obj, box, t1, t2, t3);
|
||||
|
||||
// Handle inflated monitor.
|
||||
Label inflated, inflated_load_monitor;
|
||||
Label inflated, inflated_load_mark;
|
||||
// Finish fast unlock successfully. MUST branch to with flag == EQ
|
||||
Label unlocked;
|
||||
// Finish fast unlock unsuccessfully. MUST branch to with flag == NE
|
||||
@ -349,13 +399,15 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
|
||||
|
||||
{ // Lightweight unlock
|
||||
|
||||
Label push_and_slow_path;
|
||||
|
||||
// Check if obj is top of lock-stack.
|
||||
ldrw(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
|
||||
subw(t2_top, t2_top, oopSize);
|
||||
ldr(t3_t, Address(rthread, t2_top));
|
||||
cmp(obj, t3_t);
|
||||
// Top of lock stack was not obj. Must be monitor.
|
||||
br(Assembler::NE, inflated_load_monitor);
|
||||
br(Assembler::NE, inflated_load_mark);
|
||||
|
||||
// Pop lock-stack.
|
||||
DEBUG_ONLY(str(zr, Address(rthread, t2_top));)
|
||||
@ -372,7 +424,10 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
|
||||
ldr(t1_mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
|
||||
// Check header for monitor (0b10).
|
||||
tbnz(t1_mark, exact_log2(markWord::monitor_value), inflated);
|
||||
// Because we got here by popping (meaning we pushed in locked)
|
||||
// there will be no monitor in the box. So we need to push back the obj
|
||||
// so that the runtime can fix any potential anonymous owner.
|
||||
tbnz(t1_mark, exact_log2(markWord::monitor_value), UseObjectMonitorTable ? push_and_slow_path : inflated);
|
||||
|
||||
// Try to unlock. Transition lock bits 0b00 => 0b01
|
||||
assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea");
|
||||
@ -381,6 +436,7 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
|
||||
/*acquire*/ false, /*release*/ true, /*weak*/ false, noreg);
|
||||
br(Assembler::EQ, unlocked);
|
||||
|
||||
bind(push_and_slow_path);
|
||||
// Compare and exchange failed.
|
||||
// Restore lock-stack and handle the unlock in runtime.
|
||||
DEBUG_ONLY(str(obj, Address(rthread, t2_top));)
|
||||
@ -391,7 +447,7 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
|
||||
|
||||
|
||||
{ // Handle inflated monitor.
|
||||
bind(inflated_load_monitor);
|
||||
bind(inflated_load_mark);
|
||||
ldr(t1_mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
#ifdef ASSERT
|
||||
tbnz(t1_mark, exact_log2(markWord::monitor_value), inflated);
|
||||
@ -412,12 +468,19 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
|
||||
bind(check_done);
|
||||
#endif
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register t1_monitor = t1_mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
const Register t1_monitor = t1;
|
||||
|
||||
// Untag the monitor.
|
||||
sub(t1_monitor, t1_mark, monitor_tag);
|
||||
if (!UseObjectMonitorTable) {
|
||||
assert(t1_monitor == t1_mark, "should be the same here");
|
||||
|
||||
// Untag the monitor.
|
||||
add(t1_monitor, t1_mark, -(int)markWord::monitor_value);
|
||||
} else {
|
||||
ldr(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
|
||||
// null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*)
|
||||
cmp(t1_monitor, checked_cast<uint8_t>(alignof(ObjectMonitor*)));
|
||||
br(Assembler::LO, slow_path);
|
||||
}
|
||||
|
||||
const Register t2_recursions = t2;
|
||||
Label not_recursive;
|
||||
|
@ -39,8 +39,8 @@
|
||||
void fast_lock(Register object, Register box, Register tmp, Register tmp2, Register tmp3);
|
||||
void fast_unlock(Register object, Register box, Register tmp, Register tmp2);
|
||||
// Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file.
|
||||
void fast_lock_lightweight(Register object, Register t1, Register t2, Register t3);
|
||||
void fast_unlock_lightweight(Register object, Register t1, Register t2, Register t3);
|
||||
void fast_lock_lightweight(Register object, Register box, Register t1, Register t2, Register t3);
|
||||
void fast_unlock_lightweight(Register object, Register box, Register t1, Register t2, Register t3);
|
||||
|
||||
void string_compare(Register str1, Register str2,
|
||||
Register cnt1, Register cnt2, Register result,
|
||||
|
@ -696,7 +696,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
|
||||
}
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
lightweight_lock(obj_reg, tmp, tmp2, tmp3, slow_case);
|
||||
lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case);
|
||||
b(count);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
// Load (object->mark() | 1) into swap_reg
|
||||
@ -752,15 +752,9 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
|
||||
bind(slow_case);
|
||||
|
||||
// Call the runtime routine for slow case
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj),
|
||||
obj_reg);
|
||||
} else {
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
}
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
b(done);
|
||||
|
||||
bind(count);
|
||||
|
@ -6750,9 +6750,9 @@ void MacroAssembler::double_move(VMRegPair src, VMRegPair dst, Register tmp) {
|
||||
// - obj: the object to be locked
|
||||
// - t1, t2, t3: temporary registers, will be destroyed
|
||||
// - slow: branched to if locking fails, absolute offset may larger than 32KB (imm14 encoding).
|
||||
void MacroAssembler::lightweight_lock(Register obj, Register t1, Register t2, Register t3, Label& slow) {
|
||||
void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register t1, Register t2, Register t3, Label& slow) {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking");
|
||||
assert_different_registers(obj, t1, t2, t3, rscratch1);
|
||||
assert_different_registers(basic_lock, obj, t1, t2, t3, rscratch1);
|
||||
|
||||
Label push;
|
||||
const Register top = t1;
|
||||
@ -6763,6 +6763,11 @@ void MacroAssembler::lightweight_lock(Register obj, Register t1, Register t2, Re
|
||||
// instruction emitted as it is part of C1's null check semantics.
|
||||
ldr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
|
||||
if (UseObjectMonitorTable) {
|
||||
// Clear cache in case fast locking succeeds.
|
||||
str(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))));
|
||||
}
|
||||
|
||||
// Check if the lock-stack is full.
|
||||
ldrw(top, Address(rthread, JavaThread::lock_stack_top_offset()));
|
||||
cmpw(top, (unsigned)LockStack::end_offset());
|
||||
|
@ -1639,7 +1639,7 @@ public:
|
||||
// Code for java.lang.Thread::onSpinWait() intrinsic.
|
||||
void spin_wait();
|
||||
|
||||
void lightweight_lock(Register obj, Register t1, Register t2, Register t3, Label& slow);
|
||||
void lightweight_lock(Register basic_lock, Register obj, Register t1, Register t2, Register t3, Label& slow);
|
||||
void lightweight_unlock(Register obj, Register t1, Register t2, Register t3, Label& slow);
|
||||
|
||||
private:
|
||||
|
@ -1811,7 +1811,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
||||
__ br(Assembler::NE, slow_path_lock);
|
||||
} else {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
__ lightweight_lock(obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock);
|
||||
__ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock);
|
||||
}
|
||||
__ bind(count);
|
||||
__ increment(Address(rthread, JavaThread::held_monitor_count_offset()));
|
||||
|
@ -985,15 +985,7 @@ void InterpreterMacroAssembler::lock_object(Register Rlock) {
|
||||
bind(slow_case);
|
||||
|
||||
// Call the runtime routine for slow case
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
// Pass oop, not lock, in fast lock case. call_VM wants R1 though.
|
||||
push(R1);
|
||||
mov(R1, Robj);
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj), R1);
|
||||
pop(R1);
|
||||
} else {
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), Rlock);
|
||||
}
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), Rlock);
|
||||
bind(done);
|
||||
}
|
||||
}
|
||||
|
@ -1043,11 +1043,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
|
||||
// None of the above fast optimizations worked so we have to get into the
|
||||
// slow case of monitor enter.
|
||||
bind(slow_case);
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj), object);
|
||||
} else {
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor);
|
||||
}
|
||||
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor);
|
||||
b(done);
|
||||
// }
|
||||
align(32, 12);
|
||||
|
@ -2804,32 +2804,39 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla
|
||||
{ // Handle inflated monitor.
|
||||
bind(inflated);
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register tagged_monitor = mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
const Register owner_addr = tmp2;
|
||||
if (!UseObjectMonitorTable) {
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register tagged_monitor = mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
const Register owner_addr = tmp2;
|
||||
|
||||
// Compute owner address.
|
||||
addi(owner_addr, tagged_monitor, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag);
|
||||
// Compute owner address.
|
||||
addi(owner_addr, tagged_monitor, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag);
|
||||
|
||||
// CAS owner (null => current thread).
|
||||
cmpxchgd(/*flag=*/flag,
|
||||
/*current_value=*/t,
|
||||
/*compare_value=*/(intptr_t)0,
|
||||
/*exchange_value=*/R16_thread,
|
||||
/*where=*/owner_addr,
|
||||
MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq,
|
||||
MacroAssembler::cmpxchgx_hint_acquire_lock());
|
||||
beq(flag, locked);
|
||||
// CAS owner (null => current thread).
|
||||
cmpxchgd(/*flag=*/flag,
|
||||
/*current_value=*/t,
|
||||
/*compare_value=*/(intptr_t)0,
|
||||
/*exchange_value=*/R16_thread,
|
||||
/*where=*/owner_addr,
|
||||
MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq,
|
||||
MacroAssembler::cmpxchgx_hint_acquire_lock());
|
||||
beq(flag, locked);
|
||||
|
||||
// Check if recursive.
|
||||
cmpd(flag, t, R16_thread);
|
||||
bne(flag, slow_path);
|
||||
// Check if recursive.
|
||||
cmpd(flag, t, R16_thread);
|
||||
bne(flag, slow_path);
|
||||
|
||||
// Recursive.
|
||||
ld(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
|
||||
addi(tmp1, tmp1, 1);
|
||||
std(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
|
||||
// Recursive.
|
||||
ld(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
|
||||
addi(tmp1, tmp1, 1);
|
||||
std(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
|
||||
} else {
|
||||
// OMCache lookup not supported yet. Take the slowpath.
|
||||
// Set flag to NE
|
||||
crxor(flag, Assembler::equal, flag, Assembler::equal);
|
||||
b(slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
bind(locked);
|
||||
@ -2943,49 +2950,56 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f
|
||||
bind(check_done);
|
||||
#endif
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register monitor = mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
if (!UseObjectMonitorTable) {
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register monitor = mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
|
||||
// Untag the monitor.
|
||||
subi(monitor, mark, monitor_tag);
|
||||
// Untag the monitor.
|
||||
subi(monitor, mark, monitor_tag);
|
||||
|
||||
const Register recursions = tmp2;
|
||||
Label not_recursive;
|
||||
const Register recursions = tmp2;
|
||||
Label not_recursive;
|
||||
|
||||
// Check if recursive.
|
||||
ld(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor);
|
||||
addic_(recursions, recursions, -1);
|
||||
blt(CCR0, not_recursive);
|
||||
// Check if recursive.
|
||||
ld(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor);
|
||||
addic_(recursions, recursions, -1);
|
||||
blt(CCR0, not_recursive);
|
||||
|
||||
// Recursive unlock.
|
||||
std(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor);
|
||||
crorc(CCR0, Assembler::equal, CCR0, Assembler::equal);
|
||||
b(unlocked);
|
||||
// Recursive unlock.
|
||||
std(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor);
|
||||
crorc(CCR0, Assembler::equal, CCR0, Assembler::equal);
|
||||
b(unlocked);
|
||||
|
||||
bind(not_recursive);
|
||||
bind(not_recursive);
|
||||
|
||||
Label release_;
|
||||
const Register t2 = tmp2;
|
||||
Label release_;
|
||||
const Register t2 = tmp2;
|
||||
|
||||
// Check if the entry lists are empty.
|
||||
ld(t, in_bytes(ObjectMonitor::EntryList_offset()), monitor);
|
||||
ld(t2, in_bytes(ObjectMonitor::cxq_offset()), monitor);
|
||||
orr(t, t, t2);
|
||||
cmpdi(flag, t, 0);
|
||||
beq(flag, release_);
|
||||
// Check if the entry lists are empty.
|
||||
ld(t, in_bytes(ObjectMonitor::EntryList_offset()), monitor);
|
||||
ld(t2, in_bytes(ObjectMonitor::cxq_offset()), monitor);
|
||||
orr(t, t, t2);
|
||||
cmpdi(flag, t, 0);
|
||||
beq(flag, release_);
|
||||
|
||||
// The owner may be anonymous and we removed the last obj entry in
|
||||
// the lock-stack. This loses the information about the owner.
|
||||
// Write the thread to the owner field so the runtime knows the owner.
|
||||
std(R16_thread, in_bytes(ObjectMonitor::owner_offset()), monitor);
|
||||
b(slow_path);
|
||||
// The owner may be anonymous and we removed the last obj entry in
|
||||
// the lock-stack. This loses the information about the owner.
|
||||
// Write the thread to the owner field so the runtime knows the owner.
|
||||
std(R16_thread, in_bytes(ObjectMonitor::owner_offset()), monitor);
|
||||
b(slow_path);
|
||||
|
||||
bind(release_);
|
||||
// Set owner to null.
|
||||
release();
|
||||
// t contains 0
|
||||
std(t, in_bytes(ObjectMonitor::owner_offset()), monitor);
|
||||
bind(release_);
|
||||
// Set owner to null.
|
||||
release();
|
||||
// t contains 0
|
||||
std(t, in_bytes(ObjectMonitor::owner_offset()), monitor);
|
||||
} else {
|
||||
// OMCache lookup not supported yet. Take the slowpath.
|
||||
// Set flag to NE
|
||||
crxor(flag, Assembler::equal, flag, Assembler::equal);
|
||||
b(slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
bind(unlocked);
|
||||
|
@ -323,25 +323,30 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register tmp1, Regis
|
||||
{ // Handle inflated monitor.
|
||||
bind(inflated);
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register tmp1_tagged_monitor = tmp1_mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
const Register tmp2_owner_addr = tmp2;
|
||||
const Register tmp3_owner = tmp3;
|
||||
if (!UseObjectMonitorTable) {
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register tmp1_tagged_monitor = tmp1_mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
const Register tmp2_owner_addr = tmp2;
|
||||
const Register tmp3_owner = tmp3;
|
||||
|
||||
// Compute owner address.
|
||||
la(tmp2_owner_addr, Address(tmp1_tagged_monitor, (in_bytes(ObjectMonitor::owner_offset()) - monitor_tag)));
|
||||
// Compute owner address.
|
||||
la(tmp2_owner_addr, Address(tmp1_tagged_monitor, (in_bytes(ObjectMonitor::owner_offset()) - monitor_tag)));
|
||||
|
||||
// CAS owner (null => current thread).
|
||||
cmpxchg(/*addr*/ tmp2_owner_addr, /*expected*/ zr, /*new*/ xthread, Assembler::int64,
|
||||
/*acquire*/ Assembler::aq, /*release*/ Assembler::relaxed, /*result*/ tmp3_owner);
|
||||
beqz(tmp3_owner, locked);
|
||||
// CAS owner (null => current thread).
|
||||
cmpxchg(/*addr*/ tmp2_owner_addr, /*expected*/ zr, /*new*/ xthread, Assembler::int64,
|
||||
/*acquire*/ Assembler::aq, /*release*/ Assembler::relaxed, /*result*/ tmp3_owner);
|
||||
beqz(tmp3_owner, locked);
|
||||
|
||||
// Check if recursive.
|
||||
bne(tmp3_owner, xthread, slow_path);
|
||||
// Check if recursive.
|
||||
bne(tmp3_owner, xthread, slow_path);
|
||||
|
||||
// Recursive.
|
||||
increment(Address(tmp1_tagged_monitor, in_bytes(ObjectMonitor::recursions_offset()) - monitor_tag), 1, tmp2, tmp3);
|
||||
// Recursive.
|
||||
increment(Address(tmp1_tagged_monitor, in_bytes(ObjectMonitor::recursions_offset()) - monitor_tag), 1, tmp2, tmp3);
|
||||
} else {
|
||||
// OMCache lookup not supported yet. Take the slowpath.
|
||||
j(slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
bind(locked);
|
||||
@ -453,49 +458,54 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register tmp1, Reg
|
||||
bind(check_done);
|
||||
#endif
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register tmp1_monitor = tmp1_mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
if (!UseObjectMonitorTable) {
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register tmp1_monitor = tmp1_mark;
|
||||
const uintptr_t monitor_tag = markWord::monitor_value;
|
||||
|
||||
// Untag the monitor.
|
||||
sub(tmp1_monitor, tmp1_mark, monitor_tag);
|
||||
// Untag the monitor.
|
||||
sub(tmp1_monitor, tmp1_mark, monitor_tag);
|
||||
|
||||
const Register tmp2_recursions = tmp2;
|
||||
Label not_recursive;
|
||||
const Register tmp2_recursions = tmp2;
|
||||
Label not_recursive;
|
||||
|
||||
// Check if recursive.
|
||||
ld(tmp2_recursions, Address(tmp1_monitor, ObjectMonitor::recursions_offset()));
|
||||
beqz(tmp2_recursions, not_recursive);
|
||||
// Check if recursive.
|
||||
ld(tmp2_recursions, Address(tmp1_monitor, ObjectMonitor::recursions_offset()));
|
||||
beqz(tmp2_recursions, not_recursive);
|
||||
|
||||
// Recursive unlock.
|
||||
addi(tmp2_recursions, tmp2_recursions, -1);
|
||||
sd(tmp2_recursions, Address(tmp1_monitor, ObjectMonitor::recursions_offset()));
|
||||
j(unlocked);
|
||||
// Recursive unlock.
|
||||
addi(tmp2_recursions, tmp2_recursions, -1);
|
||||
sd(tmp2_recursions, Address(tmp1_monitor, ObjectMonitor::recursions_offset()));
|
||||
j(unlocked);
|
||||
|
||||
bind(not_recursive);
|
||||
bind(not_recursive);
|
||||
|
||||
Label release;
|
||||
const Register tmp2_owner_addr = tmp2;
|
||||
Label release;
|
||||
const Register tmp2_owner_addr = tmp2;
|
||||
|
||||
// Compute owner address.
|
||||
la(tmp2_owner_addr, Address(tmp1_monitor, ObjectMonitor::owner_offset()));
|
||||
// Compute owner address.
|
||||
la(tmp2_owner_addr, Address(tmp1_monitor, ObjectMonitor::owner_offset()));
|
||||
|
||||
// Check if the entry lists are empty.
|
||||
ld(t0, Address(tmp1_monitor, ObjectMonitor::EntryList_offset()));
|
||||
ld(tmp3_t, Address(tmp1_monitor, ObjectMonitor::cxq_offset()));
|
||||
orr(t0, t0, tmp3_t);
|
||||
beqz(t0, release);
|
||||
// Check if the entry lists are empty.
|
||||
ld(t0, Address(tmp1_monitor, ObjectMonitor::EntryList_offset()));
|
||||
ld(tmp3_t, Address(tmp1_monitor, ObjectMonitor::cxq_offset()));
|
||||
orr(t0, t0, tmp3_t);
|
||||
beqz(t0, release);
|
||||
|
||||
// The owner may be anonymous and we removed the last obj entry in
|
||||
// the lock-stack. This loses the information about the owner.
|
||||
// Write the thread to the owner field so the runtime knows the owner.
|
||||
sd(xthread, Address(tmp2_owner_addr));
|
||||
j(slow_path);
|
||||
// The owner may be anonymous and we removed the last obj entry in
|
||||
// the lock-stack. This loses the information about the owner.
|
||||
// Write the thread to the owner field so the runtime knows the owner.
|
||||
sd(xthread, Address(tmp2_owner_addr));
|
||||
j(slow_path);
|
||||
|
||||
bind(release);
|
||||
// Set owner to null.
|
||||
membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
|
||||
sd(zr, Address(tmp2_owner_addr));
|
||||
bind(release);
|
||||
// Set owner to null.
|
||||
membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
|
||||
sd(zr, Address(tmp2_owner_addr));
|
||||
} else {
|
||||
// OMCache lookup not supported yet. Take the slowpath.
|
||||
j(slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
bind(unlocked);
|
||||
|
@ -792,15 +792,9 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
|
||||
bind(slow_case);
|
||||
|
||||
// Call the runtime routine for slow case
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj),
|
||||
obj_reg);
|
||||
} else {
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
}
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
j(done);
|
||||
|
||||
bind(count);
|
||||
|
@ -1072,16 +1072,9 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
|
||||
// None of the above fast optimizations worked so we have to get into the
|
||||
// slow case of monitor enter.
|
||||
bind(slow_case);
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
// for lightweight locking we need to use monitorenter_obj, see interpreterRuntime.cpp
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj),
|
||||
object);
|
||||
} else {
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
monitor);
|
||||
}
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
monitor);
|
||||
// }
|
||||
|
||||
bind(done);
|
||||
|
@ -6218,26 +6218,33 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Registe
|
||||
{ // Handle inflated monitor.
|
||||
bind(inflated);
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register tagged_monitor = mark;
|
||||
const Register zero = tmp2;
|
||||
if (!UseObjectMonitorTable) {
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register tagged_monitor = mark;
|
||||
const Register zero = tmp2;
|
||||
|
||||
// Try to CAS m->owner from null to current thread.
|
||||
// If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ.
|
||||
// Otherwise, register zero is filled with the current owner.
|
||||
z_lghi(zero, 0);
|
||||
z_csg(zero, Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), tagged_monitor);
|
||||
z_bre(locked);
|
||||
// Try to CAS m->owner from null to current thread.
|
||||
// If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ.
|
||||
// Otherwise, register zero is filled with the current owner.
|
||||
z_lghi(zero, 0);
|
||||
z_csg(zero, Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), tagged_monitor);
|
||||
z_bre(locked);
|
||||
|
||||
// Check if recursive.
|
||||
z_cgr(Z_thread, zero); // zero contains the owner from z_csg instruction
|
||||
z_brne(slow_path);
|
||||
// Check if recursive.
|
||||
z_cgr(Z_thread, zero); // zero contains the owner from z_csg instruction
|
||||
z_brne(slow_path);
|
||||
|
||||
// Recursive
|
||||
z_agsi(Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 1ll);
|
||||
z_cgr(zero, zero);
|
||||
// z_bru(locked);
|
||||
// Uncomment above line in the future, for now jump address is right next to us.
|
||||
// Recursive
|
||||
z_agsi(Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 1ll);
|
||||
z_cgr(zero, zero);
|
||||
// z_bru(locked);
|
||||
// Uncomment above line in the future, for now jump address is right next to us.
|
||||
} else {
|
||||
// OMCache lookup not supported yet. Take the slowpath.
|
||||
// Set flag to NE
|
||||
z_ltgr(obj, obj);
|
||||
z_bru(slow_path);
|
||||
}
|
||||
}
|
||||
BLOCK_COMMENT("} handle_inflated_monitor_lightweight_locking");
|
||||
|
||||
@ -6364,42 +6371,49 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Regis
|
||||
bind(check_done);
|
||||
#endif // ASSERT
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register monitor = mark;
|
||||
if (!UseObjectMonitorTable) {
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register monitor = mark;
|
||||
|
||||
NearLabel not_recursive;
|
||||
const Register recursions = tmp2;
|
||||
NearLabel not_recursive;
|
||||
const Register recursions = tmp2;
|
||||
|
||||
// Check if recursive.
|
||||
load_and_test_long(recursions, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
|
||||
z_bre(not_recursive); // if 0 then jump, it's not recursive locking
|
||||
// Check if recursive.
|
||||
load_and_test_long(recursions, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
|
||||
z_bre(not_recursive); // if 0 then jump, it's not recursive locking
|
||||
|
||||
// Recursive unlock
|
||||
z_agsi(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), -1ll);
|
||||
z_cgr(monitor, monitor); // set the CC to EQUAL
|
||||
z_bru(unlocked);
|
||||
// Recursive unlock
|
||||
z_agsi(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), -1ll);
|
||||
z_cgr(monitor, monitor); // set the CC to EQUAL
|
||||
z_bru(unlocked);
|
||||
|
||||
bind(not_recursive);
|
||||
bind(not_recursive);
|
||||
|
||||
NearLabel not_ok;
|
||||
// Check if the entry lists are empty.
|
||||
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
|
||||
z_brne(not_ok);
|
||||
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
|
||||
z_brne(not_ok);
|
||||
NearLabel not_ok;
|
||||
// Check if the entry lists are empty.
|
||||
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
|
||||
z_brne(not_ok);
|
||||
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
|
||||
z_brne(not_ok);
|
||||
|
||||
z_release();
|
||||
z_stg(tmp2 /*=0*/, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
|
||||
z_release();
|
||||
z_stg(tmp2 /*=0*/, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
|
||||
|
||||
z_bru(unlocked); // CC = EQ here
|
||||
z_bru(unlocked); // CC = EQ here
|
||||
|
||||
bind(not_ok);
|
||||
bind(not_ok);
|
||||
|
||||
// The owner may be anonymous, and we removed the last obj entry in
|
||||
// the lock-stack. This loses the information about the owner.
|
||||
// Write the thread to the owner field so the runtime knows the owner.
|
||||
z_stg(Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
|
||||
z_bru(slow_path); // CC = NE here
|
||||
// The owner may be anonymous, and we removed the last obj entry in
|
||||
// the lock-stack. This loses the information about the owner.
|
||||
// Write the thread to the owner field so the runtime knows the owner.
|
||||
z_stg(Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
|
||||
z_bru(slow_path); // CC = NE here
|
||||
} else {
|
||||
// OMCache lookup not supported yet. Take the slowpath.
|
||||
// Set flag to NE
|
||||
z_ltgr(obj, obj);
|
||||
z_bru(slow_path);
|
||||
}
|
||||
}
|
||||
|
||||
bind(unlocked);
|
||||
|
@ -66,11 +66,13 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
#ifdef _LP64
|
||||
const Register thread = r15_thread;
|
||||
lightweight_lock(disp_hdr, obj, hdr, thread, tmp, slow_case);
|
||||
#else
|
||||
const Register thread = disp_hdr;
|
||||
get_thread(thread);
|
||||
// Implicit null check.
|
||||
movptr(hdr, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
// Lacking registers and thread on x86_32. Always take slow path.
|
||||
jmp(slow_case);
|
||||
#endif
|
||||
lightweight_lock(obj, hdr, thread, tmp, slow_case);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
Label done;
|
||||
// Load object header
|
||||
@ -139,10 +141,8 @@ void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_
|
||||
#ifdef _LP64
|
||||
lightweight_unlock(obj, disp_hdr, r15_thread, hdr, slow_case);
|
||||
#else
|
||||
// This relies on the implementation of lightweight_unlock being able to handle
|
||||
// that the reg_rax and thread Register parameters may alias each other.
|
||||
get_thread(disp_hdr);
|
||||
lightweight_unlock(obj, disp_hdr, disp_hdr, hdr, slow_case);
|
||||
// Lacking registers and thread on x86_32. Always take slow path.
|
||||
jmp(slow_case);
|
||||
#endif
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
// test if object header is pointing to the displaced header, and if so, restore
|
||||
|
@ -96,6 +96,7 @@ void C2FastUnlockLightweightStub::emit(C2_MacroAssembler& masm) {
|
||||
{ // Restore held monitor count and slow path.
|
||||
|
||||
__ bind(restore_held_monitor_count_and_slow_path);
|
||||
__ bind(_slow_path);
|
||||
// Restore held monitor count.
|
||||
__ increment(Address(_thread, JavaThread::held_monitor_count_offset()));
|
||||
// increment will always result in ZF = 0 (no overflows).
|
||||
@ -112,19 +113,23 @@ void C2FastUnlockLightweightStub::emit(C2_MacroAssembler& masm) {
|
||||
#ifndef _LP64
|
||||
__ jmpb(restore_held_monitor_count_and_slow_path);
|
||||
#else // _LP64
|
||||
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast<int>(markWord::monitor_value));
|
||||
const Address succ_address(monitor, ObjectMonitor::succ_offset() - monitor_tag);
|
||||
const Address owner_address(monitor, ObjectMonitor::owner_offset() - monitor_tag);
|
||||
|
||||
// successor null check.
|
||||
__ cmpptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD);
|
||||
__ cmpptr(succ_address, NULL_WORD);
|
||||
__ jccb(Assembler::equal, restore_held_monitor_count_and_slow_path);
|
||||
|
||||
// Release lock.
|
||||
__ movptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD);
|
||||
__ movptr(owner_address, NULL_WORD);
|
||||
|
||||
// Fence.
|
||||
// Instead of MFENCE we use a dummy locked add of 0 to the top-of-stack.
|
||||
__ lock(); __ addl(Address(rsp, 0), 0);
|
||||
|
||||
// Recheck successor.
|
||||
__ cmpptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD);
|
||||
__ cmpptr(succ_address, NULL_WORD);
|
||||
// Observed a successor after the release -> fence we have handed off the monitor
|
||||
__ jccb(Assembler::notEqual, fix_zf_and_unlocked);
|
||||
|
||||
@ -133,7 +138,7 @@ void C2FastUnlockLightweightStub::emit(C2_MacroAssembler& masm) {
|
||||
// not handle the monitor handoff. Currently only works
|
||||
// due to the responsible thread.
|
||||
__ xorptr(rax, rax);
|
||||
__ lock(); __ cmpxchgptr(_thread, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)));
|
||||
__ lock(); __ cmpxchgptr(_thread, owner_address);
|
||||
__ jccb (Assembler::equal, restore_held_monitor_count_and_slow_path);
|
||||
#endif
|
||||
|
||||
|
@ -590,6 +590,11 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
|
||||
// Finish fast lock unsuccessfully. MUST jump with ZF == 0
|
||||
Label slow_path;
|
||||
|
||||
if (UseObjectMonitorTable) {
|
||||
// Clear cache in case fast locking succeeds.
|
||||
movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0);
|
||||
}
|
||||
|
||||
if (DiagnoseSyncOnValueBasedClasses != 0) {
|
||||
load_klass(rax_reg, obj, t);
|
||||
movl(rax_reg, Address(rax_reg, Klass::access_flags_offset()));
|
||||
@ -603,7 +608,7 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
|
||||
|
||||
Label push;
|
||||
|
||||
const Register top = box;
|
||||
const Register top = UseObjectMonitorTable ? rax_reg : box;
|
||||
|
||||
// Load the mark.
|
||||
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
@ -630,6 +635,10 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
|
||||
lock(); cmpxchgptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
jcc(Assembler::notEqual, slow_path);
|
||||
|
||||
if (UseObjectMonitorTable) {
|
||||
// Need to reload top, clobbered by CAS.
|
||||
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
|
||||
}
|
||||
bind(push);
|
||||
// After successful lock, push object on lock-stack.
|
||||
movptr(Address(thread, top), obj);
|
||||
@ -640,19 +649,68 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
|
||||
{ // Handle inflated monitor.
|
||||
bind(inflated);
|
||||
|
||||
const Register tagged_monitor = mark;
|
||||
const Register monitor = t;
|
||||
|
||||
if (!UseObjectMonitorTable) {
|
||||
assert(mark == monitor, "should be the same here");
|
||||
} else {
|
||||
// Uses ObjectMonitorTable. Look for the monitor in the om_cache.
|
||||
// Fetch ObjectMonitor* from the cache or take the slow-path.
|
||||
Label monitor_found;
|
||||
|
||||
// Load cache address
|
||||
lea(t, Address(thread, JavaThread::om_cache_oops_offset()));
|
||||
|
||||
const int num_unrolled = 2;
|
||||
for (int i = 0; i < num_unrolled; i++) {
|
||||
cmpptr(obj, Address(t));
|
||||
jccb(Assembler::equal, monitor_found);
|
||||
increment(t, in_bytes(OMCache::oop_to_oop_difference()));
|
||||
}
|
||||
|
||||
Label loop;
|
||||
|
||||
// Search for obj in cache.
|
||||
bind(loop);
|
||||
|
||||
// Check for match.
|
||||
cmpptr(obj, Address(t));
|
||||
jccb(Assembler::equal, monitor_found);
|
||||
|
||||
// Search until null encountered, guaranteed _null_sentinel at end.
|
||||
cmpptr(Address(t), 1);
|
||||
jcc(Assembler::below, slow_path); // 0 check, but with ZF=0 when *t == 0
|
||||
increment(t, in_bytes(OMCache::oop_to_oop_difference()));
|
||||
jmpb(loop);
|
||||
|
||||
// Cache hit.
|
||||
bind(monitor_found);
|
||||
movptr(monitor, Address(t, OMCache::oop_to_monitor_difference()));
|
||||
}
|
||||
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast<int>(markWord::monitor_value));
|
||||
const Address recursions_address(monitor, ObjectMonitor::recursions_offset() - monitor_tag);
|
||||
const Address owner_address(monitor, ObjectMonitor::owner_offset() - monitor_tag);
|
||||
|
||||
Label monitor_locked;
|
||||
// Lock the monitor.
|
||||
|
||||
// CAS owner (null => current thread).
|
||||
xorptr(rax_reg, rax_reg);
|
||||
lock(); cmpxchgptr(thread, Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)));
|
||||
jccb(Assembler::equal, locked);
|
||||
lock(); cmpxchgptr(thread, owner_address);
|
||||
jccb(Assembler::equal, monitor_locked);
|
||||
|
||||
// Check if recursive.
|
||||
cmpptr(thread, rax_reg);
|
||||
jccb(Assembler::notEqual, slow_path);
|
||||
|
||||
// Recursive.
|
||||
increment(Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
|
||||
increment(recursions_address);
|
||||
|
||||
bind(monitor_locked);
|
||||
if (UseObjectMonitorTable) {
|
||||
// Cache the monitor for unlock
|
||||
movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), monitor);
|
||||
}
|
||||
}
|
||||
|
||||
bind(locked);
|
||||
@ -694,7 +752,9 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
|
||||
decrement(Address(thread, JavaThread::held_monitor_count_offset()));
|
||||
|
||||
const Register mark = t;
|
||||
const Register top = reg_rax;
|
||||
const Register monitor = t;
|
||||
const Register top = UseObjectMonitorTable ? t : reg_rax;
|
||||
const Register box = reg_rax;
|
||||
|
||||
Label dummy;
|
||||
C2FastUnlockLightweightStub* stub = nullptr;
|
||||
@ -706,14 +766,17 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
|
||||
|
||||
Label& push_and_slow_path = stub == nullptr ? dummy : stub->push_and_slow_path();
|
||||
Label& check_successor = stub == nullptr ? dummy : stub->check_successor();
|
||||
Label& slow_path = stub == nullptr ? dummy : stub->slow_path();
|
||||
|
||||
{ // Lightweight Unlock
|
||||
|
||||
// Load top.
|
||||
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
|
||||
|
||||
// Prefetch mark.
|
||||
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
if (!UseObjectMonitorTable) {
|
||||
// Prefetch mark.
|
||||
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
}
|
||||
|
||||
// Check if obj is top of lock-stack.
|
||||
cmpptr(obj, Address(thread, top, Address::times_1, -oopSize));
|
||||
@ -730,6 +793,11 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
|
||||
|
||||
// We elide the monitor check, let the CAS fail instead.
|
||||
|
||||
if (UseObjectMonitorTable) {
|
||||
// Load mark.
|
||||
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
}
|
||||
|
||||
// Try to unlock. Transition lock bits 0b00 => 0b01
|
||||
movptr(reg_rax, mark);
|
||||
andptr(reg_rax, ~(int32_t)markWord::lock_mask);
|
||||
@ -751,6 +819,9 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
|
||||
jccb(Assembler::notEqual, inflated_check_lock_stack);
|
||||
stop("Fast Unlock lock on stack");
|
||||
bind(check_done);
|
||||
if (UseObjectMonitorTable) {
|
||||
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
}
|
||||
testptr(mark, markWord::monitor_value);
|
||||
jccb(Assembler::notZero, inflated);
|
||||
stop("Fast Unlock not monitor");
|
||||
@ -758,43 +829,40 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
|
||||
|
||||
bind(inflated);
|
||||
|
||||
// mark contains the tagged ObjectMonitor*.
|
||||
const Register monitor = mark;
|
||||
if (!UseObjectMonitorTable) {
|
||||
assert(mark == monitor, "should be the same here");
|
||||
} else {
|
||||
// Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack.
|
||||
movptr(monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
|
||||
// null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*)
|
||||
cmpptr(monitor, alignof(ObjectMonitor*));
|
||||
jcc(Assembler::below, slow_path);
|
||||
}
|
||||
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast<int>(markWord::monitor_value));
|
||||
const Address recursions_address{monitor, ObjectMonitor::recursions_offset() - monitor_tag};
|
||||
const Address cxq_address{monitor, ObjectMonitor::cxq_offset() - monitor_tag};
|
||||
const Address EntryList_address{monitor, ObjectMonitor::EntryList_offset() - monitor_tag};
|
||||
const Address owner_address{monitor, ObjectMonitor::owner_offset() - monitor_tag};
|
||||
|
||||
#ifndef _LP64
|
||||
// Check if recursive.
|
||||
xorptr(reg_rax, reg_rax);
|
||||
orptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
|
||||
jcc(Assembler::notZero, check_successor);
|
||||
|
||||
// Check if the entry lists are empty.
|
||||
movptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
|
||||
orptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
|
||||
jcc(Assembler::notZero, check_successor);
|
||||
|
||||
// Release lock.
|
||||
movptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD);
|
||||
#else // _LP64
|
||||
Label recursive;
|
||||
|
||||
// Check if recursive.
|
||||
cmpptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 0);
|
||||
cmpptr(recursions_address, 0);
|
||||
jccb(Assembler::notEqual, recursive);
|
||||
|
||||
// Check if the entry lists are empty.
|
||||
movptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
|
||||
orptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
|
||||
movptr(reg_rax, cxq_address);
|
||||
orptr(reg_rax, EntryList_address);
|
||||
jcc(Assembler::notZero, check_successor);
|
||||
|
||||
// Release lock.
|
||||
movptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD);
|
||||
movptr(owner_address, NULL_WORD);
|
||||
jmpb(unlocked);
|
||||
|
||||
// Recursive unlock.
|
||||
bind(recursive);
|
||||
decrement(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
|
||||
decrement(recursions_address);
|
||||
xorl(t, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
bind(unlocked);
|
||||
|
@ -1183,11 +1183,11 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) {
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
#ifdef _LP64
|
||||
const Register thread = r15_thread;
|
||||
lightweight_lock(lock_reg, obj_reg, swap_reg, thread, tmp_reg, slow_case);
|
||||
#else
|
||||
const Register thread = lock_reg;
|
||||
get_thread(thread);
|
||||
// Lacking registers and thread on x86_32. Always take slow path.
|
||||
jmp(slow_case);
|
||||
#endif
|
||||
lightweight_lock(obj_reg, swap_reg, thread, tmp_reg, slow_case);
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
// Load immediate 1 into swap_reg %rax
|
||||
movl(swap_reg, 1);
|
||||
@ -1249,15 +1249,9 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) {
|
||||
bind(slow_case);
|
||||
|
||||
// Call the runtime routine for slow case
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj),
|
||||
obj_reg);
|
||||
} else {
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
}
|
||||
call_VM(noreg,
|
||||
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
|
||||
lock_reg);
|
||||
bind(done);
|
||||
}
|
||||
}
|
||||
@ -1306,10 +1300,8 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) {
|
||||
#ifdef _LP64
|
||||
lightweight_unlock(obj_reg, swap_reg, r15_thread, header_reg, slow_case);
|
||||
#else
|
||||
// This relies on the implementation of lightweight_unlock being able to handle
|
||||
// that the reg_rax and thread Register parameters may alias each other.
|
||||
get_thread(swap_reg);
|
||||
lightweight_unlock(obj_reg, swap_reg, swap_reg, header_reg, slow_case);
|
||||
// Lacking registers and thread on x86_32. Always take slow path.
|
||||
jmp(slow_case);
|
||||
#endif
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
// Load the old header from BasicLock structure
|
||||
|
@ -10275,9 +10275,9 @@ void MacroAssembler::check_stack_alignment(Register sp, const char* msg, unsigne
|
||||
// reg_rax: rax
|
||||
// thread: the thread which attempts to lock obj
|
||||
// tmp: a temporary register
|
||||
void MacroAssembler::lightweight_lock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow) {
|
||||
void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register reg_rax, Register thread, Register tmp, Label& slow) {
|
||||
assert(reg_rax == rax, "");
|
||||
assert_different_registers(obj, reg_rax, thread, tmp);
|
||||
assert_different_registers(basic_lock, obj, reg_rax, thread, tmp);
|
||||
|
||||
Label push;
|
||||
const Register top = tmp;
|
||||
@ -10286,6 +10286,11 @@ void MacroAssembler::lightweight_lock(Register obj, Register reg_rax, Register t
|
||||
// instruction emitted as it is part of C1's null check semantics.
|
||||
movptr(reg_rax, Address(obj, oopDesc::mark_offset_in_bytes()));
|
||||
|
||||
if (UseObjectMonitorTable) {
|
||||
// Clear cache in case fast locking succeeds.
|
||||
movptr(Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))), 0);
|
||||
}
|
||||
|
||||
// Load top.
|
||||
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
|
||||
|
||||
@ -10324,13 +10329,9 @@ void MacroAssembler::lightweight_lock(Register obj, Register reg_rax, Register t
|
||||
// reg_rax: rax
|
||||
// thread: the thread
|
||||
// tmp: a temporary register
|
||||
//
|
||||
// x86_32 Note: reg_rax and thread may alias each other due to limited register
|
||||
// availiability.
|
||||
void MacroAssembler::lightweight_unlock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow) {
|
||||
assert(reg_rax == rax, "");
|
||||
assert_different_registers(obj, reg_rax, tmp);
|
||||
LP64_ONLY(assert_different_registers(obj, reg_rax, thread, tmp);)
|
||||
assert_different_registers(obj, reg_rax, thread, tmp);
|
||||
|
||||
Label unlocked, push_and_slow;
|
||||
const Register top = tmp;
|
||||
@ -10370,10 +10371,6 @@ void MacroAssembler::lightweight_unlock(Register obj, Register reg_rax, Register
|
||||
|
||||
bind(push_and_slow);
|
||||
// Restore lock-stack and handle the unlock in runtime.
|
||||
if (thread == reg_rax) {
|
||||
// On x86_32 we may lose the thread.
|
||||
get_thread(thread);
|
||||
}
|
||||
#ifdef ASSERT
|
||||
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
|
||||
movptr(Address(thread, top), obj);
|
||||
|
@ -2148,7 +2148,7 @@ public:
|
||||
|
||||
void check_stack_alignment(Register sp, const char* msg, unsigned bias = 0, Register tmp = noreg);
|
||||
|
||||
void lightweight_lock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow);
|
||||
void lightweight_lock(Register basic_lock, Register obj, Register reg_rax, Register thread, Register tmp, Label& slow);
|
||||
void lightweight_unlock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow);
|
||||
|
||||
#ifdef _LP64
|
||||
|
@ -62,9 +62,11 @@ void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* mas
|
||||
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
// check if monitor
|
||||
__ testptr(result, markWord::monitor_value);
|
||||
__ jcc(Assembler::notZero, slowCase);
|
||||
if (!UseObjectMonitorTable) {
|
||||
// check if monitor
|
||||
__ testptr(result, markWord::monitor_value);
|
||||
__ jcc(Assembler::notZero, slowCase);
|
||||
}
|
||||
} else {
|
||||
// check if locked
|
||||
__ testptr(result, markWord::unlocked_value);
|
||||
|
@ -1686,7 +1686,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
||||
__ jcc(Assembler::notEqual, slow_path_lock);
|
||||
} else {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
__ lightweight_lock(obj_reg, swap_reg, thread, lock_reg, slow_path_lock);
|
||||
// Lacking registers and thread on x86_32. Always take slow path.
|
||||
__ jmp(slow_path_lock);
|
||||
}
|
||||
__ bind(count_mon);
|
||||
__ inc_held_monitor_count();
|
||||
|
@ -2266,7 +2266,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
||||
__ jcc(Assembler::notEqual, slow_path_lock);
|
||||
} else {
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
__ lightweight_lock(obj_reg, swap_reg, r15_thread, rscratch1, slow_path_lock);
|
||||
__ lightweight_lock(lock_reg, obj_reg, swap_reg, r15_thread, rscratch1, slow_path_lock);
|
||||
}
|
||||
__ bind(count_mon);
|
||||
__ inc_held_monitor_count();
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "oops/method.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "runtime/basicLock.inline.hpp"
|
||||
#include "runtime/frame.inline.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
@ -44,6 +45,7 @@
|
||||
#include "runtime/timer.hpp"
|
||||
#include "runtime/timerTrace.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
#include "entry_zero.hpp"
|
||||
@ -331,23 +333,27 @@ int ZeroInterpreter::native_entry(Method* method, intptr_t UNUSED, TRAPS) {
|
||||
if (method->is_synchronized()) {
|
||||
monitor = (BasicObjectLock*) istate->stack_base();
|
||||
oop lockee = monitor->obj();
|
||||
markWord disp = lockee->mark().set_unlocked();
|
||||
monitor->lock()->set_displaced_header(disp);
|
||||
bool call_vm = (LockingMode == LM_MONITOR);
|
||||
bool inc_monitor_count = true;
|
||||
if (call_vm || lockee->cas_set_mark(markWord::from_pointer(monitor), disp) != disp) {
|
||||
// Is it simple recursive case?
|
||||
if (!call_vm && thread->is_lock_owned((address) disp.clear_lock_bits().to_pointer())) {
|
||||
monitor->lock()->set_displaced_header(markWord::from_pointer(nullptr));
|
||||
} else {
|
||||
inc_monitor_count = false;
|
||||
CALL_VM_NOCHECK(InterpreterRuntime::monitorenter(thread, monitor));
|
||||
if (HAS_PENDING_EXCEPTION)
|
||||
goto unwind_and_return;
|
||||
bool success = false;
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
markWord disp = lockee->mark().set_unlocked();
|
||||
monitor->lock()->set_displaced_header(disp);
|
||||
success = true;
|
||||
if (lockee->cas_set_mark(markWord::from_pointer(monitor), disp) != disp) {
|
||||
// Is it simple recursive case?
|
||||
if (thread->is_lock_owned((address) disp.clear_lock_bits().to_pointer())) {
|
||||
monitor->lock()->set_displaced_header(markWord::from_pointer(nullptr));
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
THREAD->inc_held_monitor_count();
|
||||
}
|
||||
}
|
||||
if (inc_monitor_count) {
|
||||
THREAD->inc_held_monitor_count();
|
||||
if (!success) {
|
||||
CALL_VM_NOCHECK(InterpreterRuntime::monitorenter(thread, monitor));
|
||||
if (HAS_PENDING_EXCEPTION)
|
||||
goto unwind_and_return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -757,8 +757,8 @@ JRT_BLOCK_ENTRY(void, Runtime1::monitorenter(JavaThread* current, oopDesc* obj,
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
lock->set_obj(obj);
|
||||
}
|
||||
assert(LockingMode == LM_LIGHTWEIGHT || obj == lock->obj(), "must match");
|
||||
SharedRuntime::monitor_enter_helper(obj, LockingMode == LM_LIGHTWEIGHT ? nullptr : lock->lock(), current);
|
||||
assert(obj == lock->obj(), "must match");
|
||||
SharedRuntime::monitor_enter_helper(obj, lock->lock(), current);
|
||||
JRT_END
|
||||
|
||||
|
||||
|
@ -71,7 +71,7 @@
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "runtime/stackWatermarkSet.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/synchronizer.inline.hpp"
|
||||
#include "runtime/threadCritical.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/checkedCast.hpp"
|
||||
@ -725,7 +725,6 @@ void InterpreterRuntime::resolve_get_put(Bytecodes::Code bytecode, int field_ind
|
||||
|
||||
//%note monitor_1
|
||||
JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* current, BasicObjectLock* elem))
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "Should call monitorenter_obj() when using the new lightweight locking");
|
||||
#ifdef ASSERT
|
||||
current->last_frame().interpreter_frame_verify_monitor(elem);
|
||||
#endif
|
||||
@ -740,23 +739,6 @@ JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* current, B
|
||||
#endif
|
||||
JRT_END
|
||||
|
||||
// NOTE: We provide a separate implementation for the new lightweight locking to workaround a limitation
|
||||
// of registers in x86_32. This entry point accepts an oop instead of a BasicObjectLock*.
|
||||
// The problem is that we would need to preserve the register that holds the BasicObjectLock,
|
||||
// but we are using that register to hold the thread. We don't have enough registers to
|
||||
// also keep the BasicObjectLock, but we don't really need it anyway, we only need
|
||||
// the object. See also InterpreterMacroAssembler::lock_object().
|
||||
// As soon as legacy stack-locking goes away we could remove the other monitorenter() entry
|
||||
// point, and only use oop-accepting entries (same for monitorexit() below).
|
||||
JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter_obj(JavaThread* current, oopDesc* obj))
|
||||
assert(LockingMode == LM_LIGHTWEIGHT, "Should call monitorenter() when not using the new lightweight locking");
|
||||
Handle h_obj(current, cast_to_oop(obj));
|
||||
assert(Universe::heap()->is_in_or_null(h_obj()),
|
||||
"must be null or an object");
|
||||
ObjectSynchronizer::enter(h_obj, nullptr, current);
|
||||
return;
|
||||
JRT_END
|
||||
|
||||
JRT_LEAF(void, InterpreterRuntime::monitorexit(BasicObjectLock* elem))
|
||||
oop obj = elem->obj();
|
||||
assert(Universe::heap()->is_in(obj), "must be an object");
|
||||
|
@ -53,7 +53,9 @@
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "prims/jvmtiThreadState.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/basicLock.inline.hpp"
|
||||
#include "runtime/frame.inline.hpp"
|
||||
#include "runtime/globals.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/orderAccess.hpp"
|
||||
@ -61,6 +63,7 @@
|
||||
#include "runtime/threadCritical.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/exceptions.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
/*
|
||||
@ -624,23 +627,28 @@ void BytecodeInterpreter::run(interpreterState istate) {
|
||||
BasicObjectLock* mon = &istate->monitor_base()[-1];
|
||||
mon->set_obj(rcvr);
|
||||
|
||||
// Traditional lightweight locking.
|
||||
markWord displaced = rcvr->mark().set_unlocked();
|
||||
mon->lock()->set_displaced_header(displaced);
|
||||
bool call_vm = (LockingMode == LM_MONITOR);
|
||||
bool inc_monitor_count = true;
|
||||
if (call_vm || rcvr->cas_set_mark(markWord::from_pointer(mon), displaced) != displaced) {
|
||||
// Is it simple recursive case?
|
||||
if (!call_vm && THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
|
||||
mon->lock()->set_displaced_header(markWord::from_pointer(nullptr));
|
||||
} else {
|
||||
inc_monitor_count = false;
|
||||
CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception);
|
||||
bool success = false;
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
// Traditional fast locking.
|
||||
markWord displaced = rcvr->mark().set_unlocked();
|
||||
mon->lock()->set_displaced_header(displaced);
|
||||
success = true;
|
||||
if (rcvr->cas_set_mark(markWord::from_pointer(mon), displaced) != displaced) {
|
||||
// Is it simple recursive case?
|
||||
if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
|
||||
mon->lock()->set_displaced_header(markWord::from_pointer(nullptr));
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
THREAD->inc_held_monitor_count();
|
||||
}
|
||||
}
|
||||
if (inc_monitor_count) {
|
||||
THREAD->inc_held_monitor_count();
|
||||
if (!success) {
|
||||
CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception);
|
||||
}
|
||||
|
||||
}
|
||||
THREAD->set_do_not_unlock_if_synchronized(false);
|
||||
|
||||
@ -723,23 +731,28 @@ void BytecodeInterpreter::run(interpreterState istate) {
|
||||
assert(entry->obj() == nullptr, "Frame manager didn't allocate the monitor");
|
||||
entry->set_obj(lockee);
|
||||
|
||||
// traditional lightweight locking
|
||||
markWord displaced = lockee->mark().set_unlocked();
|
||||
entry->lock()->set_displaced_header(displaced);
|
||||
bool call_vm = (LockingMode == LM_MONITOR);
|
||||
bool inc_monitor_count = true;
|
||||
if (call_vm || lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) {
|
||||
// Is it simple recursive case?
|
||||
if (!call_vm && THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
|
||||
entry->lock()->set_displaced_header(markWord::from_pointer(nullptr));
|
||||
} else {
|
||||
inc_monitor_count = false;
|
||||
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
|
||||
bool success = false;
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
// Traditional fast locking.
|
||||
markWord displaced = lockee->mark().set_unlocked();
|
||||
entry->lock()->set_displaced_header(displaced);
|
||||
success = true;
|
||||
if (lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) {
|
||||
// Is it simple recursive case?
|
||||
if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
|
||||
entry->lock()->set_displaced_header(markWord::from_pointer(nullptr));
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
THREAD->inc_held_monitor_count();
|
||||
}
|
||||
}
|
||||
if (inc_monitor_count) {
|
||||
THREAD->inc_held_monitor_count();
|
||||
if (!success) {
|
||||
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
|
||||
}
|
||||
|
||||
UPDATE_PC_AND_TOS(1, -1);
|
||||
goto run;
|
||||
}
|
||||
@ -1653,23 +1666,28 @@ run:
|
||||
if (entry != nullptr) {
|
||||
entry->set_obj(lockee);
|
||||
|
||||
// traditional lightweight locking
|
||||
markWord displaced = lockee->mark().set_unlocked();
|
||||
entry->lock()->set_displaced_header(displaced);
|
||||
bool call_vm = (LockingMode == LM_MONITOR);
|
||||
bool inc_monitor_count = true;
|
||||
if (call_vm || lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) {
|
||||
// Is it simple recursive case?
|
||||
if (!call_vm && THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
|
||||
entry->lock()->set_displaced_header(markWord::from_pointer(nullptr));
|
||||
} else {
|
||||
inc_monitor_count = false;
|
||||
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
|
||||
bool success = false;
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
// Traditional fast locking.
|
||||
markWord displaced = lockee->mark().set_unlocked();
|
||||
entry->lock()->set_displaced_header(displaced);
|
||||
success = true;
|
||||
if (lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) {
|
||||
// Is it simple recursive case?
|
||||
if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
|
||||
entry->lock()->set_displaced_header(markWord::from_pointer(nullptr));
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
THREAD->inc_held_monitor_count();
|
||||
}
|
||||
}
|
||||
if (inc_monitor_count) {
|
||||
THREAD->inc_held_monitor_count();
|
||||
if (!success) {
|
||||
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
|
||||
}
|
||||
|
||||
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
|
||||
} else {
|
||||
istate->set_msg(more_monitors);
|
||||
@ -1687,23 +1705,27 @@ run:
|
||||
while (most_recent != limit ) {
|
||||
if ((most_recent)->obj() == lockee) {
|
||||
BasicLock* lock = most_recent->lock();
|
||||
markWord header = lock->displaced_header();
|
||||
most_recent->set_obj(nullptr);
|
||||
|
||||
// If it isn't recursive we either must swap old header or call the runtime
|
||||
bool dec_monitor_count = true;
|
||||
bool call_vm = (LockingMode == LM_MONITOR);
|
||||
if (header.to_pointer() != nullptr || call_vm) {
|
||||
markWord old_header = markWord::encode(lock);
|
||||
if (call_vm || lockee->cas_set_mark(header, old_header) != old_header) {
|
||||
// restore object for the slow case
|
||||
most_recent->set_obj(lockee);
|
||||
dec_monitor_count = false;
|
||||
InterpreterRuntime::monitorexit(most_recent);
|
||||
bool success = false;
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
// If it isn't recursive we either must swap old header or call the runtime
|
||||
most_recent->set_obj(nullptr);
|
||||
success = true;
|
||||
markWord header = lock->displaced_header();
|
||||
if (header.to_pointer() != nullptr) {
|
||||
markWord old_header = markWord::encode(lock);
|
||||
if (lockee->cas_set_mark(header, old_header) != old_header) {
|
||||
// restore object for the slow case
|
||||
most_recent->set_obj(lockee);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
THREAD->dec_held_monitor_count();
|
||||
}
|
||||
}
|
||||
if (dec_monitor_count) {
|
||||
THREAD->dec_held_monitor_count();
|
||||
if (!success) {
|
||||
InterpreterRuntime::monitorexit(most_recent);
|
||||
}
|
||||
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
|
||||
}
|
||||
@ -3125,22 +3147,28 @@ run:
|
||||
oop lockee = end->obj();
|
||||
if (lockee != nullptr) {
|
||||
BasicLock* lock = end->lock();
|
||||
markWord header = lock->displaced_header();
|
||||
end->set_obj(nullptr);
|
||||
|
||||
// If it isn't recursive we either must swap old header or call the runtime
|
||||
bool dec_monitor_count = true;
|
||||
if (header.to_pointer() != nullptr) {
|
||||
markWord old_header = markWord::encode(lock);
|
||||
if (lockee->cas_set_mark(header, old_header) != old_header) {
|
||||
// restore object for the slow case
|
||||
end->set_obj(lockee);
|
||||
dec_monitor_count = false;
|
||||
InterpreterRuntime::monitorexit(end);
|
||||
bool success = false;
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
markWord header = lock->displaced_header();
|
||||
end->set_obj(nullptr);
|
||||
|
||||
// If it isn't recursive we either must swap old header or call the runtime
|
||||
success = true;
|
||||
if (header.to_pointer() != nullptr) {
|
||||
markWord old_header = markWord::encode(lock);
|
||||
if (lockee->cas_set_mark(header, old_header) != old_header) {
|
||||
// restore object for the slow case
|
||||
end->set_obj(lockee);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
THREAD->dec_held_monitor_count();
|
||||
}
|
||||
}
|
||||
if (dec_monitor_count) {
|
||||
THREAD->dec_held_monitor_count();
|
||||
if (!success) {
|
||||
InterpreterRuntime::monitorexit(end);
|
||||
}
|
||||
|
||||
// One error is plenty
|
||||
@ -3188,7 +3216,7 @@ run:
|
||||
illegal_state_oop = Handle(THREAD, THREAD->pending_exception());
|
||||
THREAD->clear_pending_exception();
|
||||
}
|
||||
} else if (LockingMode == LM_MONITOR) {
|
||||
} else if (LockingMode != LM_LEGACY) {
|
||||
InterpreterRuntime::monitorexit(base);
|
||||
if (THREAD->has_pending_exception()) {
|
||||
if (!suppress_error) illegal_state_oop = Handle(THREAD, THREAD->pending_exception());
|
||||
|
@ -151,7 +151,7 @@
|
||||
nonstatic_field(Array<Klass*>, _length, int) \
|
||||
nonstatic_field(Array<Klass*>, _data[0], Klass*) \
|
||||
\
|
||||
volatile_nonstatic_field(BasicLock, _displaced_header, markWord) \
|
||||
volatile_nonstatic_field(BasicLock, _metadata, uintptr_t) \
|
||||
\
|
||||
static_field(CodeCache, _low_bound, address) \
|
||||
static_field(CodeCache, _high_bound, address) \
|
||||
@ -241,6 +241,7 @@
|
||||
nonstatic_field(JavaThread, _stack_overflow_state._reserved_stack_activation, address) \
|
||||
nonstatic_field(JavaThread, _held_monitor_count, intx) \
|
||||
nonstatic_field(JavaThread, _lock_stack, LockStack) \
|
||||
nonstatic_field(JavaThread, _om_cache, OMCache) \
|
||||
JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_VTMS_transition, bool)) \
|
||||
JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_tmp_VTMS_transition, bool)) \
|
||||
JVMTI_ONLY(nonstatic_field(JavaThread, _is_disable_suspend, bool)) \
|
||||
@ -531,6 +532,8 @@
|
||||
\
|
||||
declare_constant_with_value("CardTable::dirty_card", CardTable::dirty_card_val()) \
|
||||
declare_constant_with_value("LockStack::_end_offset", LockStack::end_offset()) \
|
||||
declare_constant_with_value("OMCache::oop_to_oop_difference", OMCache::oop_to_oop_difference()) \
|
||||
declare_constant_with_value("OMCache::oop_to_monitor_difference", OMCache::oop_to_monitor_difference()) \
|
||||
\
|
||||
declare_constant(CodeInstaller::VERIFIED_ENTRY) \
|
||||
declare_constant(CodeInstaller::UNVERIFIED_ENTRY) \
|
||||
|
@ -130,6 +130,7 @@ class outputStream;
|
||||
LOG_TAG(module) \
|
||||
LOG_TAG(monitorinflation) \
|
||||
LOG_TAG(monitormismatch) \
|
||||
LOG_TAG(monitortable) \
|
||||
LOG_TAG(native) \
|
||||
LOG_TAG(nestmates) \
|
||||
LOG_TAG(nmethod) \
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "oops/markWord.hpp"
|
||||
#include "runtime/basicLock.inline.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
@ -67,7 +68,7 @@ void markWord::print_on(outputStream* st, bool print_monitor_info) const {
|
||||
} else if (has_monitor()) { // last bits = 10
|
||||
// have to check has_monitor() before is_locked()
|
||||
st->print(" monitor(" INTPTR_FORMAT ")=", value());
|
||||
if (print_monitor_info) {
|
||||
if (print_monitor_info && !UseObjectMonitorTable) {
|
||||
ObjectMonitor* mon = monitor();
|
||||
if (mon == nullptr) {
|
||||
st->print("null (this should never be seen!)");
|
||||
|
@ -197,13 +197,17 @@ class markWord {
|
||||
}
|
||||
ObjectMonitor* monitor() const {
|
||||
assert(has_monitor(), "check");
|
||||
assert(!UseObjectMonitorTable, "Lightweight locking with OM table does not use markWord for monitors");
|
||||
// Use xor instead of &~ to provide one extra tag-bit check.
|
||||
return (ObjectMonitor*) (value() ^ monitor_value);
|
||||
}
|
||||
bool has_displaced_mark_helper() const {
|
||||
intptr_t lockbits = value() & lock_mask_in_place;
|
||||
return LockingMode == LM_LIGHTWEIGHT ? lockbits == monitor_value // monitor?
|
||||
: (lockbits & unlocked_value) == 0; // monitor | stack-locked?
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
return !UseObjectMonitorTable && lockbits == monitor_value;
|
||||
}
|
||||
// monitor (0b10) | stack-locked (0b00)?
|
||||
return (lockbits & unlocked_value) == 0;
|
||||
}
|
||||
markWord displaced_mark_helper() const;
|
||||
void set_displaced_mark_helper(markWord m) const;
|
||||
@ -223,10 +227,15 @@ class markWord {
|
||||
return from_pointer(lock);
|
||||
}
|
||||
static markWord encode(ObjectMonitor* monitor) {
|
||||
assert(!UseObjectMonitorTable, "Lightweight locking with OM table does not use markWord for monitors");
|
||||
uintptr_t tmp = (uintptr_t) monitor;
|
||||
return markWord(tmp | monitor_value);
|
||||
}
|
||||
|
||||
markWord set_has_monitor() const {
|
||||
return markWord((value() & ~lock_mask_in_place) | monitor_value);
|
||||
}
|
||||
|
||||
// used to encode pointers during GC
|
||||
markWord clear_lock_bits() const { return markWord(value() & ~lock_mask_in_place); }
|
||||
|
||||
|
@ -103,6 +103,7 @@ private:
|
||||
Register _mark;
|
||||
Register _t;
|
||||
Register _thread;
|
||||
Label _slow_path;
|
||||
Label _push_and_slow_path;
|
||||
Label _check_successor;
|
||||
Label _unlocked_continuation;
|
||||
@ -111,6 +112,7 @@ public:
|
||||
_obj(obj), _mark(mark), _t(t), _thread(thread) {}
|
||||
int max_size() const;
|
||||
void emit(C2_MacroAssembler& masm);
|
||||
Label& slow_path() { return _slow_path; }
|
||||
Label& push_and_slow_path() { return _push_and_slow_path; }
|
||||
Label& check_successor() { return _check_successor; }
|
||||
Label& unlocked_continuation() { return _unlocked_continuation; }
|
||||
|
@ -4604,21 +4604,23 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
|
||||
Node* no_ctrl = nullptr;
|
||||
Node* header = make_load(no_ctrl, header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
|
||||
|
||||
// Test the header to see if it is safe to read w.r.t. locking.
|
||||
Node *lock_mask = _gvn.MakeConX(markWord::lock_mask_in_place);
|
||||
Node *lmasked_header = _gvn.transform(new AndXNode(header, lock_mask));
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
Node *monitor_val = _gvn.MakeConX(markWord::monitor_value);
|
||||
Node *chk_monitor = _gvn.transform(new CmpXNode(lmasked_header, monitor_val));
|
||||
Node *test_monitor = _gvn.transform(new BoolNode(chk_monitor, BoolTest::eq));
|
||||
if (!UseObjectMonitorTable) {
|
||||
// Test the header to see if it is safe to read w.r.t. locking.
|
||||
Node *lock_mask = _gvn.MakeConX(markWord::lock_mask_in_place);
|
||||
Node *lmasked_header = _gvn.transform(new AndXNode(header, lock_mask));
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
Node *monitor_val = _gvn.MakeConX(markWord::monitor_value);
|
||||
Node *chk_monitor = _gvn.transform(new CmpXNode(lmasked_header, monitor_val));
|
||||
Node *test_monitor = _gvn.transform(new BoolNode(chk_monitor, BoolTest::eq));
|
||||
|
||||
generate_slow_guard(test_monitor, slow_region);
|
||||
} else {
|
||||
Node *unlocked_val = _gvn.MakeConX(markWord::unlocked_value);
|
||||
Node *chk_unlocked = _gvn.transform(new CmpXNode(lmasked_header, unlocked_val));
|
||||
Node *test_not_unlocked = _gvn.transform(new BoolNode(chk_unlocked, BoolTest::ne));
|
||||
generate_slow_guard(test_monitor, slow_region);
|
||||
} else {
|
||||
Node *unlocked_val = _gvn.MakeConX(markWord::unlocked_value);
|
||||
Node *chk_unlocked = _gvn.transform(new CmpXNode(lmasked_header, unlocked_val));
|
||||
Node *test_not_unlocked = _gvn.transform(new BoolNode(chk_unlocked, BoolTest::ne));
|
||||
|
||||
generate_slow_guard(test_not_unlocked, slow_region);
|
||||
generate_slow_guard(test_not_unlocked, slow_region);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the hash value and check to see that it has been properly assigned.
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "runtime/osThread.hpp"
|
||||
#include "runtime/signature.hpp"
|
||||
#include "runtime/stackWatermarkSet.inline.hpp"
|
||||
#include "runtime/synchronizer.inline.hpp"
|
||||
#include "runtime/threads.hpp"
|
||||
#include "runtime/threadSMR.inline.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
@ -1465,7 +1466,6 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
|
||||
ThreadsListHandle tlh(current_thread);
|
||||
JavaThread *owning_thread = nullptr;
|
||||
ObjectMonitor *mon = nullptr;
|
||||
jvmtiMonitorUsage ret = {
|
||||
nullptr, 0, 0, nullptr, 0, nullptr
|
||||
};
|
||||
@ -1495,9 +1495,11 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
|
||||
ResourceMark rm(current_thread);
|
||||
GrowableArray<JavaThread*>* wantList = nullptr;
|
||||
|
||||
if (mark.has_monitor()) {
|
||||
mon = mark.monitor();
|
||||
assert(mon != nullptr, "must have monitor");
|
||||
ObjectMonitor* mon = mark.has_monitor()
|
||||
? ObjectSynchronizer::read_monitor(current_thread, hobj(), mark)
|
||||
: nullptr;
|
||||
|
||||
if (mon != nullptr) {
|
||||
// this object has a heavyweight monitor
|
||||
nWant = mon->contentions(); // # of threads contending for monitor entry, but not re-entry
|
||||
nWait = mon->waiters(); // # of threads waiting for notification,
|
||||
|
@ -1816,8 +1816,18 @@ bool Arguments::check_vm_args_consistency() {
|
||||
FLAG_SET_CMDLINE(LockingMode, LM_LEGACY);
|
||||
warning("New lightweight locking not supported on this platform");
|
||||
}
|
||||
if (UseObjectMonitorTable) {
|
||||
FLAG_SET_CMDLINE(UseObjectMonitorTable, false);
|
||||
warning("UseObjectMonitorTable not supported on this platform");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (UseObjectMonitorTable && LockingMode != LM_LIGHTWEIGHT) {
|
||||
// ObjectMonitorTable requires lightweight locking.
|
||||
FLAG_SET_CMDLINE(UseObjectMonitorTable, false);
|
||||
warning("UseObjectMonitorTable requires LM_LIGHTWEIGHT");
|
||||
}
|
||||
|
||||
#if !defined(X86) && !defined(AARCH64) && !defined(PPC64) && !defined(RISCV64) && !defined(S390)
|
||||
if (LockingMode == LM_MONITOR) {
|
||||
jio_fprintf(defaultStream::error_stream(),
|
||||
|
@ -24,16 +24,24 @@
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/basicLock.hpp"
|
||||
#include "runtime/basicLock.inline.hpp"
|
||||
#include "runtime/objectMonitor.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
|
||||
void BasicLock::print_on(outputStream* st, oop owner) const {
|
||||
st->print("monitor");
|
||||
markWord mark_word = displaced_header();
|
||||
if (mark_word.value() != 0) {
|
||||
// Print monitor info if there's an owning oop and it refers to this BasicLock.
|
||||
bool print_monitor_info = (owner != nullptr) && (owner->mark() == markWord::from_pointer((void*)this));
|
||||
mark_word.print_on(st, print_monitor_info);
|
||||
if (UseObjectMonitorTable) {
|
||||
ObjectMonitor* mon = object_monitor_cache();
|
||||
if (mon != nullptr) {
|
||||
mon->print_on(st);
|
||||
}
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
markWord mark_word = displaced_header();
|
||||
if (mark_word.value() != 0) {
|
||||
// Print monitor info if there's an owning oop and it refers to this BasicLock.
|
||||
bool print_monitor_info = (owner != nullptr) && (owner->mark() == markWord::from_pointer((void*)this));
|
||||
mark_word.print_on(st, print_monitor_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,10 +90,15 @@ void BasicLock::move_to(oop obj, BasicLock* dest) {
|
||||
// we can find any flavor mark in the displaced mark.
|
||||
}
|
||||
dest->set_displaced_header(displaced_header());
|
||||
} else if (UseObjectMonitorTable) {
|
||||
// Preserve the ObjectMonitor*, the cache is cleared when a box is reused
|
||||
// and only read while the lock is held, so no stale ObjectMonitor* is
|
||||
// encountered.
|
||||
dest->set_object_monitor_cache(object_monitor_cache());
|
||||
}
|
||||
#ifdef ASSERT
|
||||
else {
|
||||
dest->set_displaced_header(markWord(badDispHeaderDeopt));
|
||||
dest->set_bad_metadata_deopt();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -28,30 +28,46 @@
|
||||
#include "oops/markWord.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/handles.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/sizes.hpp"
|
||||
|
||||
class BasicLock {
|
||||
friend class VMStructs;
|
||||
friend class JVMCIVMStructs;
|
||||
private:
|
||||
// * For LM_MONITOR
|
||||
// Unused.
|
||||
// * For LM_LEGACY
|
||||
// This is either the actual displaced header from a locked object, or
|
||||
// a sentinel zero value indicating a recursive stack-lock.
|
||||
volatile markWord _displaced_header;
|
||||
public:
|
||||
markWord displaced_header() const {
|
||||
return Atomic::load(&_displaced_header);
|
||||
}
|
||||
// * For LM_LIGHTWEIGHT
|
||||
// Used as a cache of the ObjectMonitor* used when locking. Must either
|
||||
// be nullptr or the ObjectMonitor* used when locking.
|
||||
volatile uintptr_t _metadata;
|
||||
|
||||
void set_displaced_header(markWord header) {
|
||||
Atomic::store(&_displaced_header, header);
|
||||
}
|
||||
uintptr_t get_metadata() const { return Atomic::load(&_metadata); }
|
||||
void set_metadata(uintptr_t value) { Atomic::store(&_metadata, value); }
|
||||
static int metadata_offset_in_bytes() { return (int)offset_of(BasicLock, _metadata); }
|
||||
|
||||
public:
|
||||
// LM_MONITOR
|
||||
void set_bad_metadata_deopt() { set_metadata(badDispHeaderDeopt); }
|
||||
|
||||
// LM_LEGACY
|
||||
inline markWord displaced_header() const;
|
||||
inline void set_displaced_header(markWord header);
|
||||
static int displaced_header_offset_in_bytes() { return metadata_offset_in_bytes(); }
|
||||
|
||||
// LM_LIGHTWEIGHT
|
||||
inline ObjectMonitor* object_monitor_cache() const;
|
||||
inline void clear_object_monitor_cache();
|
||||
inline void set_object_monitor_cache(ObjectMonitor* mon);
|
||||
static int object_monitor_cache_offset_in_bytes() { return metadata_offset_in_bytes(); }
|
||||
|
||||
void print_on(outputStream* st, oop owner) const;
|
||||
|
||||
// move a basic lock (used during deoptimization)
|
||||
void move_to(oop obj, BasicLock* dest);
|
||||
|
||||
static int displaced_header_offset_in_bytes() { return (int)offset_of(BasicLock, _displaced_header); }
|
||||
};
|
||||
|
||||
// A BasicObjectLock associates a specific Java object with a BasicLock.
|
||||
|
62
src/hotspot/share/runtime/basicLock.inline.hpp
Normal file
62
src/hotspot/share/runtime/basicLock.inline.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_RUNTIME_BASICLOCK_INLINE_HPP
|
||||
#define SHARE_RUNTIME_BASICLOCK_INLINE_HPP
|
||||
|
||||
#include "runtime/basicLock.hpp"
|
||||
|
||||
inline markWord BasicLock::displaced_header() const {
|
||||
assert(LockingMode == LM_LEGACY, "must be");
|
||||
return markWord(get_metadata());
|
||||
}
|
||||
|
||||
inline void BasicLock::set_displaced_header(markWord header) {
|
||||
assert(LockingMode == LM_LEGACY, "must be");
|
||||
Atomic::store(&_metadata, header.value());
|
||||
}
|
||||
|
||||
inline ObjectMonitor* BasicLock::object_monitor_cache() const {
|
||||
assert(UseObjectMonitorTable, "must be");
|
||||
#if defined(X86) || defined(AARCH64)
|
||||
return reinterpret_cast<ObjectMonitor*>(get_metadata());
|
||||
#else
|
||||
// Other platforms do not make use of the cache yet,
|
||||
// and are not as careful with maintaining the invariant
|
||||
// that the metadata either is nullptr or ObjectMonitor*.
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void BasicLock::clear_object_monitor_cache() {
|
||||
assert(UseObjectMonitorTable, "must be");
|
||||
set_metadata(0);
|
||||
}
|
||||
|
||||
inline void BasicLock::set_object_monitor_cache(ObjectMonitor* mon) {
|
||||
assert(UseObjectMonitorTable, "must be");
|
||||
set_metadata(reinterpret_cast<uintptr_t>(mon));
|
||||
}
|
||||
|
||||
#endif // SHARE_RUNTIME_BASICLOCK_INLINE_HPP
|
@ -63,6 +63,7 @@
|
||||
#include "prims/methodHandles.hpp"
|
||||
#include "prims/vectorSupport.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/basicLock.inline.hpp"
|
||||
#include "runtime/continuation.hpp"
|
||||
#include "runtime/continuationEntry.inline.hpp"
|
||||
#include "runtime/deoptimization.hpp"
|
||||
@ -75,6 +76,8 @@
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/jniHandles.inline.hpp"
|
||||
#include "runtime/keepStackGCProcessed.hpp"
|
||||
#include "runtime/lightweightSynchronizer.hpp"
|
||||
#include "runtime/lockStack.inline.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "runtime/osThread.hpp"
|
||||
#include "runtime/safepointVerifiers.hpp"
|
||||
@ -84,7 +87,7 @@
|
||||
#include "runtime/stackValue.hpp"
|
||||
#include "runtime/stackWatermarkSet.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/synchronizer.inline.hpp"
|
||||
#include "runtime/threadSMR.hpp"
|
||||
#include "runtime/threadWXSetters.inline.hpp"
|
||||
#include "runtime/vframe.hpp"
|
||||
@ -1634,22 +1637,37 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArray<MonitorInf
|
||||
ObjectMonitor* waiting_monitor = deoptee_thread->current_waiting_monitor();
|
||||
if (waiting_monitor != nullptr && waiting_monitor->object() == obj()) {
|
||||
assert(fr.is_deoptimized_frame(), "frame must be scheduled for deoptimization");
|
||||
mon_info->lock()->set_displaced_header(markWord::unused_mark());
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
mon_info->lock()->set_displaced_header(markWord::unused_mark());
|
||||
} else if (UseObjectMonitorTable) {
|
||||
mon_info->lock()->clear_object_monitor_cache();
|
||||
}
|
||||
#ifdef ASSERT
|
||||
else {
|
||||
assert(LockingMode == LM_MONITOR || !UseObjectMonitorTable, "must be");
|
||||
mon_info->lock()->set_bad_metadata_deopt();
|
||||
}
|
||||
#endif
|
||||
JvmtiDeferredUpdates::inc_relock_count_after_wait(deoptee_thread);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
BasicLock* lock = mon_info->lock();
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
// We have lost information about the correct state of the lock stack.
|
||||
// Inflate the locks instead. Enter then inflate to avoid races with
|
||||
// deflation.
|
||||
ObjectSynchronizer::enter_for(obj, nullptr, deoptee_thread);
|
||||
// Entering may create an invalid lock stack. Inflate the lock if it
|
||||
// was fast_locked to restore the valid lock stack.
|
||||
ObjectSynchronizer::enter_for(obj, lock, deoptee_thread);
|
||||
if (deoptee_thread->lock_stack().contains(obj())) {
|
||||
LightweightSynchronizer::inflate_fast_locked_object(obj(), ObjectSynchronizer::InflateCause::inflate_cause_vm_internal,
|
||||
deoptee_thread, thread);
|
||||
}
|
||||
assert(mon_info->owner()->is_locked(), "object must be locked now");
|
||||
ObjectMonitor* mon = ObjectSynchronizer::inflate_for(deoptee_thread, obj(), ObjectSynchronizer::inflate_cause_vm_internal);
|
||||
assert(mon->owner() == deoptee_thread, "must be");
|
||||
assert(obj->mark().has_monitor(), "must be");
|
||||
assert(!deoptee_thread->lock_stack().contains(obj()), "must be");
|
||||
assert(ObjectSynchronizer::read_monitor(thread, obj(), obj->mark())->owner() == deoptee_thread, "must be");
|
||||
} else {
|
||||
BasicLock* lock = mon_info->lock();
|
||||
ObjectSynchronizer::enter_for(obj, lock, deoptee_thread);
|
||||
assert(mon_info->owner()->is_locked(), "object must be locked now");
|
||||
}
|
||||
|
@ -1956,6 +1956,17 @@ const int ObjectAlignmentInBytes = 8;
|
||||
"2: monitors & new lightweight locking (LM_LIGHTWEIGHT, default)") \
|
||||
range(0, 2) \
|
||||
\
|
||||
product(bool, UseObjectMonitorTable, false, DIAGNOSTIC, \
|
||||
"With Lightweight Locking mode, use a table to record inflated " \
|
||||
"monitors rather than the first word of the object.") \
|
||||
\
|
||||
product(int, LightweightFastLockingSpins, 13, DIAGNOSTIC, \
|
||||
"Specifies the number of times lightweight fast locking will " \
|
||||
"attempt to CAS the markWord before inflating. Between each " \
|
||||
"CAS it will spin for exponentially more time, resulting in " \
|
||||
"a total number of spins on the order of O(2^value)") \
|
||||
range(1, 30) \
|
||||
\
|
||||
product(uint, TrimNativeHeapInterval, 0, \
|
||||
"Interval, in ms, at which the JVM will trim the native heap if " \
|
||||
"the platform supports that. Lower values will reclaim memory " \
|
||||
|
@ -504,7 +504,8 @@ JavaThread::JavaThread(MEMFLAGS flags) :
|
||||
|
||||
_SleepEvent(ParkEvent::Allocate(this)),
|
||||
|
||||
_lock_stack(this) {
|
||||
_lock_stack(this),
|
||||
_om_cache(this) {
|
||||
set_jni_functions(jni_functions());
|
||||
|
||||
#if INCLUDE_JVMCI
|
||||
@ -803,6 +804,8 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
|
||||
elapsedTimer _timer_exit_phase3;
|
||||
elapsedTimer _timer_exit_phase4;
|
||||
|
||||
om_clear_monitor_cache();
|
||||
|
||||
if (log_is_enabled(Debug, os, thread, timer)) {
|
||||
_timer_exit_phase1.start();
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ class JvmtiSampledObjectAllocEventCollector;
|
||||
class JvmtiThreadState;
|
||||
|
||||
class Metadata;
|
||||
class ObjectMonitor;
|
||||
class OopHandleList;
|
||||
class OopStorage;
|
||||
class OSThread;
|
||||
@ -1165,6 +1166,7 @@ public:
|
||||
|
||||
private:
|
||||
LockStack _lock_stack;
|
||||
OMCache _om_cache;
|
||||
|
||||
public:
|
||||
LockStack& lock_stack() { return _lock_stack; }
|
||||
@ -1176,6 +1178,13 @@ public:
|
||||
static ByteSize lock_stack_top_offset() { return lock_stack_offset() + LockStack::top_offset(); }
|
||||
static ByteSize lock_stack_base_offset() { return lock_stack_offset() + LockStack::base_offset(); }
|
||||
|
||||
static ByteSize om_cache_offset() { return byte_offset_of(JavaThread, _om_cache); }
|
||||
static ByteSize om_cache_oops_offset() { return om_cache_offset() + OMCache::entries_offset(); }
|
||||
|
||||
void om_set_monitor_cache(ObjectMonitor* monitor);
|
||||
void om_clear_monitor_cache();
|
||||
ObjectMonitor* om_get_from_monitor_cache(oop obj);
|
||||
|
||||
static OopStorage* thread_oop_storage();
|
||||
|
||||
static void verify_cross_modify_fence_failure(JavaThread *thread) PRODUCT_RETURN;
|
||||
|
@ -36,7 +36,9 @@
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/continuation.hpp"
|
||||
#include "runtime/continuationEntry.inline.hpp"
|
||||
#include "runtime/lockStack.inline.hpp"
|
||||
#include "runtime/nonJavaThread.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "runtime/orderAccess.hpp"
|
||||
#include "runtime/safepoint.hpp"
|
||||
|
||||
@ -239,4 +241,25 @@ inline InstanceKlass* JavaThread::class_to_be_initialized() const {
|
||||
return _class_to_be_initialized;
|
||||
}
|
||||
|
||||
inline void JavaThread::om_set_monitor_cache(ObjectMonitor* monitor) {
|
||||
assert(UseObjectMonitorTable, "must be");
|
||||
assert(monitor != nullptr, "use om_clear_monitor_cache to clear");
|
||||
assert(this == current() || monitor->owner_raw() == this, "only add owned monitors for other threads");
|
||||
assert(this == current() || is_obj_deopt_suspend(), "thread must not run concurrently");
|
||||
|
||||
_om_cache.set_monitor(monitor);
|
||||
}
|
||||
|
||||
inline void JavaThread::om_clear_monitor_cache() {
|
||||
if (UseObjectMonitorTable) {
|
||||
_om_cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
inline ObjectMonitor* JavaThread::om_get_from_monitor_cache(oop obj) {
|
||||
assert(obj != nullptr, "do not look for null objects");
|
||||
assert(this == current(), "only get own thread locals");
|
||||
return _om_cache.get_monitor(obj);
|
||||
}
|
||||
|
||||
#endif // SHARE_RUNTIME_JAVATHREAD_INLINE_HPP
|
||||
|
1223
src/hotspot/share/runtime/lightweightSynchronizer.cpp
Normal file
1223
src/hotspot/share/runtime/lightweightSynchronizer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
80
src/hotspot/share/runtime/lightweightSynchronizer.hpp
Normal file
80
src/hotspot/share/runtime/lightweightSynchronizer.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_RUNTIME_LIGHTWEIGHTSYNCHRONIZER_HPP
|
||||
#define SHARE_RUNTIME_LIGHTWEIGHTSYNCHRONIZER_HPP
|
||||
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/objectMonitor.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
|
||||
class ObjectMonitorTable;
|
||||
|
||||
class LightweightSynchronizer : AllStatic {
|
||||
private:
|
||||
static ObjectMonitor* get_or_insert_monitor_from_table(oop object, JavaThread* current, bool* inserted);
|
||||
static ObjectMonitor* get_or_insert_monitor(oop object, JavaThread* current, ObjectSynchronizer::InflateCause cause);
|
||||
|
||||
static ObjectMonitor* add_monitor(JavaThread* current, ObjectMonitor* monitor, oop obj);
|
||||
static bool remove_monitor(Thread* current, ObjectMonitor* monitor, oop obj);
|
||||
|
||||
static void deflate_mark_word(oop object);
|
||||
|
||||
static void ensure_lock_stack_space(JavaThread* current);
|
||||
|
||||
class CacheSetter;
|
||||
class LockStackInflateContendedLocks;
|
||||
class VerifyThreadState;
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
|
||||
static bool needs_resize();
|
||||
static bool resize_table(JavaThread* current);
|
||||
|
||||
private:
|
||||
static inline bool fast_lock_try_enter(oop obj, LockStack& lock_stack, JavaThread* current);
|
||||
static bool fast_lock_spin_enter(oop obj, LockStack& lock_stack, JavaThread* current, bool observed_deflation);
|
||||
|
||||
public:
|
||||
static void enter_for(Handle obj, BasicLock* lock, JavaThread* locking_thread);
|
||||
static void enter(Handle obj, BasicLock* lock, JavaThread* current);
|
||||
static void exit(oop object, JavaThread* current);
|
||||
|
||||
static ObjectMonitor* inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* inflating_thread, Thread* current);
|
||||
static ObjectMonitor* inflate_locked_or_imse(oop object, ObjectSynchronizer::InflateCause cause, TRAPS);
|
||||
static ObjectMonitor* inflate_fast_locked_object(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current);
|
||||
static ObjectMonitor* inflate_and_enter(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current);
|
||||
|
||||
static void deflate_monitor(Thread* current, oop obj, ObjectMonitor* monitor);
|
||||
|
||||
static ObjectMonitor* get_monitor_from_table(Thread* current, oop obj);
|
||||
|
||||
static bool contains_monitor(Thread* current, ObjectMonitor* monitor);
|
||||
|
||||
static bool quick_enter(oop obj, BasicLock* Lock, JavaThread* current);
|
||||
};
|
||||
|
||||
#endif // SHARE_RUNTIME_LIGHTWEIGHTSYNCHRONIZER_HPP
|
@ -29,17 +29,20 @@
|
||||
#include "oops/markWord.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/globals.hpp"
|
||||
#include "runtime/javaThread.inline.hpp"
|
||||
#include "runtime/lockStack.inline.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "runtime/safepoint.hpp"
|
||||
#include "runtime/stackWatermark.hpp"
|
||||
#include "runtime/stackWatermarkSet.inline.hpp"
|
||||
#include "runtime/synchronizer.inline.hpp"
|
||||
#include "runtime/thread.hpp"
|
||||
#include "utilities/copy.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
#include "utilities/sizes.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
@ -114,3 +117,11 @@ void LockStack::print_on(outputStream* st) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OMCache::OMCache(JavaThread* jt) : _entries() {
|
||||
STATIC_ASSERT(std::is_standard_layout<OMCache>::value);
|
||||
STATIC_ASSERT(std::is_standard_layout<OMCache::OMCacheEntry>::value);
|
||||
STATIC_ASSERT(offsetof(OMCache, _null_sentinel) == offsetof(OMCache, _entries) +
|
||||
offsetof(OMCache::OMCacheEntry, _oop) +
|
||||
OMCache::CAPACITY * in_bytes(oop_to_oop_difference()));
|
||||
}
|
||||
|
@ -32,18 +32,20 @@
|
||||
#include "utilities/sizes.hpp"
|
||||
|
||||
class JavaThread;
|
||||
class ObjectMonitor;
|
||||
class OopClosure;
|
||||
class outputStream;
|
||||
template<typename>
|
||||
class GrowableArray;
|
||||
class Thread;
|
||||
|
||||
class LockStack {
|
||||
friend class LockStackTest;
|
||||
friend class VMStructs;
|
||||
JVMCI_ONLY(friend class JVMCIVMStructs;)
|
||||
public:
|
||||
public:
|
||||
static const int CAPACITY = 8;
|
||||
private:
|
||||
private:
|
||||
|
||||
// TODO: It would be very useful if JavaThread::lock_stack_offset() and friends were constexpr,
|
||||
// but this is currently not the case because we're using offset_of() which is non-constexpr,
|
||||
@ -73,7 +75,7 @@ private:
|
||||
// Given an offset (in bytes) calculate the index into the lock-stack.
|
||||
static inline int to_index(uint32_t offset);
|
||||
|
||||
public:
|
||||
public:
|
||||
static ByteSize top_offset() { return byte_offset_of(LockStack, _top); }
|
||||
static ByteSize base_offset() { return byte_offset_of(LockStack, _base); }
|
||||
|
||||
@ -123,4 +125,29 @@ public:
|
||||
void print_on(outputStream* st);
|
||||
};
|
||||
|
||||
class OMCache {
|
||||
friend class VMStructs;
|
||||
public:
|
||||
static constexpr int CAPACITY = 8;
|
||||
|
||||
private:
|
||||
struct OMCacheEntry {
|
||||
oop _oop = nullptr;
|
||||
ObjectMonitor* _monitor = nullptr;
|
||||
} _entries[CAPACITY];
|
||||
const oop _null_sentinel = nullptr;
|
||||
|
||||
public:
|
||||
static ByteSize entries_offset() { return byte_offset_of(OMCache, _entries); }
|
||||
static constexpr ByteSize oop_to_oop_difference() { return in_ByteSize(sizeof(OMCacheEntry)); }
|
||||
static constexpr ByteSize oop_to_monitor_difference() { return in_ByteSize(sizeof(oop)); }
|
||||
|
||||
explicit OMCache(JavaThread* jt);
|
||||
|
||||
inline ObjectMonitor* get_monitor(oop o);
|
||||
inline void set_monitor(ObjectMonitor* monitor);
|
||||
inline void clear();
|
||||
|
||||
};
|
||||
|
||||
#endif // SHARE_RUNTIME_LOCKSTACK_HPP
|
||||
|
@ -31,6 +31,8 @@
|
||||
|
||||
#include "memory/iterator.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/lightweightSynchronizer.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "runtime/safepoint.hpp"
|
||||
#include "runtime/stackWatermark.hpp"
|
||||
#include "runtime/stackWatermarkSet.inline.hpp"
|
||||
@ -222,4 +224,54 @@ inline void LockStack::oops_do(OopClosure* cl) {
|
||||
verify("post-oops-do");
|
||||
}
|
||||
|
||||
inline void OMCache::set_monitor(ObjectMonitor *monitor) {
|
||||
const int end = OMCache::CAPACITY - 1;
|
||||
|
||||
oop obj = monitor->object_peek();
|
||||
assert(obj != nullptr, "must be alive");
|
||||
assert(monitor == LightweightSynchronizer::get_monitor_from_table(JavaThread::current(), obj), "must exist in table");
|
||||
|
||||
OMCacheEntry to_insert = {obj, monitor};
|
||||
|
||||
for (int i = 0; i < end; ++i) {
|
||||
if (_entries[i]._oop == obj ||
|
||||
_entries[i]._monitor == nullptr ||
|
||||
_entries[i]._monitor->is_being_async_deflated()) {
|
||||
// Use stale slot.
|
||||
_entries[i] = to_insert;
|
||||
return;
|
||||
}
|
||||
// Swap with the most recent value.
|
||||
::swap(to_insert, _entries[i]);
|
||||
}
|
||||
_entries[end] = to_insert;
|
||||
}
|
||||
|
||||
inline ObjectMonitor* OMCache::get_monitor(oop o) {
|
||||
for (int i = 0; i < CAPACITY; ++i) {
|
||||
if (_entries[i]._oop == o) {
|
||||
assert(_entries[i]._monitor != nullptr, "monitor must exist");
|
||||
if (_entries[i]._monitor->is_being_async_deflated()) {
|
||||
// Bad monitor
|
||||
// Shift down rest
|
||||
for (; i < CAPACITY - 1; ++i) {
|
||||
_entries[i] = _entries[i + 1];
|
||||
}
|
||||
// Clear end
|
||||
_entries[i] = {};
|
||||
return nullptr;
|
||||
}
|
||||
return _entries[i]._monitor;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void OMCache::clear() {
|
||||
for (size_t i = 0; i < CAPACITY; ++i) {
|
||||
// Clear
|
||||
_entries[i] = {};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SHARE_RUNTIME_LOCKSTACK_INLINE_HPP
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/javaThread.inline.hpp"
|
||||
#include "runtime/lightweightSynchronizer.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/objectMonitor.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
@ -53,6 +54,7 @@
|
||||
#include "runtime/safepointMechanism.inline.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "services/threadService.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/dtrace.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
@ -246,7 +248,7 @@ static void check_object_context() {
|
||||
}
|
||||
|
||||
ObjectMonitor::ObjectMonitor(oop object) :
|
||||
_header(markWord::zero()),
|
||||
_metadata(0),
|
||||
_object(_oop_storage, object),
|
||||
_owner(nullptr),
|
||||
_previous_owner_tid(0),
|
||||
@ -272,10 +274,6 @@ oop ObjectMonitor::object() const {
|
||||
return _object.resolve();
|
||||
}
|
||||
|
||||
oop ObjectMonitor::object_peek() const {
|
||||
return _object.peek();
|
||||
}
|
||||
|
||||
void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) {
|
||||
if (current->is_suspended()) {
|
||||
_om->_recursions = 0;
|
||||
@ -297,109 +295,147 @@ void ObjectMonitor::ClearSuccOnSuspend::operator()(JavaThread* current) {
|
||||
}
|
||||
}
|
||||
|
||||
#define assert_mark_word_consistency() \
|
||||
assert(UseObjectMonitorTable || object()->mark() == markWord::encode(this), \
|
||||
"object mark must match encoded this: mark=" INTPTR_FORMAT \
|
||||
", encoded this=" INTPTR_FORMAT, object()->mark().value(), \
|
||||
markWord::encode(this).value());
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Enter support
|
||||
|
||||
bool ObjectMonitor::enter_for(JavaThread* locking_thread) {
|
||||
bool ObjectMonitor::enter_is_async_deflating() {
|
||||
if (is_being_async_deflated()) {
|
||||
if (!UseObjectMonitorTable) {
|
||||
const oop l_object = object();
|
||||
if (l_object != nullptr) {
|
||||
// Attempt to restore the header/dmw to the object's header so that
|
||||
// we only retry once if the deflater thread happens to be slow.
|
||||
install_displaced_markword_in_object(l_object);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ObjectMonitor::enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark) {
|
||||
// Used by ObjectSynchronizer::enter_for to enter for another thread.
|
||||
// The monitor is private to or already owned by locking_thread which must be suspended.
|
||||
// So this code may only contend with deflation.
|
||||
assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be");
|
||||
assert(contention_mark._monitor == this, "must be");
|
||||
assert(!is_being_async_deflated(), "must be");
|
||||
|
||||
// Block out deflation as soon as possible.
|
||||
add_to_contentions(1);
|
||||
|
||||
void* prev_owner = try_set_owner_from(nullptr, locking_thread);
|
||||
|
||||
bool success = false;
|
||||
if (!is_being_async_deflated()) {
|
||||
void* prev_owner = try_set_owner_from(nullptr, locking_thread);
|
||||
|
||||
if (prev_owner == nullptr) {
|
||||
assert(_recursions == 0, "invariant");
|
||||
success = true;
|
||||
} else if (prev_owner == locking_thread) {
|
||||
_recursions++;
|
||||
success = true;
|
||||
} else if (prev_owner == DEFLATER_MARKER) {
|
||||
// Racing with deflation.
|
||||
prev_owner = try_set_owner_from(DEFLATER_MARKER, locking_thread);
|
||||
if (prev_owner == DEFLATER_MARKER) {
|
||||
// Cancelled deflation. Increment contentions as part of the deflation protocol.
|
||||
add_to_contentions(1);
|
||||
success = true;
|
||||
} else if (prev_owner == nullptr) {
|
||||
// At this point we cannot race with deflation as we have both incremented
|
||||
// contentions, seen contention > 0 and seen a DEFLATER_MARKER.
|
||||
// success will only be false if this races with something other than
|
||||
// deflation.
|
||||
prev_owner = try_set_owner_from(nullptr, locking_thread);
|
||||
success = prev_owner == nullptr;
|
||||
}
|
||||
} else if (LockingMode == LM_LEGACY && locking_thread->is_lock_owned((address)prev_owner)) {
|
||||
assert(_recursions == 0, "must be");
|
||||
_recursions = 1;
|
||||
set_owner_from_BasicLock(prev_owner, locking_thread);
|
||||
if (prev_owner == nullptr) {
|
||||
assert(_recursions == 0, "invariant");
|
||||
success = true;
|
||||
} else if (prev_owner == locking_thread) {
|
||||
_recursions++;
|
||||
success = true;
|
||||
} else if (prev_owner == DEFLATER_MARKER) {
|
||||
// Racing with deflation.
|
||||
prev_owner = try_set_owner_from(DEFLATER_MARKER, locking_thread);
|
||||
if (prev_owner == DEFLATER_MARKER) {
|
||||
// Cancelled deflation. Increment contentions as part of the deflation protocol.
|
||||
add_to_contentions(1);
|
||||
success = true;
|
||||
} else if (prev_owner == nullptr) {
|
||||
// At this point we cannot race with deflation as we have both incremented
|
||||
// contentions, seen contention > 0 and seen a DEFLATER_MARKER.
|
||||
// success will only be false if this races with something other than
|
||||
// deflation.
|
||||
prev_owner = try_set_owner_from(nullptr, locking_thread);
|
||||
success = prev_owner == nullptr;
|
||||
}
|
||||
assert(success, "Failed to enter_for: locking_thread=" INTPTR_FORMAT
|
||||
", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}, observed owner: " INTPTR_FORMAT,
|
||||
p2i(locking_thread), p2i(this), p2i(owner_raw()), p2i(prev_owner));
|
||||
} else {
|
||||
// Async deflation is in progress and our contentions increment
|
||||
// above lost the race to async deflation. Undo the work and
|
||||
// force the caller to retry.
|
||||
const oop l_object = object();
|
||||
if (l_object != nullptr) {
|
||||
// Attempt to restore the header/dmw to the object's header so that
|
||||
// we only retry once if the deflater thread happens to be slow.
|
||||
install_displaced_markword_in_object(l_object);
|
||||
}
|
||||
} else if (LockingMode == LM_LEGACY && locking_thread->is_lock_owned((address)prev_owner)) {
|
||||
assert(_recursions == 0, "must be");
|
||||
_recursions = 1;
|
||||
set_owner_from_BasicLock(prev_owner, locking_thread);
|
||||
success = true;
|
||||
}
|
||||
|
||||
add_to_contentions(-1);
|
||||
|
||||
assert(!success || owner_raw() == locking_thread, "must be");
|
||||
|
||||
return success;
|
||||
assert(success, "Failed to enter_for: locking_thread=" INTPTR_FORMAT
|
||||
", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}, observed owner: " INTPTR_FORMAT,
|
||||
p2i(locking_thread), p2i(this), p2i(owner_raw()), p2i(prev_owner));
|
||||
}
|
||||
|
||||
bool ObjectMonitor::enter(JavaThread* current) {
|
||||
assert(current == JavaThread::current(), "must be");
|
||||
// The following code is ordered to check the most common cases first
|
||||
// and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors.
|
||||
bool ObjectMonitor::enter_for(JavaThread* locking_thread) {
|
||||
|
||||
void* cur = try_set_owner_from(nullptr, current);
|
||||
if (cur == nullptr) {
|
||||
// Block out deflation as soon as possible.
|
||||
ObjectMonitorContentionMark contention_mark(this);
|
||||
|
||||
// Check for deflation.
|
||||
if (enter_is_async_deflating()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
enter_for_with_contention_mark(locking_thread, contention_mark);
|
||||
assert(owner_raw() == locking_thread, "must be");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjectMonitor::try_enter(JavaThread* current) {
|
||||
// TryLock avoids the CAS
|
||||
TryLockResult r = TryLock(current);
|
||||
if (r == TryLockResult::Success) {
|
||||
assert(_recursions == 0, "invariant");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cur == current) {
|
||||
// TODO-FIXME: check for integer overflow! BUGID 6557169.
|
||||
if (r == TryLockResult::HasOwner && owner() == current) {
|
||||
_recursions++;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LockingMode != LM_LIGHTWEIGHT && current->is_lock_owned((address)cur)) {
|
||||
void* cur = owner_raw();
|
||||
if (LockingMode == LM_LEGACY && current->is_lock_owned((address)cur)) {
|
||||
assert(_recursions == 0, "internal state error");
|
||||
_recursions = 1;
|
||||
set_owner_from_BasicLock(cur, current); // Convert from BasicLock* to Thread*.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectMonitor::spin_enter(JavaThread* current) {
|
||||
assert(current == JavaThread::current(), "must be");
|
||||
|
||||
// Check for recursion.
|
||||
if (try_enter(current)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for deflation.
|
||||
if (enter_is_async_deflating()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We've encountered genuine contention.
|
||||
|
||||
// Try one round of spinning *before* enqueueing current
|
||||
// and before going through the awkward and expensive state
|
||||
// transitions. The following spin is strictly optional ...
|
||||
// Do one round of spinning.
|
||||
// Note that if we acquire the monitor from an initial spin
|
||||
// we forgo posting JVMTI events and firing DTRACE probes.
|
||||
if (TrySpin(current)) {
|
||||
assert(owner_raw() == current, "must be current: owner=" INTPTR_FORMAT, p2i(owner_raw()));
|
||||
assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions);
|
||||
assert(object()->mark() == markWord::encode(this),
|
||||
"object mark must match encoded this: mark=" INTPTR_FORMAT
|
||||
", encoded this=" INTPTR_FORMAT, object()->mark().value(),
|
||||
markWord::encode(this).value());
|
||||
assert_mark_word_consistency();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectMonitor::enter(JavaThread* current) {
|
||||
assert(current == JavaThread::current(), "must be");
|
||||
|
||||
if (spin_enter(current)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -408,22 +444,25 @@ bool ObjectMonitor::enter(JavaThread* current) {
|
||||
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
|
||||
assert(current->thread_state() != _thread_blocked, "invariant");
|
||||
|
||||
// Keep track of contention for JVM/TI and M&M queries.
|
||||
add_to_contentions(1);
|
||||
if (is_being_async_deflated()) {
|
||||
// Async deflation is in progress and our contentions increment
|
||||
// above lost the race to async deflation. Undo the work and
|
||||
// force the caller to retry.
|
||||
const oop l_object = object();
|
||||
if (l_object != nullptr) {
|
||||
// Attempt to restore the header/dmw to the object's header so that
|
||||
// we only retry once if the deflater thread happens to be slow.
|
||||
install_displaced_markword_in_object(l_object);
|
||||
}
|
||||
add_to_contentions(-1);
|
||||
// Keep is_being_async_deflated stable across the rest of enter
|
||||
ObjectMonitorContentionMark contention_mark(this);
|
||||
|
||||
// Check for deflation.
|
||||
if (enter_is_async_deflating()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point this ObjectMonitor cannot be deflated, finish contended enter
|
||||
enter_with_contention_mark(current, contention_mark);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonitorContentionMark &cm) {
|
||||
assert(current == JavaThread::current(), "must be");
|
||||
assert(owner_raw() != current, "must be");
|
||||
assert(cm._monitor == this, "must be");
|
||||
assert(!is_being_async_deflated(), "must be");
|
||||
|
||||
JFR_ONLY(JfrConditionalFlush<EventJavaMonitorEnter> flush(current);)
|
||||
EventJavaMonitorEnter event;
|
||||
if (event.is_started()) {
|
||||
@ -480,14 +519,13 @@ bool ObjectMonitor::enter(JavaThread* current) {
|
||||
// the monitor free and clear.
|
||||
}
|
||||
|
||||
add_to_contentions(-1);
|
||||
assert(contentions() >= 0, "must not be negative: contentions=%d", contentions());
|
||||
|
||||
// Must either set _recursions = 0 or ASSERT _recursions == 0.
|
||||
assert(_recursions == 0, "invariant");
|
||||
assert(owner_raw() == current, "invariant");
|
||||
assert(_succ != current, "invariant");
|
||||
assert(object()->mark() == markWord::encode(this), "invariant");
|
||||
assert_mark_word_consistency();
|
||||
|
||||
// The thread -- now the owner -- is back in vm mode.
|
||||
// Report the glorious news via TI,DTrace and jvmstat.
|
||||
@ -516,7 +554,6 @@ bool ObjectMonitor::enter(JavaThread* current) {
|
||||
event.commit();
|
||||
}
|
||||
OM_PERFDATA_OP(ContendedLockAttempts, inc());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Caveat: TryLock() is not necessarily serializing if it returns failure.
|
||||
@ -549,7 +586,7 @@ ObjectMonitor::TryLockResult ObjectMonitor::TryLock(JavaThread* current) {
|
||||
// (contentions < 0)
|
||||
// Contending threads that see that condition know to retry their operation.
|
||||
//
|
||||
bool ObjectMonitor::deflate_monitor() {
|
||||
bool ObjectMonitor::deflate_monitor(Thread* current) {
|
||||
if (is_busy()) {
|
||||
// Easy checks are first - the ObjectMonitor is busy so no deflation.
|
||||
return false;
|
||||
@ -620,7 +657,11 @@ bool ObjectMonitor::deflate_monitor() {
|
||||
p2i(obj), obj->mark().value(),
|
||||
obj->klass()->external_name());
|
||||
}
|
||||
}
|
||||
|
||||
if (UseObjectMonitorTable) {
|
||||
LightweightSynchronizer::deflate_monitor(current, obj, this);
|
||||
} else if (obj != nullptr) {
|
||||
// Install the old mark word if nobody else has already done it.
|
||||
install_displaced_markword_in_object(obj);
|
||||
}
|
||||
@ -636,6 +677,7 @@ bool ObjectMonitor::deflate_monitor() {
|
||||
// monitor and by other threads that have detected a race with the
|
||||
// deflation process.
|
||||
void ObjectMonitor::install_displaced_markword_in_object(const oop obj) {
|
||||
assert(!UseObjectMonitorTable, "ObjectMonitorTable has no dmw");
|
||||
// This function must only be called when (owner == DEFLATER_MARKER
|
||||
// && contentions <= 0), but we can't guarantee that here because
|
||||
// those values could change when the ObjectMonitor gets moved from
|
||||
@ -972,12 +1014,11 @@ void ObjectMonitor::EnterI(JavaThread* current) {
|
||||
|
||||
void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
|
||||
assert(current != nullptr, "invariant");
|
||||
assert(current->thread_state() != _thread_blocked, "invariant");
|
||||
assert(currentNode != nullptr, "invariant");
|
||||
assert(currentNode->_thread == current, "invariant");
|
||||
assert(_waiters > 0, "invariant");
|
||||
assert(object()->mark() == markWord::encode(this), "invariant");
|
||||
|
||||
assert(current->thread_state() != _thread_blocked, "invariant");
|
||||
assert_mark_word_consistency();
|
||||
|
||||
for (;;) {
|
||||
ObjectWaiter::TStates v = currentNode->TState;
|
||||
@ -1042,7 +1083,7 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
|
||||
// In addition, current.TState is stable.
|
||||
|
||||
assert(owner_raw() == current, "invariant");
|
||||
assert(object()->mark() == markWord::encode(this), "invariant");
|
||||
assert_mark_word_consistency();
|
||||
UnlinkAfterAcquire(current, currentNode);
|
||||
if (_succ == current) _succ = nullptr;
|
||||
assert(_succ != current, "invariant");
|
||||
@ -1668,7 +1709,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
||||
// Verify a few postconditions
|
||||
assert(owner_raw() == current, "invariant");
|
||||
assert(_succ != current, "invariant");
|
||||
assert(object()->mark() == markWord::encode(this), "invariant");
|
||||
assert_mark_word_consistency();
|
||||
|
||||
// check if the notification happened
|
||||
if (!WasNotified) {
|
||||
@ -2185,7 +2226,7 @@ void ObjectMonitor::print() const { print_on(tty); }
|
||||
// Print the ObjectMonitor like a debugger would:
|
||||
//
|
||||
// (ObjectMonitor) 0x00007fdfb6012e40 = {
|
||||
// _header = 0x0000000000000001
|
||||
// _metadata = 0x0000000000000001
|
||||
// _object = 0x000000070ff45fd0
|
||||
// _pad_buf0 = {
|
||||
// [0] = '\0'
|
||||
@ -2214,7 +2255,7 @@ void ObjectMonitor::print() const { print_on(tty); }
|
||||
//
|
||||
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(" _metadata = " INTPTR_FORMAT, _metadata);
|
||||
st->print_cr(" _object = " INTPTR_FORMAT, p2i(object_peek()));
|
||||
st->print_cr(" _pad_buf0 = {");
|
||||
st->print_cr(" [0] = '\\0'");
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "utilities/checkedCast.hpp"
|
||||
|
||||
class ObjectMonitor;
|
||||
class ObjectMonitorContentionMark;
|
||||
class ParkEvent;
|
||||
|
||||
// ObjectWaiter serves as a "proxy" or surrogate thread.
|
||||
@ -69,20 +70,21 @@ class ObjectWaiter : public StackObj {
|
||||
//
|
||||
// ObjectMonitor Layout Overview/Highlights/Restrictions:
|
||||
//
|
||||
// - The _header field must be at offset 0 because the displaced header
|
||||
// - The _metadata field must be at offset 0 because the displaced header
|
||||
// from markWord is stored there. We do not want markWord.hpp to include
|
||||
// ObjectMonitor.hpp to avoid exposing ObjectMonitor everywhere. This
|
||||
// means that ObjectMonitor cannot inherit from any other class nor can
|
||||
// it use any virtual member functions. This restriction is critical to
|
||||
// the proper functioning of the VM.
|
||||
// - The _header and _owner fields should be separated by enough space
|
||||
// - The _metadata and _owner fields should be separated by enough space
|
||||
// to avoid false sharing due to parallel access by different threads.
|
||||
// This is an advisory recommendation.
|
||||
// - The general layout of the fields in ObjectMonitor is:
|
||||
// _header
|
||||
// _metadata
|
||||
// <lightly_used_fields>
|
||||
// <optional padding>
|
||||
// _owner
|
||||
// <optional padding>
|
||||
// <remaining_fields>
|
||||
// - The VM assumes write ordering and machine word alignment with
|
||||
// respect to the _owner field and the <remaining_fields> that can
|
||||
@ -106,20 +108,19 @@ class ObjectWaiter : public StackObj {
|
||||
// in synchronizer.cpp. Also see TEST_VM(SynchronizerTest, sanity) gtest.
|
||||
//
|
||||
// Futures notes:
|
||||
// - Separating _owner from the <remaining_fields> by enough space to
|
||||
// avoid false sharing might be profitable. Given
|
||||
// http://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
|
||||
// we know that the CAS in monitorenter will invalidate the line
|
||||
// underlying _owner. We want to avoid an L1 data cache miss on that
|
||||
// same line for monitorexit. Putting these <remaining_fields>:
|
||||
// _recursions, _EntryList, _cxq, and _succ, all of which may be
|
||||
// fetched in the inflated unlock path, on a different cache line
|
||||
// would make them immune to CAS-based invalidation from the _owner
|
||||
// field.
|
||||
// - Separating _owner from the <remaining_fields> by enough space to
|
||||
// avoid false sharing might be profitable. Given that the CAS in
|
||||
// monitorenter will invalidate the line underlying _owner. We want
|
||||
// to avoid an L1 data cache miss on that same line for monitorexit.
|
||||
// Putting these <remaining_fields>:
|
||||
// _recursions, _EntryList, _cxq, and _succ, all of which may be
|
||||
// fetched in the inflated unlock path, on a different cache line
|
||||
// would make them immune to CAS-based invalidation from the _owner
|
||||
// field.
|
||||
//
|
||||
// - The _recursions field should be of type int, or int32_t but not
|
||||
// intptr_t. There's no reason to use a 64-bit type for this field
|
||||
// in a 64-bit JVM.
|
||||
// - The _recursions field should be of type int, or int32_t but not
|
||||
// intptr_t. There's no reason to use a 64-bit type for this field
|
||||
// in a 64-bit JVM.
|
||||
|
||||
#define OM_CACHE_LINE_SIZE DEFAULT_CACHE_LINE_SIZE
|
||||
|
||||
@ -131,15 +132,19 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
|
||||
|
||||
static OopStorage* _oop_storage;
|
||||
|
||||
// The sync code expects the header field to be at offset zero (0).
|
||||
// Enforced by the assert() in header_addr().
|
||||
volatile markWord _header; // displaced object header word - mark
|
||||
// The sync code expects the metadata field to be at offset zero (0).
|
||||
// Enforced by the assert() in metadata_addr().
|
||||
// * LM_LIGHTWEIGHT with UseObjectMonitorTable:
|
||||
// Contains the _object's hashCode.
|
||||
// * LM_LEGACY, LM_MONITOR, LM_LIGHTWEIGHT without UseObjectMonitorTable:
|
||||
// Contains the displaced object header word - mark
|
||||
volatile uintptr_t _metadata; // metadata
|
||||
WeakHandle _object; // backward object pointer
|
||||
// Separate _header and _owner on different cache lines since both can
|
||||
// have busy multi-threaded access. _header and _object are set at initial
|
||||
// Separate _metadata and _owner on different cache lines since both can
|
||||
// have busy multi-threaded access. _metadata and _object are set at initial
|
||||
// inflation. The _object does not change, so it is a good choice to share
|
||||
// its cache line with _header.
|
||||
DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE, sizeof(volatile markWord) +
|
||||
// its cache line with _metadata.
|
||||
DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE, sizeof(_metadata) +
|
||||
sizeof(WeakHandle));
|
||||
// Used by async deflation as a marker in the _owner field.
|
||||
// Note that the choice of the two markers is peculiar:
|
||||
@ -149,12 +154,13 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
|
||||
// and small values encode much better.
|
||||
// - We test for anonymous owner by testing for the lowest bit, therefore
|
||||
// DEFLATER_MARKER must *not* have that bit set.
|
||||
#define DEFLATER_MARKER reinterpret_cast<void*>(2)
|
||||
public:
|
||||
static const uintptr_t DEFLATER_MARKER_VALUE = 2;
|
||||
#define DEFLATER_MARKER reinterpret_cast<void*>(DEFLATER_MARKER_VALUE)
|
||||
public:
|
||||
// NOTE: Typed as uintptr_t so that we can pick it up in SA, via vmStructs.
|
||||
static const uintptr_t ANONYMOUS_OWNER = 1;
|
||||
|
||||
private:
|
||||
private:
|
||||
static void* anon_owner_ptr() { return reinterpret_cast<void*>(ANONYMOUS_OWNER); }
|
||||
|
||||
void* volatile _owner; // pointer to owning thread OR BasicLock
|
||||
@ -181,10 +187,9 @@ private:
|
||||
// along with other fields to determine if an ObjectMonitor can be
|
||||
// deflated. It is also used by the async deflation protocol. See
|
||||
// ObjectMonitor::deflate_monitor().
|
||||
protected:
|
||||
|
||||
ObjectWaiter* volatile _WaitSet; // LL of threads wait()ing on the monitor
|
||||
volatile int _waiters; // number of waiting threads
|
||||
private:
|
||||
volatile int _WaitSetLock; // protects Wait Queue - simple spinlock
|
||||
|
||||
public:
|
||||
@ -213,6 +218,7 @@ private:
|
||||
|
||||
static int Knob_SpinLimit;
|
||||
|
||||
static ByteSize metadata_offset() { return byte_offset_of(ObjectMonitor, _metadata); }
|
||||
static ByteSize owner_offset() { return byte_offset_of(ObjectMonitor, _owner); }
|
||||
static ByteSize recursions_offset() { return byte_offset_of(ObjectMonitor, _recursions); }
|
||||
static ByteSize cxq_offset() { return byte_offset_of(ObjectMonitor, _cxq); }
|
||||
@ -233,9 +239,15 @@ private:
|
||||
#define OM_OFFSET_NO_MONITOR_VALUE_TAG(f) \
|
||||
((in_bytes(ObjectMonitor::f ## _offset())) - checked_cast<int>(markWord::monitor_value))
|
||||
|
||||
markWord header() const;
|
||||
volatile markWord* header_addr();
|
||||
void set_header(markWord hdr);
|
||||
uintptr_t metadata() const;
|
||||
void set_metadata(uintptr_t value);
|
||||
volatile uintptr_t* metadata_addr();
|
||||
|
||||
markWord header() const;
|
||||
void set_header(markWord hdr);
|
||||
|
||||
intptr_t hash() const;
|
||||
void set_hash(intptr_t hash);
|
||||
|
||||
bool is_busy() const {
|
||||
// TODO-FIXME: assert _owner == null implies _recursions = 0
|
||||
@ -306,6 +318,8 @@ private:
|
||||
|
||||
oop object() const;
|
||||
oop object_peek() const;
|
||||
bool object_is_dead() const;
|
||||
bool object_refers_to(oop obj) const;
|
||||
|
||||
// Returns true if the specified thread owns the ObjectMonitor. Otherwise
|
||||
// returns false and throws IllegalMonitorStateException (IMSE).
|
||||
@ -328,9 +342,15 @@ private:
|
||||
ClearSuccOnSuspend(ObjectMonitor* om) : _om(om) {}
|
||||
void operator()(JavaThread* current);
|
||||
};
|
||||
|
||||
bool enter_is_async_deflating();
|
||||
public:
|
||||
void enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark);
|
||||
bool enter_for(JavaThread* locking_thread);
|
||||
bool enter(JavaThread* current);
|
||||
bool try_enter(JavaThread* current);
|
||||
bool spin_enter(JavaThread* current);
|
||||
void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark);
|
||||
void exit(JavaThread* current, bool not_suspended = true);
|
||||
void wait(jlong millis, bool interruptible, TRAPS);
|
||||
void notify(TRAPS);
|
||||
@ -364,8 +384,23 @@ private:
|
||||
void ExitEpilog(JavaThread* current, ObjectWaiter* Wakee);
|
||||
|
||||
// Deflation support
|
||||
bool deflate_monitor();
|
||||
bool deflate_monitor(Thread* current);
|
||||
private:
|
||||
void install_displaced_markword_in_object(const oop obj);
|
||||
};
|
||||
|
||||
// RAII object to ensure that ObjectMonitor::is_being_async_deflated() is
|
||||
// stable within the context of this mark.
|
||||
class ObjectMonitorContentionMark : StackObj {
|
||||
DEBUG_ONLY(friend class ObjectMonitor;)
|
||||
|
||||
ObjectMonitor* _monitor;
|
||||
|
||||
NONCOPYABLE(ObjectMonitorContentionMark);
|
||||
|
||||
public:
|
||||
explicit ObjectMonitorContentionMark(ObjectMonitor* monitor);
|
||||
~ObjectMonitorContentionMark();
|
||||
};
|
||||
|
||||
#endif // SHARE_RUNTIME_OBJECTMONITOR_HPP
|
||||
|
@ -29,9 +29,13 @@
|
||||
|
||||
#include "logging/log.hpp"
|
||||
#include "oops/access.inline.hpp"
|
||||
#include "oops/markWord.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/globals.hpp"
|
||||
#include "runtime/lockStack.inline.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "utilities/checkedCast.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
inline bool ObjectMonitor::is_entered(JavaThread* current) const {
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
@ -49,16 +53,38 @@ inline bool ObjectMonitor::is_entered(JavaThread* current) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline markWord ObjectMonitor::header() const {
|
||||
return Atomic::load(&_header);
|
||||
inline uintptr_t ObjectMonitor::metadata() const {
|
||||
return Atomic::load(&_metadata);
|
||||
}
|
||||
|
||||
inline volatile markWord* ObjectMonitor::header_addr() {
|
||||
return &_header;
|
||||
inline void ObjectMonitor::set_metadata(uintptr_t value) {
|
||||
Atomic::store(&_metadata, value);
|
||||
}
|
||||
|
||||
inline volatile uintptr_t* ObjectMonitor::metadata_addr() {
|
||||
STATIC_ASSERT(std::is_standard_layout<ObjectMonitor>::value);
|
||||
STATIC_ASSERT(offsetof(ObjectMonitor, _metadata) == 0);
|
||||
return &_metadata;
|
||||
}
|
||||
|
||||
inline markWord ObjectMonitor::header() const {
|
||||
assert(!UseObjectMonitorTable, "Lightweight locking with OM table does not use header");
|
||||
return markWord(metadata());
|
||||
}
|
||||
|
||||
inline void ObjectMonitor::set_header(markWord hdr) {
|
||||
Atomic::store(&_header, hdr);
|
||||
assert(!UseObjectMonitorTable, "Lightweight locking with OM table does not use header");
|
||||
set_metadata(hdr.value());
|
||||
}
|
||||
|
||||
inline intptr_t ObjectMonitor::hash() const {
|
||||
assert(UseObjectMonitorTable, "Only used by lightweight locking with OM table");
|
||||
return metadata();
|
||||
}
|
||||
|
||||
inline void ObjectMonitor::set_hash(intptr_t hash) {
|
||||
assert(UseObjectMonitorTable, "Only used by lightweight locking with OM table");
|
||||
set_metadata(hash);
|
||||
}
|
||||
|
||||
inline int ObjectMonitor::waiters() const {
|
||||
@ -180,4 +206,31 @@ inline void ObjectMonitor::set_next_om(ObjectMonitor* new_value) {
|
||||
Atomic::store(&_next_om, new_value);
|
||||
}
|
||||
|
||||
inline ObjectMonitorContentionMark::ObjectMonitorContentionMark(ObjectMonitor* monitor)
|
||||
: _monitor(monitor) {
|
||||
_monitor->add_to_contentions(1);
|
||||
}
|
||||
|
||||
inline ObjectMonitorContentionMark::~ObjectMonitorContentionMark() {
|
||||
_monitor->add_to_contentions(-1);
|
||||
}
|
||||
|
||||
inline oop ObjectMonitor::object_peek() const {
|
||||
if (_object.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
return _object.peek();
|
||||
}
|
||||
|
||||
inline bool ObjectMonitor::object_is_dead() const {
|
||||
return object_peek() == nullptr;
|
||||
}
|
||||
|
||||
inline bool ObjectMonitor::object_refers_to(oop obj) const {
|
||||
if (_object.is_null()) {
|
||||
return false;
|
||||
}
|
||||
return _object.peek() == obj;
|
||||
}
|
||||
|
||||
#endif // SHARE_RUNTIME_OBJECTMONITOR_INLINE_HPP
|
||||
|
@ -731,6 +731,11 @@ void ThreadSafepointState::account_safe_thread() {
|
||||
DEBUG_ONLY(_thread->set_visited_for_critical_count(SafepointSynchronize::safepoint_counter());)
|
||||
assert(!_safepoint_safe, "Must be unsafe before safe");
|
||||
_safepoint_safe = true;
|
||||
|
||||
// The oops in the monitor cache are cleared to prevent stale cache entries
|
||||
// from keeping dead objects alive. Because these oops are always cleared
|
||||
// before safepoint operations they are not visited in JavaThread::oops_do.
|
||||
_thread->om_clear_monitor_cache();
|
||||
}
|
||||
|
||||
void ThreadSafepointState::restart() {
|
||||
|
@ -32,20 +32,21 @@
|
||||
#include "classfile/vmClasses.hpp"
|
||||
#include "gc/shared/oopStorage.hpp"
|
||||
#include "gc/shared/oopStorageSet.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "interpreter/oopMapCache.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "oops/oopHandle.inline.hpp"
|
||||
#include "prims/jvmtiImpl.hpp"
|
||||
#include "prims/jvmtiTagMap.hpp"
|
||||
#include "prims/resolvedMethodTable.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/java.hpp"
|
||||
#include "runtime/javaCalls.hpp"
|
||||
#include "runtime/jniHandles.hpp"
|
||||
#include "runtime/serviceThread.hpp"
|
||||
#include "runtime/lightweightSynchronizer.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "prims/jvmtiImpl.hpp"
|
||||
#include "prims/jvmtiTagMap.hpp"
|
||||
#include "prims/resolvedMethodTable.hpp"
|
||||
#include "runtime/serviceThread.hpp"
|
||||
#include "services/diagnosticArgument.hpp"
|
||||
#include "services/diagnosticFramework.hpp"
|
||||
#include "services/finalizerService.hpp"
|
||||
@ -94,6 +95,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
||||
bool cldg_cleanup_work = false;
|
||||
bool jvmti_tagmap_work = false;
|
||||
bool oopmap_cache_work = false;
|
||||
bool object_monitor_table_work = false;
|
||||
{
|
||||
// Need state transition ThreadBlockInVM so that this thread
|
||||
// will be handled by safepoint correctly when this thread is
|
||||
@ -121,7 +123,8 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
||||
(oop_handles_to_release = JavaThread::has_oop_handles_to_release()) |
|
||||
(cldg_cleanup_work = ClassLoaderDataGraph::should_clean_metaspaces_and_reset()) |
|
||||
(jvmti_tagmap_work = JvmtiTagMap::has_object_free_events_and_reset()) |
|
||||
(oopmap_cache_work = OopMapCache::has_cleanup_work())
|
||||
(oopmap_cache_work = OopMapCache::has_cleanup_work()) |
|
||||
(object_monitor_table_work = LightweightSynchronizer::needs_resize())
|
||||
) == 0) {
|
||||
// Wait until notified that there is some work to do or timer expires.
|
||||
// Some cleanup requests don't notify the ServiceThread so work needs to be done at periodic intervals.
|
||||
@ -183,6 +186,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
|
||||
if (oopmap_cache_work) {
|
||||
OopMapCache::cleanup();
|
||||
}
|
||||
|
||||
if (object_monitor_table_work) {
|
||||
LightweightSynchronizer::resize_table(jt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "prims/nativeLookup.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/basicLock.inline.hpp"
|
||||
#include "runtime/frame.inline.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/init.hpp"
|
||||
@ -69,13 +70,14 @@
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "runtime/stackWatermarkSet.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/synchronizer.inline.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
#include "runtime/vframeArray.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
#include "utilities/copy.hpp"
|
||||
#include "utilities/dtrace.hpp"
|
||||
#include "utilities/events.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/resourceHash.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
#include "utilities/xmlstream.hpp"
|
||||
@ -1883,7 +1885,7 @@ void SharedRuntime::monitor_enter_helper(oopDesc* obj, BasicLock* lock, JavaThre
|
||||
if (!SafepointSynchronize::is_synchronizing()) {
|
||||
// Only try quick_enter() if we're not trying to reach a safepoint
|
||||
// so that the calling thread reaches the safepoint more quickly.
|
||||
if (ObjectSynchronizer::quick_enter(obj, current, lock)) {
|
||||
if (ObjectSynchronizer::quick_enter(obj, lock, current)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -2945,6 +2947,8 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *current) )
|
||||
// Now the displaced header is free to move because the
|
||||
// object's header no longer refers to it.
|
||||
buf[i] = (intptr_t)lock->displaced_header().value();
|
||||
} else if (UseObjectMonitorTable) {
|
||||
buf[i] = (intptr_t)lock->object_monitor_cache();
|
||||
}
|
||||
#ifdef ASSERT
|
||||
else {
|
||||
|
@ -35,12 +35,14 @@
|
||||
#include "oops/markWord.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/basicLock.inline.hpp"
|
||||
#include "runtime/frame.inline.hpp"
|
||||
#include "runtime/globals.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/handshake.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "runtime/lightweightSynchronizer.hpp"
|
||||
#include "runtime/lockStack.inline.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/objectMonitor.hpp"
|
||||
@ -52,7 +54,7 @@
|
||||
#include "runtime/safepointVerifiers.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/synchronizer.inline.hpp"
|
||||
#include "runtime/threads.hpp"
|
||||
#include "runtime/timer.hpp"
|
||||
#include "runtime/trimNativeHeap.hpp"
|
||||
@ -276,6 +278,10 @@ void ObjectSynchronizer::initialize() {
|
||||
|
||||
// Start the timer for deflations, so it does not trigger immediately.
|
||||
_last_async_deflation_time_ns = os::javaTimeNanos();
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
LightweightSynchronizer::initialize();
|
||||
}
|
||||
}
|
||||
|
||||
MonitorList ObjectSynchronizer::_in_use_list;
|
||||
@ -349,7 +355,11 @@ bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool al
|
||||
}
|
||||
|
||||
if (mark.has_monitor()) {
|
||||
ObjectMonitor* const mon = mark.monitor();
|
||||
ObjectMonitor* const mon = read_monitor(current, obj, mark);
|
||||
if (LockingMode == LM_LIGHTWEIGHT && mon == nullptr) {
|
||||
// Racing with inflation/deflation go slow path
|
||||
return false;
|
||||
}
|
||||
assert(mon->object() == oop(obj), "invariant");
|
||||
if (mon->owner() != current) return false; // slow-path for IMS exception
|
||||
|
||||
@ -376,6 +386,13 @@ bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool al
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool useHeavyMonitors() {
|
||||
#if defined(X86) || defined(AARCH64) || defined(PPC64) || defined(RISCV64) || defined(S390)
|
||||
return LockingMode == LM_MONITOR;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// The LockNode emitted directly at the synchronization site would have
|
||||
// been too big if it were to have included support for the cases of inflated
|
||||
@ -383,33 +400,24 @@ bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool al
|
||||
// Note that we can't safely call AsyncPrintJavaStack() from within
|
||||
// quick_enter() as our thread state remains _in_Java.
|
||||
|
||||
bool ObjectSynchronizer::quick_enter(oop obj, JavaThread* current,
|
||||
BasicLock * lock) {
|
||||
bool ObjectSynchronizer::quick_enter_legacy(oop obj, BasicLock* lock, JavaThread* current) {
|
||||
assert(current->thread_state() == _thread_in_Java, "invariant");
|
||||
NoSafepointVerifier nsv;
|
||||
if (obj == nullptr) return false; // Need to throw NPE
|
||||
|
||||
if (obj->klass()->is_value_based()) {
|
||||
return false;
|
||||
if (useHeavyMonitors()) {
|
||||
return false; // Slow path
|
||||
}
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
LockStack& lock_stack = current->lock_stack();
|
||||
if (lock_stack.is_full()) {
|
||||
// Always go into runtime if the lock stack is full.
|
||||
return false;
|
||||
}
|
||||
if (lock_stack.try_recursive_enter(obj)) {
|
||||
// Recursive lock successful.
|
||||
current->inc_held_monitor_count();
|
||||
return true;
|
||||
}
|
||||
return LightweightSynchronizer::quick_enter(obj, lock, current);
|
||||
}
|
||||
|
||||
assert(LockingMode == LM_LEGACY, "legacy mode below");
|
||||
|
||||
const markWord mark = obj->mark();
|
||||
|
||||
if (mark.has_monitor()) {
|
||||
ObjectMonitor* const m = mark.monitor();
|
||||
|
||||
ObjectMonitor* const m = read_monitor(mark);
|
||||
// An async deflation or GC can race us before we manage to make
|
||||
// the ObjectMonitor busy by setting the owner below. If we detect
|
||||
// that race we just bail out to the slow-path here.
|
||||
@ -429,18 +437,16 @@ bool ObjectSynchronizer::quick_enter(oop obj, JavaThread* current,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LockingMode != LM_LIGHTWEIGHT) {
|
||||
// This Java Monitor is inflated so obj's header will never be
|
||||
// displaced to this thread's BasicLock. Make the displaced header
|
||||
// non-null so this BasicLock is not seen as recursive nor as
|
||||
// being locked. We do this unconditionally so that this thread's
|
||||
// BasicLock cannot be mis-interpreted by any stack walkers. For
|
||||
// performance reasons, stack walkers generally first check for
|
||||
// stack-locking in the object's header, the second check is for
|
||||
// recursive stack-locking in the displaced header in the BasicLock,
|
||||
// and last are the inflated Java Monitor (ObjectMonitor) checks.
|
||||
lock->set_displaced_header(markWord::unused_mark());
|
||||
}
|
||||
// This Java Monitor is inflated so obj's header will never be
|
||||
// displaced to this thread's BasicLock. Make the displaced header
|
||||
// non-null so this BasicLock is not seen as recursive nor as
|
||||
// being locked. We do this unconditionally so that this thread's
|
||||
// BasicLock cannot be mis-interpreted by any stack walkers. For
|
||||
// performance reasons, stack walkers generally first check for
|
||||
// stack-locking in the object's header, the second check is for
|
||||
// recursive stack-locking in the displaced header in the BasicLock,
|
||||
// and last are the inflated Java Monitor (ObjectMonitor) checks.
|
||||
lock->set_displaced_header(markWord::unused_mark());
|
||||
|
||||
if (owner == nullptr && m->try_set_owner_from(nullptr, current) == nullptr) {
|
||||
assert(m->_recursions == 0, "invariant");
|
||||
@ -508,14 +514,6 @@ void ObjectSynchronizer::handle_sync_on_value_based_class(Handle obj, JavaThread
|
||||
}
|
||||
}
|
||||
|
||||
static bool useHeavyMonitors() {
|
||||
#if defined(X86) || defined(AARCH64) || defined(PPC64) || defined(RISCV64) || defined(S390)
|
||||
return LockingMode == LM_MONITOR;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Monitor Enter/Exit
|
||||
|
||||
@ -524,6 +522,11 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock
|
||||
// the locking_thread with respect to the current thread. Currently only used when
|
||||
// deoptimizing and re-locking locks. See Deoptimization::relock_objects
|
||||
assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be");
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
return LightweightSynchronizer::enter_for(obj, lock, locking_thread);
|
||||
}
|
||||
|
||||
if (!enter_fast_impl(obj, lock, locking_thread)) {
|
||||
// Inflated ObjectMonitor::enter_for is required
|
||||
|
||||
@ -540,8 +543,7 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) {
|
||||
assert(current == Thread::current(), "must be");
|
||||
void ObjectSynchronizer::enter_legacy(Handle obj, BasicLock* lock, JavaThread* current) {
|
||||
if (!enter_fast_impl(obj, lock, current)) {
|
||||
// Inflated ObjectMonitor::enter is required
|
||||
|
||||
@ -561,6 +563,7 @@ void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current)
|
||||
// of this algorithm. Make sure to update that code if the following function is
|
||||
// changed. The implementation is extremely sensitive to race condition. Be careful.
|
||||
bool ObjectSynchronizer::enter_fast_impl(Handle obj, BasicLock* lock, JavaThread* locking_thread) {
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "Use LightweightSynchronizer");
|
||||
|
||||
if (obj->klass()->is_value_based()) {
|
||||
handle_sync_on_value_based_class(obj, locking_thread);
|
||||
@ -569,61 +572,7 @@ bool ObjectSynchronizer::enter_fast_impl(Handle obj, BasicLock* lock, JavaThread
|
||||
locking_thread->inc_held_monitor_count();
|
||||
|
||||
if (!useHeavyMonitors()) {
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
// Fast-locking does not use the 'lock' argument.
|
||||
LockStack& lock_stack = locking_thread->lock_stack();
|
||||
if (lock_stack.is_full()) {
|
||||
// We unconditionally make room on the lock stack by inflating
|
||||
// the least recently locked object on the lock stack.
|
||||
|
||||
// About the choice to inflate least recently locked object.
|
||||
// First we must chose to inflate a lock, either some lock on
|
||||
// the lock-stack or the lock that is currently being entered
|
||||
// (which may or may not be on the lock-stack).
|
||||
// Second the best lock to inflate is a lock which is entered
|
||||
// in a control flow where there are only a very few locks being
|
||||
// used, as the costly part of inflated locking is inflation,
|
||||
// not locking. But this property is entirely program dependent.
|
||||
// Third inflating the lock currently being entered on when it
|
||||
// is not present on the lock-stack will result in a still full
|
||||
// lock-stack. This creates a scenario where every deeper nested
|
||||
// monitorenter must call into the runtime.
|
||||
// The rational here is as follows:
|
||||
// Because we cannot (currently) figure out the second, and want
|
||||
// to avoid the third, we inflate a lock on the lock-stack.
|
||||
// The least recently locked lock is chosen as it is the lock
|
||||
// with the longest critical section.
|
||||
|
||||
log_info(monitorinflation)("LockStack capacity exceeded, inflating.");
|
||||
ObjectMonitor* monitor = inflate_for(locking_thread, lock_stack.bottom(), inflate_cause_vm_internal);
|
||||
assert(monitor->owner() == locking_thread, "must be owner=" PTR_FORMAT " locking_thread=" PTR_FORMAT " mark=" PTR_FORMAT,
|
||||
p2i(monitor->owner()), p2i(locking_thread), monitor->object()->mark_acquire().value());
|
||||
assert(!lock_stack.is_full(), "must have made room here");
|
||||
}
|
||||
|
||||
markWord mark = obj()->mark_acquire();
|
||||
while (mark.is_unlocked()) {
|
||||
// Retry until a lock state change has been observed. cas_set_mark() may collide with non lock bits modifications.
|
||||
// Try to swing into 'fast-locked' state.
|
||||
assert(!lock_stack.contains(obj()), "thread must not already hold the lock");
|
||||
const markWord locked_mark = mark.set_fast_locked();
|
||||
const markWord old_mark = obj()->cas_set_mark(locked_mark, mark);
|
||||
if (old_mark == mark) {
|
||||
// Successfully fast-locked, push object to lock-stack and return.
|
||||
lock_stack.push(obj());
|
||||
return true;
|
||||
}
|
||||
mark = old_mark;
|
||||
}
|
||||
|
||||
if (mark.is_fast_locked() && lock_stack.try_recursive_enter(obj())) {
|
||||
// Recursive lock successful.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Failed to fast lock.
|
||||
return false;
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
markWord mark = obj->mark();
|
||||
if (mark.is_unlocked()) {
|
||||
// Anticipate successful CAS -- the ST of the displaced mark must
|
||||
@ -656,37 +605,12 @@ bool ObjectSynchronizer::enter_fast_impl(Handle obj, BasicLock* lock, JavaThread
|
||||
return false;
|
||||
}
|
||||
|
||||
void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) {
|
||||
current->dec_held_monitor_count();
|
||||
void ObjectSynchronizer::exit_legacy(oop object, BasicLock* lock, JavaThread* current) {
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "Use LightweightSynchronizer");
|
||||
|
||||
if (!useHeavyMonitors()) {
|
||||
markWord mark = object->mark();
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
// Fast-locking does not use the 'lock' argument.
|
||||
LockStack& lock_stack = current->lock_stack();
|
||||
if (mark.is_fast_locked() && lock_stack.try_recursive_exit(object)) {
|
||||
// Recursively unlocked.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mark.is_fast_locked() && lock_stack.is_recursive(object)) {
|
||||
// This lock is recursive but is not at the top of the lock stack so we're
|
||||
// doing an unbalanced exit. We have to fall thru to inflation below and
|
||||
// let ObjectMonitor::exit() do the unlock.
|
||||
} else {
|
||||
while (mark.is_fast_locked()) {
|
||||
// Retry until a lock state change has been observed. cas_set_mark() may collide with non lock bits modifications.
|
||||
const markWord unlocked_mark = mark.set_unlocked();
|
||||
const markWord old_mark = object->cas_set_mark(unlocked_mark, mark);
|
||||
if (old_mark == mark) {
|
||||
size_t recursions = lock_stack.remove(object) - 1;
|
||||
assert(recursions == 0, "must not be recursive here");
|
||||
return;
|
||||
}
|
||||
mark = old_mark;
|
||||
}
|
||||
}
|
||||
} else if (LockingMode == LM_LEGACY) {
|
||||
if (LockingMode == LM_LEGACY) {
|
||||
markWord dhw = lock->displaced_header();
|
||||
if (dhw.value() == 0) {
|
||||
// If the displaced header is null, then this exit matches up with
|
||||
@ -708,7 +632,7 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current)
|
||||
// Monitor owner's stack and update the BasicLocks because a
|
||||
// Java Monitor can be asynchronously inflated by a thread that
|
||||
// does not own the Java Monitor.
|
||||
ObjectMonitor* m = mark.monitor();
|
||||
ObjectMonitor* m = read_monitor(mark);
|
||||
assert(m->object()->mark() == mark, "invariant");
|
||||
assert(m->is_entered(current), "invariant");
|
||||
}
|
||||
@ -752,8 +676,16 @@ void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) {
|
||||
// enter() can make the ObjectMonitor busy. enter() returns false if
|
||||
// we have lost the race to async deflation and we simply try again.
|
||||
while (true) {
|
||||
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_jni_enter);
|
||||
if (monitor->enter(current)) {
|
||||
ObjectMonitor* monitor;
|
||||
bool entered;
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
entered = LightweightSynchronizer::inflate_and_enter(obj(), inflate_cause_jni_enter, current, current) != nullptr;
|
||||
} else {
|
||||
monitor = inflate(current, obj(), inflate_cause_jni_enter);
|
||||
entered = monitor->enter(current);
|
||||
}
|
||||
|
||||
if (entered) {
|
||||
current->inc_held_monitor_count(1, true);
|
||||
break;
|
||||
}
|
||||
@ -765,9 +697,14 @@ void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) {
|
||||
void ObjectSynchronizer::jni_exit(oop obj, TRAPS) {
|
||||
JavaThread* current = THREAD;
|
||||
|
||||
// The ObjectMonitor* can't be async deflated until ownership is
|
||||
// dropped inside exit() and the ObjectMonitor* must be !is_busy().
|
||||
ObjectMonitor* monitor = inflate(current, obj, inflate_cause_jni_exit);
|
||||
ObjectMonitor* monitor;
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj, inflate_cause_jni_exit, CHECK);
|
||||
} else {
|
||||
// The ObjectMonitor* can't be async deflated until ownership is
|
||||
// dropped inside exit() and the ObjectMonitor* must be !is_busy().
|
||||
monitor = inflate(current, obj, inflate_cause_jni_exit);
|
||||
}
|
||||
// If this thread has locked the object, exit the monitor. We
|
||||
// intentionally do not use CHECK on check_owner because we must exit the
|
||||
// monitor even if an exception was already pending.
|
||||
@ -800,15 +737,22 @@ ObjectLocker::~ObjectLocker() {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Wait/Notify/NotifyAll
|
||||
// NOTE: must use heavy weight monitor to handle wait()
|
||||
|
||||
int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
|
||||
JavaThread* current = THREAD;
|
||||
if (millis < 0) {
|
||||
THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
|
||||
}
|
||||
// The ObjectMonitor* can't be async deflated because the _waiters
|
||||
// field is incremented before ownership is dropped and decremented
|
||||
// after ownership is regained.
|
||||
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_wait);
|
||||
|
||||
ObjectMonitor* monitor;
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj(), inflate_cause_wait, CHECK_0);
|
||||
} else {
|
||||
// The ObjectMonitor* can't be async deflated because the _waiters
|
||||
// field is incremented before ownership is dropped and decremented
|
||||
// after ownership is regained.
|
||||
monitor = inflate(current, obj(), inflate_cause_wait);
|
||||
}
|
||||
|
||||
DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), current, millis);
|
||||
monitor->wait(millis, true, THREAD); // Not CHECK as we need following code
|
||||
@ -825,9 +769,14 @@ void ObjectSynchronizer::waitUninterruptibly(Handle obj, jlong millis, TRAPS) {
|
||||
if (millis < 0) {
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
|
||||
}
|
||||
ObjectSynchronizer::inflate(THREAD,
|
||||
obj(),
|
||||
inflate_cause_wait)->wait(millis, false, THREAD);
|
||||
|
||||
ObjectMonitor* monitor;
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj(), inflate_cause_wait, CHECK);
|
||||
} else {
|
||||
monitor = inflate(THREAD, obj(), inflate_cause_wait);
|
||||
}
|
||||
monitor->wait(millis, false, THREAD);
|
||||
}
|
||||
|
||||
|
||||
@ -846,9 +795,15 @@ void ObjectSynchronizer::notify(Handle obj, TRAPS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// The ObjectMonitor* can't be async deflated until ownership is
|
||||
// dropped by the calling thread.
|
||||
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_notify);
|
||||
|
||||
ObjectMonitor* monitor;
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj(), inflate_cause_notify, CHECK);
|
||||
} else {
|
||||
// The ObjectMonitor* can't be async deflated until ownership is
|
||||
// dropped by the calling thread.
|
||||
monitor = inflate(current, obj(), inflate_cause_notify);
|
||||
}
|
||||
monitor->notify(CHECK);
|
||||
}
|
||||
|
||||
@ -868,9 +823,15 @@ void ObjectSynchronizer::notifyall(Handle obj, TRAPS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// The ObjectMonitor* can't be async deflated until ownership is
|
||||
// dropped by the calling thread.
|
||||
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_notify);
|
||||
|
||||
ObjectMonitor* monitor;
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj(), inflate_cause_notify, CHECK);
|
||||
} else {
|
||||
// The ObjectMonitor* can't be async deflated until ownership is
|
||||
// dropped by the calling thread.
|
||||
monitor = inflate(current, obj(), inflate_cause_notify);
|
||||
}
|
||||
monitor->notifyAll(CHECK);
|
||||
}
|
||||
|
||||
@ -968,7 +929,7 @@ static markWord read_stable_mark(oop obj) {
|
||||
// There are simple ways to "diffuse" the middle address bits over the
|
||||
// generated hashCode values:
|
||||
|
||||
static inline intptr_t get_next_hash(Thread* current, oop obj) {
|
||||
static intptr_t get_next_hash(Thread* current, oop obj) {
|
||||
intptr_t value = 0;
|
||||
if (hashCode == 0) {
|
||||
// This form uses global Park-Miller RNG.
|
||||
@ -1008,7 +969,33 @@ static inline intptr_t get_next_hash(Thread* current, oop obj) {
|
||||
return value;
|
||||
}
|
||||
|
||||
static intptr_t install_hash_code(Thread* current, oop obj) {
|
||||
assert(UseObjectMonitorTable && LockingMode == LM_LIGHTWEIGHT, "must be");
|
||||
|
||||
markWord mark = obj->mark_acquire();
|
||||
for (;;) {
|
||||
intptr_t hash = mark.hash();
|
||||
if (hash != 0) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
hash = get_next_hash(current, obj);
|
||||
const markWord old_mark = mark;
|
||||
const markWord new_mark = old_mark.copy_set_hash(hash);
|
||||
|
||||
mark = obj->cas_set_mark(new_mark, old_mark);
|
||||
if (old_mark == mark) {
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
|
||||
if (UseObjectMonitorTable) {
|
||||
// Since the monitor isn't in the object header, the hash can simply be
|
||||
// installed in the object header.
|
||||
return install_hash_code(current, obj);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
ObjectMonitor* monitor = nullptr;
|
||||
@ -1102,7 +1089,7 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
|
||||
hash = get_next_hash(current, obj); // get a new hash
|
||||
temp = mark.copy_set_hash(hash) ; // merge the hash into header
|
||||
assert(temp.is_neutral(), "invariant: header=" INTPTR_FORMAT, temp.value());
|
||||
uintptr_t v = Atomic::cmpxchg((volatile uintptr_t*)monitor->header_addr(), mark.value(), temp.value());
|
||||
uintptr_t v = Atomic::cmpxchg(monitor->metadata_addr(), mark.value(), temp.value());
|
||||
test = markWord(v);
|
||||
if (test != mark) {
|
||||
// The attempt to update the ObjectMonitor's header/dmw field
|
||||
@ -1114,7 +1101,7 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
|
||||
assert(test.is_neutral(), "invariant: header=" INTPTR_FORMAT, test.value());
|
||||
assert(hash != 0, "should only have lost the race to a thread that set a non-zero hash");
|
||||
}
|
||||
if (monitor->is_being_async_deflated()) {
|
||||
if (monitor->is_being_async_deflated() && !UseObjectMonitorTable) {
|
||||
// If we detect that async deflation has occurred, then we
|
||||
// attempt to restore the header/dmw to the object's header
|
||||
// so that we only retry once if the deflater thread happens
|
||||
@ -1145,11 +1132,25 @@ bool ObjectSynchronizer::current_thread_holds_lock(JavaThread* current,
|
||||
return current->lock_stack().contains(h_obj());
|
||||
}
|
||||
|
||||
if (mark.has_monitor()) {
|
||||
while (LockingMode == LM_LIGHTWEIGHT && mark.has_monitor()) {
|
||||
ObjectMonitor* monitor = read_monitor(current, obj, mark);
|
||||
if (monitor != nullptr) {
|
||||
return monitor->is_entered(current) != 0;
|
||||
}
|
||||
// Racing with inflation/deflation, retry
|
||||
mark = obj->mark_acquire();
|
||||
|
||||
if (mark.is_fast_locked()) {
|
||||
// Some other thread fast_locked, current could not have held the lock
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (LockingMode != LM_LIGHTWEIGHT && mark.has_monitor()) {
|
||||
// Inflated monitor so header points to ObjectMonitor (tagged pointer).
|
||||
// The first stage of async deflation does not affect any field
|
||||
// used by this comparison so the ObjectMonitor* is usable here.
|
||||
ObjectMonitor* monitor = mark.monitor();
|
||||
ObjectMonitor* monitor = read_monitor(mark);
|
||||
return monitor->is_entered(current) != 0;
|
||||
}
|
||||
// Unlocked case, header in place
|
||||
@ -1173,11 +1174,25 @@ JavaThread* ObjectSynchronizer::get_lock_owner(ThreadsList * t_list, Handle h_ob
|
||||
return Threads::owning_thread_from_object(t_list, h_obj());
|
||||
}
|
||||
|
||||
if (mark.has_monitor()) {
|
||||
while (LockingMode == LM_LIGHTWEIGHT && mark.has_monitor()) {
|
||||
ObjectMonitor* monitor = read_monitor(Thread::current(), obj, mark);
|
||||
if (monitor != nullptr) {
|
||||
return Threads::owning_thread_from_monitor(t_list, monitor);
|
||||
}
|
||||
// Racing with inflation/deflation, retry
|
||||
mark = obj->mark_acquire();
|
||||
|
||||
if (mark.is_fast_locked()) {
|
||||
// Some other thread fast_locked
|
||||
return Threads::owning_thread_from_object(t_list, h_obj());
|
||||
}
|
||||
}
|
||||
|
||||
if (LockingMode != LM_LIGHTWEIGHT && mark.has_monitor()) {
|
||||
// Inflated monitor so header points to ObjectMonitor (tagged pointer).
|
||||
// The first stage of async deflation does not affect any field
|
||||
// used by this comparison so the ObjectMonitor* is usable here.
|
||||
ObjectMonitor* monitor = mark.monitor();
|
||||
ObjectMonitor* monitor = read_monitor(mark);
|
||||
assert(monitor != nullptr, "monitor should be non-null");
|
||||
// owning_thread_from_monitor() may also return null here:
|
||||
return Threads::owning_thread_from_monitor(t_list, monitor);
|
||||
@ -1389,9 +1404,10 @@ static void post_monitor_inflate_event(EventJavaMonitorInflate* event,
|
||||
|
||||
// Fast path code shared by multiple functions
|
||||
void ObjectSynchronizer::inflate_helper(oop obj) {
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "only inflate through enter");
|
||||
markWord mark = obj->mark_acquire();
|
||||
if (mark.has_monitor()) {
|
||||
ObjectMonitor* monitor = mark.monitor();
|
||||
ObjectMonitor* monitor = read_monitor(mark);
|
||||
markWord dmw = monitor->header();
|
||||
assert(dmw.is_neutral(), "sanity check: header=" INTPTR_FORMAT, dmw.value());
|
||||
return;
|
||||
@ -1401,39 +1417,25 @@ void ObjectSynchronizer::inflate_helper(oop obj) {
|
||||
|
||||
ObjectMonitor* ObjectSynchronizer::inflate(Thread* current, oop obj, const InflateCause cause) {
|
||||
assert(current == Thread::current(), "must be");
|
||||
if (LockingMode == LM_LIGHTWEIGHT && current->is_Java_thread()) {
|
||||
return inflate_impl(JavaThread::cast(current), obj, cause);
|
||||
}
|
||||
return inflate_impl(nullptr, obj, cause);
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "only inflate through enter");
|
||||
return inflate_impl(obj, cause);
|
||||
}
|
||||
|
||||
ObjectMonitor* ObjectSynchronizer::inflate_for(JavaThread* thread, oop obj, const InflateCause cause) {
|
||||
assert(thread == Thread::current() || thread->is_obj_deopt_suspend(), "must be");
|
||||
return inflate_impl(thread, obj, cause);
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "LM_LIGHTWEIGHT cannot use inflate_for");
|
||||
return inflate_impl(obj, cause);
|
||||
}
|
||||
|
||||
ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* inflating_thread, oop object, const InflateCause cause) {
|
||||
// The JavaThread* inflating_thread parameter is only used by LM_LIGHTWEIGHT and requires
|
||||
// that the inflating_thread == Thread::current() or is suspended throughout the call by
|
||||
// some other mechanism.
|
||||
// Even with LM_LIGHTWEIGHT the thread might be nullptr when called from a non
|
||||
// JavaThread. (As may still be the case from FastHashCode). However it is only
|
||||
// important for the correctness of the LM_LIGHTWEIGHT algorithm that the thread
|
||||
// is set when called from ObjectSynchronizer::enter from the owning thread,
|
||||
// ObjectSynchronizer::enter_for from any thread, or ObjectSynchronizer::exit.
|
||||
ObjectMonitor* ObjectSynchronizer::inflate_impl(oop object, const InflateCause cause) {
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "LM_LIGHTWEIGHT cannot use inflate_impl");
|
||||
EventJavaMonitorInflate event;
|
||||
|
||||
for (;;) {
|
||||
const markWord mark = object->mark_acquire();
|
||||
|
||||
// The mark can be in one of the following states:
|
||||
// * inflated - Just return if using stack-locking.
|
||||
// If using fast-locking and the ObjectMonitor owner
|
||||
// is anonymous and the inflating_thread owns the
|
||||
// object lock, then we make the inflating_thread
|
||||
// the ObjectMonitor owner and remove the lock from
|
||||
// the inflating_thread's lock stack.
|
||||
// * fast-locked - Coerce it to inflated from fast-locked.
|
||||
// * inflated - Just return it.
|
||||
// * stack-locked - Coerce it to inflated from stack-locked.
|
||||
// * INFLATING - Busy wait for conversion from stack-locked to
|
||||
// inflated.
|
||||
@ -1444,80 +1446,18 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* inflating_thread, oo
|
||||
ObjectMonitor* inf = mark.monitor();
|
||||
markWord dmw = inf->header();
|
||||
assert(dmw.is_neutral(), "invariant: header=" INTPTR_FORMAT, dmw.value());
|
||||
if (LockingMode == LM_LIGHTWEIGHT && inf->is_owner_anonymous() &&
|
||||
inflating_thread != nullptr && inflating_thread->lock_stack().contains(object)) {
|
||||
inf->set_owner_from_anonymous(inflating_thread);
|
||||
size_t removed = inflating_thread->lock_stack().remove(object);
|
||||
inf->set_recursions(removed - 1);
|
||||
}
|
||||
return inf;
|
||||
}
|
||||
|
||||
if (LockingMode != LM_LIGHTWEIGHT) {
|
||||
// New lightweight locking does not use INFLATING.
|
||||
// CASE: inflation in progress - inflating over a stack-lock.
|
||||
// Some other thread is converting from stack-locked to inflated.
|
||||
// Only that thread can complete inflation -- other threads must wait.
|
||||
// The INFLATING value is transient.
|
||||
// Currently, we spin/yield/park and poll the markword, waiting for inflation to finish.
|
||||
// We could always eliminate polling by parking the thread on some auxiliary list.
|
||||
if (mark == markWord::INFLATING()) {
|
||||
read_stable_mark(object);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: fast-locked
|
||||
// Could be fast-locked either by the inflating_thread or by some other thread.
|
||||
//
|
||||
// Note that we allocate the ObjectMonitor speculatively, _before_
|
||||
// attempting to set the object's mark to the new ObjectMonitor. If
|
||||
// the inflating_thread owns the monitor, then we set the ObjectMonitor's
|
||||
// owner to the inflating_thread. Otherwise, we set the ObjectMonitor's owner
|
||||
// to anonymous. If we lose the race to set the object's mark to the
|
||||
// new ObjectMonitor, then we just delete it and loop around again.
|
||||
//
|
||||
LogStreamHandle(Trace, monitorinflation) lsh;
|
||||
if (LockingMode == LM_LIGHTWEIGHT && mark.is_fast_locked()) {
|
||||
ObjectMonitor* monitor = new ObjectMonitor(object);
|
||||
monitor->set_header(mark.set_unlocked());
|
||||
bool own = inflating_thread != nullptr && inflating_thread->lock_stack().contains(object);
|
||||
if (own) {
|
||||
// Owned by inflating_thread.
|
||||
monitor->set_owner_from(nullptr, inflating_thread);
|
||||
} else {
|
||||
// Owned by somebody else.
|
||||
monitor->set_owner_anonymous();
|
||||
}
|
||||
markWord monitor_mark = markWord::encode(monitor);
|
||||
markWord old_mark = object->cas_set_mark(monitor_mark, mark);
|
||||
if (old_mark == mark) {
|
||||
// Success! Return inflated monitor.
|
||||
if (own) {
|
||||
size_t removed = inflating_thread->lock_stack().remove(object);
|
||||
monitor->set_recursions(removed - 1);
|
||||
}
|
||||
// Once the ObjectMonitor is configured and object is associated
|
||||
// with the ObjectMonitor, it is safe to allow async deflation:
|
||||
_in_use_list.add(monitor);
|
||||
|
||||
// Hopefully the performance counters are allocated on distinct
|
||||
// cache lines to avoid false sharing on MP systems ...
|
||||
OM_PERFDATA_OP(Inflations, inc());
|
||||
if (log_is_enabled(Trace, monitorinflation)) {
|
||||
ResourceMark rm;
|
||||
lsh.print_cr("inflate(has_locker): object=" INTPTR_FORMAT ", mark="
|
||||
INTPTR_FORMAT ", type='%s'", p2i(object),
|
||||
object->mark().value(), object->klass()->external_name());
|
||||
}
|
||||
if (event.should_commit()) {
|
||||
post_monitor_inflate_event(&event, object, cause);
|
||||
}
|
||||
return monitor;
|
||||
} else {
|
||||
delete monitor;
|
||||
continue; // Interference -- just retry
|
||||
}
|
||||
// CASE: inflation in progress - inflating over a stack-lock.
|
||||
// Some other thread is converting from stack-locked to inflated.
|
||||
// Only that thread can complete inflation -- other threads must wait.
|
||||
// The INFLATING value is transient.
|
||||
// Currently, we spin/yield/park and poll the markword, waiting for inflation to finish.
|
||||
// We could always eliminate polling by parking the thread on some auxiliary list.
|
||||
if (mark == markWord::INFLATING()) {
|
||||
read_stable_mark(object);
|
||||
continue;
|
||||
}
|
||||
|
||||
// CASE: stack-locked
|
||||
@ -1531,8 +1471,8 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* inflating_thread, oo
|
||||
// the odds of inflation contention. If we lose the race to set INFLATING,
|
||||
// then we just delete the ObjectMonitor and loop around again.
|
||||
//
|
||||
LogStreamHandle(Trace, monitorinflation) lsh;
|
||||
if (LockingMode == LM_LEGACY && mark.has_locker()) {
|
||||
assert(LockingMode != LM_LIGHTWEIGHT, "cannot happen with new lightweight locking");
|
||||
ObjectMonitor* m = new ObjectMonitor(object);
|
||||
// Optimistically prepare the ObjectMonitor - anticipate successful CAS
|
||||
// We do this before the CAS in order to minimize the length of time
|
||||
@ -1664,13 +1604,14 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* inflating_thread, oo
|
||||
size_t ObjectSynchronizer::deflate_monitor_list(ObjectMonitorDeflationSafepointer* safepointer) {
|
||||
MonitorList::Iterator iter = _in_use_list.iterator();
|
||||
size_t deflated_count = 0;
|
||||
Thread* current = Thread::current();
|
||||
|
||||
while (iter.has_next()) {
|
||||
if (deflated_count >= (size_t)MonitorDeflationMax) {
|
||||
break;
|
||||
}
|
||||
ObjectMonitor* mid = iter.next();
|
||||
if (mid->deflate_monitor()) {
|
||||
if (mid->deflate_monitor(current)) {
|
||||
deflated_count++;
|
||||
}
|
||||
|
||||
@ -1688,6 +1629,11 @@ class HandshakeForDeflation : public HandshakeClosure {
|
||||
void do_thread(Thread* thread) {
|
||||
log_trace(monitorinflation)("HandshakeForDeflation::do_thread: thread="
|
||||
INTPTR_FORMAT, p2i(thread));
|
||||
if (thread->is_Java_thread()) {
|
||||
// Clear OM cache
|
||||
JavaThread* jt = JavaThread::cast(thread);
|
||||
jt->om_clear_monitor_cache();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1834,6 +1780,14 @@ size_t ObjectSynchronizer::deflate_idle_monitors() {
|
||||
GrowableArray<ObjectMonitor*> delete_list((int)deflated_count);
|
||||
unlinked_count = _in_use_list.unlink_deflated(deflated_count, &delete_list, &safepointer);
|
||||
|
||||
#ifdef ASSERT
|
||||
if (UseObjectMonitorTable) {
|
||||
for (ObjectMonitor* monitor : delete_list) {
|
||||
assert(!LightweightSynchronizer::contains_monitor(current, monitor), "Should have been removed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
log.before_handshake(unlinked_count);
|
||||
|
||||
// A JavaThread needs to handshake in order to safely free the
|
||||
@ -2042,29 +1996,35 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out,
|
||||
return;
|
||||
}
|
||||
|
||||
if (n->header().value() == 0) {
|
||||
|
||||
if (n->metadata() == 0) {
|
||||
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor must "
|
||||
"have non-null _header field.", p2i(n));
|
||||
"have non-null _metadata (header/hash) field.", p2i(n));
|
||||
*error_cnt_p = *error_cnt_p + 1;
|
||||
}
|
||||
|
||||
const oop obj = n->object_peek();
|
||||
if (obj != nullptr) {
|
||||
const markWord mark = obj->mark();
|
||||
if (!mark.has_monitor()) {
|
||||
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
|
||||
"object does not think it has a monitor: obj="
|
||||
INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n),
|
||||
p2i(obj), mark.value());
|
||||
*error_cnt_p = *error_cnt_p + 1;
|
||||
}
|
||||
ObjectMonitor* const obj_mon = mark.monitor();
|
||||
if (n != obj_mon) {
|
||||
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
|
||||
"object does not refer to the same monitor: obj="
|
||||
INTPTR_FORMAT ", mark=" INTPTR_FORMAT ", obj_mon="
|
||||
INTPTR_FORMAT, p2i(n), p2i(obj), mark.value(), p2i(obj_mon));
|
||||
*error_cnt_p = *error_cnt_p + 1;
|
||||
}
|
||||
if (obj == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const markWord mark = obj->mark();
|
||||
if (!mark.has_monitor()) {
|
||||
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
|
||||
"object does not think it has a monitor: obj="
|
||||
INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n),
|
||||
p2i(obj), mark.value());
|
||||
*error_cnt_p = *error_cnt_p + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectMonitor* const obj_mon = read_monitor(Thread::current(), obj, mark);
|
||||
if (n != obj_mon) {
|
||||
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
|
||||
"object does not refer to the same monitor: obj="
|
||||
INTPTR_FORMAT ", mark=" INTPTR_FORMAT ", obj_mon="
|
||||
INTPTR_FORMAT, p2i(n), p2i(obj), mark.value(), p2i(obj_mon));
|
||||
*error_cnt_p = *error_cnt_p + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2087,10 +2047,10 @@ void ObjectSynchronizer::log_in_use_monitor_details(outputStream* out, bool log_
|
||||
monitors_iterate([&](ObjectMonitor* monitor) {
|
||||
if (is_interesting(monitor)) {
|
||||
const oop obj = monitor->object_peek();
|
||||
const markWord mark = monitor->header();
|
||||
const intptr_t hash = UseObjectMonitorTable ? monitor->hash() : monitor->header().hash();
|
||||
ResourceMark rm;
|
||||
out->print(INTPTR_FORMAT " %d%d%d " INTPTR_FORMAT " %s", p2i(monitor),
|
||||
monitor->is_busy(), mark.hash() != 0, monitor->owner() != nullptr,
|
||||
monitor->is_busy(), hash != 0, monitor->owner() != nullptr,
|
||||
p2i(obj), obj == nullptr ? "" : obj->klass()->external_name());
|
||||
if (monitor->is_busy()) {
|
||||
out->print(" (%s)", monitor->is_busy_to_string(&ss));
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "oops/markWord.hpp"
|
||||
#include "runtime/basicLock.hpp"
|
||||
#include "runtime/handles.hpp"
|
||||
#include "runtime/javaThread.hpp"
|
||||
#include "utilities/resourceHash.hpp"
|
||||
|
||||
template <typename T> class GrowableArray;
|
||||
@ -93,8 +94,9 @@ class ObjectSynchronizer : AllStatic {
|
||||
// deoptimization at monitor exit. Hence, it does not take a Handle argument.
|
||||
|
||||
// This is the "slow path" version of monitor enter and exit.
|
||||
static void enter(Handle obj, BasicLock* lock, JavaThread* current);
|
||||
static void exit(oop obj, BasicLock* lock, JavaThread* current);
|
||||
static inline void enter(Handle obj, BasicLock* lock, JavaThread* current);
|
||||
static inline void exit(oop obj, BasicLock* lock, JavaThread* current);
|
||||
|
||||
// Used to enter a monitor for another thread. This requires that the
|
||||
// locking_thread is suspended, and that entering on a potential
|
||||
// inflated monitor may only contend with deflation. That is the obj being
|
||||
@ -106,6 +108,9 @@ private:
|
||||
// inflated monitor enter.
|
||||
static bool enter_fast_impl(Handle obj, BasicLock* lock, JavaThread* locking_thread);
|
||||
|
||||
static bool quick_enter_legacy(oop obj, BasicLock* Lock, JavaThread* current);
|
||||
static void enter_legacy(Handle obj, BasicLock* Lock, JavaThread* current);
|
||||
static void exit_legacy(oop obj, BasicLock* lock, JavaThread* current);
|
||||
public:
|
||||
// Used only to handle jni locks or other unmatched monitor enter/exit
|
||||
// Internally they will use heavy weight monitor.
|
||||
@ -118,7 +123,7 @@ public:
|
||||
static void notifyall(Handle obj, TRAPS);
|
||||
|
||||
static bool quick_notify(oopDesc* obj, JavaThread* current, bool All);
|
||||
static bool quick_enter(oop obj, JavaThread* current, BasicLock* Lock);
|
||||
static inline bool quick_enter(oop obj, BasicLock* Lock, JavaThread* current);
|
||||
|
||||
// Special internal-use-only method for use by JVM infrastructure
|
||||
// that needs to wait() on a java-level object but that can't risk
|
||||
@ -132,13 +137,16 @@ public:
|
||||
|
||||
private:
|
||||
// Shared implementation between the different LockingMode.
|
||||
static ObjectMonitor* inflate_impl(JavaThread* thread, oop obj, const InflateCause cause);
|
||||
static ObjectMonitor* inflate_impl(oop obj, const InflateCause cause);
|
||||
|
||||
public:
|
||||
// This version is only for internal use
|
||||
static void inflate_helper(oop obj);
|
||||
static const char* inflate_cause_name(const InflateCause cause);
|
||||
|
||||
inline static ObjectMonitor* read_monitor(markWord mark);
|
||||
inline static ObjectMonitor* read_monitor(Thread* current, oop obj, markWord mark);
|
||||
|
||||
// Returns the identity hash value for an oop
|
||||
// NOTE: It may cause monitor inflation
|
||||
static intptr_t FastHashCode(Thread* current, oop obj);
|
||||
@ -200,6 +208,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class SynchronizerTest;
|
||||
friend class LightweightSynchronizer;
|
||||
|
||||
static MonitorList _in_use_list;
|
||||
static volatile bool _is_async_deflation_requested;
|
||||
|
81
src/hotspot/share/runtime/synchronizer.inline.hpp
Normal file
81
src/hotspot/share/runtime/synchronizer.inline.hpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_RUNTIME_SYNCHRONIZER_INLINE_HPP
|
||||
#define SHARE_RUNTIME_SYNCHRONIZER_INLINE_HPP
|
||||
|
||||
#include "runtime/synchronizer.hpp"
|
||||
|
||||
#include "runtime/lightweightSynchronizer.hpp"
|
||||
#include "runtime/safepointVerifiers.hpp"
|
||||
|
||||
inline ObjectMonitor* ObjectSynchronizer::read_monitor(markWord mark) {
|
||||
return mark.monitor();
|
||||
}
|
||||
|
||||
inline ObjectMonitor* ObjectSynchronizer::read_monitor(Thread* current, oop obj, markWord mark) {
|
||||
if (!UseObjectMonitorTable) {
|
||||
return read_monitor(mark);
|
||||
} else {
|
||||
return LightweightSynchronizer::get_monitor_from_table(current, obj);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) {
|
||||
assert(current == Thread::current(), "must be");
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
LightweightSynchronizer::enter(obj, lock, current);
|
||||
} else {
|
||||
enter_legacy(obj, lock, current);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ObjectSynchronizer::quick_enter(oop obj, BasicLock* lock, JavaThread* current) {
|
||||
assert(current->thread_state() == _thread_in_Java, "invariant");
|
||||
NoSafepointVerifier nsv;
|
||||
if (obj == nullptr) return false; // Need to throw NPE
|
||||
|
||||
if (obj->klass()->is_value_based()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
return LightweightSynchronizer::quick_enter(obj, lock, current);
|
||||
} else {
|
||||
return quick_enter_legacy(obj, lock, current);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) {
|
||||
current->dec_held_monitor_count();
|
||||
|
||||
if (LockingMode == LM_LIGHTWEIGHT) {
|
||||
LightweightSynchronizer::exit(object, current);
|
||||
} else {
|
||||
exit_legacy(object, lock, current);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SHARE_RUNTIME_SYNCHRONIZER_INLINE_HPP
|
@ -50,7 +50,7 @@
|
||||
#include "runtime/signature.hpp"
|
||||
#include "runtime/stackFrameStream.inline.hpp"
|
||||
#include "runtime/stubRoutines.hpp"
|
||||
#include "runtime/synchronizer.hpp"
|
||||
#include "runtime/synchronizer.inline.hpp"
|
||||
#include "runtime/vframe.inline.hpp"
|
||||
#include "runtime/vframeArray.hpp"
|
||||
#include "runtime/vframe_hp.hpp"
|
||||
@ -247,13 +247,16 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) {
|
||||
markWord mark = monitor->owner()->mark();
|
||||
// The first stage of async deflation does not affect any field
|
||||
// used by this comparison so the ObjectMonitor* is usable here.
|
||||
if (mark.has_monitor() &&
|
||||
( // we have marked ourself as pending on this monitor
|
||||
mark.monitor() == thread()->current_pending_monitor() ||
|
||||
if (mark.has_monitor()) {
|
||||
ObjectMonitor* mon = ObjectSynchronizer::read_monitor(current, monitor->owner(), mark);
|
||||
if (// if the monitor is null we must be in the process of locking
|
||||
mon == nullptr ||
|
||||
// we have marked ourself as pending on this monitor
|
||||
mon == thread()->current_pending_monitor() ||
|
||||
// we are not the owner of this monitor
|
||||
!mark.monitor()->is_entered(thread())
|
||||
)) {
|
||||
lock_state = "waiting to lock";
|
||||
!mon->is_entered(thread())) {
|
||||
lock_state = "waiting to lock";
|
||||
}
|
||||
}
|
||||
}
|
||||
print_locked_object_class_name(st, Handle(current, monitor->owner()), lock_state);
|
||||
|
@ -777,11 +777,11 @@
|
||||
/* Monitors */ \
|
||||
/************/ \
|
||||
\
|
||||
volatile_nonstatic_field(ObjectMonitor, _header, markWord) \
|
||||
volatile_nonstatic_field(ObjectMonitor, _metadata, uintptr_t) \
|
||||
unchecked_nonstatic_field(ObjectMonitor, _object, sizeof(void *)) /* NOTE: no type */ \
|
||||
unchecked_nonstatic_field(ObjectMonitor, _owner, sizeof(void *)) /* NOTE: no type */ \
|
||||
volatile_nonstatic_field(ObjectMonitor, _next_om, ObjectMonitor*) \
|
||||
volatile_nonstatic_field(BasicLock, _displaced_header, markWord) \
|
||||
volatile_nonstatic_field(BasicLock, _metadata, uintptr_t) \
|
||||
nonstatic_field(ObjectMonitor, _contentions, int) \
|
||||
volatile_nonstatic_field(ObjectMonitor, _waiters, int) \
|
||||
volatile_nonstatic_field(ObjectMonitor, _recursions, intx) \
|
||||
|
@ -156,6 +156,16 @@ public class Mark extends VMObject {
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(hasMonitor(), "check");
|
||||
}
|
||||
if (VM.getVM().getCommandLineFlag("UseObjectMonitorTable").getBool()) {
|
||||
Iterator it = ObjectSynchronizer.objectMonitorIterator();
|
||||
while (it != null && it.hasNext()) {
|
||||
ObjectMonitor mon = (ObjectMonitor)it.next();
|
||||
if (getAddress().equals(mon.object())) {
|
||||
return mon;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// Use xor instead of &~ to provide one extra tag-bit check.
|
||||
Address monAddr = valueAsAddress().xorWithMask(monitorValue);
|
||||
return new ObjectMonitor(monAddr);
|
||||
|
@ -43,7 +43,7 @@ public class BasicLock extends VMObject {
|
||||
|
||||
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
|
||||
Type type = db.lookupType("BasicLock");
|
||||
displacedHeaderField = type.getCIntegerField("_displaced_header");
|
||||
displacedHeaderField = type.getCIntegerField("_metadata");
|
||||
}
|
||||
|
||||
private static CIntegerField displacedHeaderField;
|
||||
|
@ -45,8 +45,8 @@ public class ObjectMonitor extends VMObject {
|
||||
heap = VM.getVM().getObjectHeap();
|
||||
Type type = db.lookupType("ObjectMonitor");
|
||||
|
||||
sun.jvm.hotspot.types.Field f = type.getField("_header");
|
||||
headerFieldOffset = f.getOffset();
|
||||
sun.jvm.hotspot.types.Field f = type.getField("_metadata");
|
||||
metadataFieldOffset = f.getOffset();
|
||||
f = type.getField("_object");
|
||||
objectFieldOffset = f.getOffset();
|
||||
f = type.getField("_owner");
|
||||
@ -65,7 +65,7 @@ public class ObjectMonitor extends VMObject {
|
||||
}
|
||||
|
||||
public Mark header() {
|
||||
return new Mark(addr.addOffsetTo(headerFieldOffset));
|
||||
return new Mark(addr.addOffsetTo(metadataFieldOffset));
|
||||
}
|
||||
|
||||
// FIXME
|
||||
@ -114,7 +114,7 @@ public class ObjectMonitor extends VMObject {
|
||||
// vmStructs.cpp because they aren't strongly typed in the VM, or
|
||||
// would confuse the SA's type system.
|
||||
private static ObjectHeap heap;
|
||||
private static long headerFieldOffset;
|
||||
private static long metadataFieldOffset;
|
||||
private static long objectFieldOffset;
|
||||
private static long ownerFieldOffset;
|
||||
private static long nextOMFieldOffset;
|
||||
|
@ -55,6 +55,9 @@ public class ObjectSynchronizer {
|
||||
// FIXME: can not generate marks in debugging system
|
||||
return mark.hash();
|
||||
} else if (mark.hasMonitor()) {
|
||||
if (VM.getVM().getCommandLineFlag("UseObjectMonitorTable").getBool()) {
|
||||
return mark.hash();
|
||||
}
|
||||
ObjectMonitor monitor = mark.monitor();
|
||||
Mark temp = monitor.header();
|
||||
return temp.hash();
|
||||
|
@ -27,20 +27,20 @@
|
||||
#include "unittest.hpp"
|
||||
|
||||
TEST_VM(ObjectMonitor, sanity) {
|
||||
uint cache_line_size = VM_Version::L1_data_cache_line_size();
|
||||
uint cache_line_size = VM_Version::L1_data_cache_line_size();
|
||||
|
||||
if (cache_line_size != 0) {
|
||||
// We were able to determine the L1 data cache line size so
|
||||
// do some cache line specific sanity checks
|
||||
EXPECT_EQ((size_t) 0, sizeof (PaddedEnd<ObjectMonitor>) % cache_line_size)
|
||||
<< "PaddedEnd<ObjectMonitor> size is not a "
|
||||
<< "multiple of a cache line which permits false sharing. "
|
||||
<< "sizeof(PaddedEnd<ObjectMonitor>) = "
|
||||
<< sizeof (PaddedEnd<ObjectMonitor>)
|
||||
<< "; cache_line_size = " << cache_line_size;
|
||||
if (cache_line_size != 0) {
|
||||
|
||||
EXPECT_GE((size_t) in_bytes(ObjectMonitor::owner_offset()), cache_line_size)
|
||||
<< "the _header and _owner fields are closer "
|
||||
EXPECT_EQ(in_bytes(ObjectMonitor::metadata_offset()), 0)
|
||||
<< "_metadata at a non 0 offset. metadata_offset = "
|
||||
<< in_bytes(ObjectMonitor::metadata_offset());
|
||||
|
||||
EXPECT_GE((size_t) in_bytes(ObjectMonitor::owner_offset()), cache_line_size)
|
||||
<< "the _metadata and _owner fields are closer "
|
||||
<< "than a cache line which permits false sharing.";
|
||||
|
||||
EXPECT_GE((size_t) in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), cache_line_size)
|
||||
<< "the _owner and _recursions fields are closer "
|
||||
<< "than a cache line which permits false sharing.";
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=NormalDeflation
|
||||
* @summary A collection of small tests using synchronized, wait, notify to try
|
||||
* and achieve good cheap coverage of UseObjectMonitorTable.
|
||||
* @library /test/lib
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
||||
* -XX:+UseObjectMonitorTable
|
||||
* UseObjectMonitorTableTest
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=ExtremeDeflation
|
||||
* @summary Run the same tests but with deflation running constantly.
|
||||
* @library /test/lib
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
||||
* -XX:GuaranteedAsyncDeflationInterval=1
|
||||
* -XX:+UseObjectMonitorTable
|
||||
* UseObjectMonitorTableTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
import java.lang.Runnable;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class UseObjectMonitorTableTest {
|
||||
static final ThreadFactory TF = Executors.defaultThreadFactory();
|
||||
|
||||
static class WaitNotifyTest implements Runnable {
|
||||
static final int ITERATIONS = 10_000;
|
||||
static final int THREADS = 10;
|
||||
final WaitNotifySyncChannel startLatchChannel = new WaitNotifySyncChannel();
|
||||
final WaitNotifySyncChannel endLatchChannel = new WaitNotifySyncChannel();
|
||||
int count = 0;
|
||||
|
||||
static class WaitNotifyCountDownLatch {
|
||||
int latch;
|
||||
WaitNotifyCountDownLatch(int count) {
|
||||
latch = count;
|
||||
}
|
||||
synchronized void await() {
|
||||
while (latch != 0) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("WaitNotifyTest: Unexpected interrupt", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized void countDown() {
|
||||
if (latch != 0) {
|
||||
latch--;
|
||||
if (latch == 0) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static class WaitNotifySyncChannel extends WaitNotifyCountDownLatch {
|
||||
WaitNotifyCountDownLatch object;
|
||||
WaitNotifySyncChannel() { super(0); }
|
||||
synchronized void send(WaitNotifyCountDownLatch object, int count) {
|
||||
await();
|
||||
latch = count;
|
||||
this.object = object;
|
||||
notifyAll();
|
||||
}
|
||||
synchronized WaitNotifyCountDownLatch receive() {
|
||||
while (latch == 0) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("WaitNotifyTest: Unexpected interrupt", e);
|
||||
}
|
||||
}
|
||||
countDown();
|
||||
return object;
|
||||
}
|
||||
}
|
||||
synchronized int getCount() {
|
||||
return count;
|
||||
}
|
||||
synchronized void increment() {
|
||||
count++;
|
||||
}
|
||||
public void run() {
|
||||
System.out.println("WaitNotifyTest started.");
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
TF.newThread(() -> {
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
startLatchChannel.receive().await();
|
||||
increment();
|
||||
endLatchChannel.receive().countDown();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
WaitNotifyCountDownLatch startLatch = new WaitNotifyCountDownLatch(1);
|
||||
WaitNotifyCountDownLatch endLatch = new WaitNotifyCountDownLatch(THREADS);
|
||||
int count = getCount();
|
||||
if (count != i * THREADS) {
|
||||
throw new RuntimeException("WaitNotifyTest: Invalid Count " + count +
|
||||
" pre-iteration " + i);
|
||||
}
|
||||
startLatchChannel.send(startLatch, 10);
|
||||
startLatch.countDown();
|
||||
endLatchChannel.send(endLatch, 10);
|
||||
endLatch.await();
|
||||
}
|
||||
int count = getCount();
|
||||
if (count != ITERATIONS * THREADS) {
|
||||
throw new RuntimeException("WaitNotifyTest: Invalid Count " + count);
|
||||
}
|
||||
System.out.println("WaitNotifyTest passed.");
|
||||
}
|
||||
}
|
||||
|
||||
static class RandomDepthTest implements Runnable {
|
||||
static final int THREADS = 10;
|
||||
static final int ITERATIONS = 10_000;
|
||||
static final int MAX_DEPTH = 20;
|
||||
static final int MAX_RECURSION_COUNT = 10;
|
||||
static final double RECURSION_CHANCE = .25;
|
||||
final Random random = Utils.getRandomInstance();
|
||||
final Locker lockers[] = new Locker[MAX_DEPTH];
|
||||
final CyclicBarrier syncBarrier = new CyclicBarrier(THREADS + 1);
|
||||
int count = 0;
|
||||
|
||||
class Locker {
|
||||
final int depth;
|
||||
Locker(int depth) {
|
||||
this.depth = depth;
|
||||
}
|
||||
synchronized int getCount() {
|
||||
if (depth == MAX_DEPTH) {
|
||||
return count;
|
||||
}
|
||||
return lockers[depth].getCount();
|
||||
}
|
||||
synchronized void increment(int recursion_count) {
|
||||
if (recursion_count != MAX_RECURSION_COUNT &&
|
||||
random.nextDouble() < RECURSION_CHANCE) {
|
||||
this.increment(recursion_count + 1);
|
||||
return;
|
||||
}
|
||||
if (depth == MAX_DEPTH) {
|
||||
count++;
|
||||
return;
|
||||
}
|
||||
lockers[depth + random.nextInt(MAX_DEPTH - depth)].increment(recursion_count);
|
||||
}
|
||||
synchronized Locker create() {
|
||||
if (depth != MAX_DEPTH) {
|
||||
lockers[depth] = (new Locker(depth + 1)).create();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
int getCount() {
|
||||
return lockers[0].getCount();
|
||||
}
|
||||
void increment() {
|
||||
lockers[random.nextInt(MAX_DEPTH)].increment(0);
|
||||
}
|
||||
void create() {
|
||||
lockers[0] = (new Locker(1)).create();
|
||||
}
|
||||
void syncPoint() {
|
||||
try {
|
||||
syncBarrier.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("RandomDepthTest: Unexpected interrupt", e);
|
||||
} catch (BrokenBarrierException e) {
|
||||
throw new RuntimeException("RandomDepthTest: Unexpected broken barrier", e);
|
||||
}
|
||||
}
|
||||
public void run() {
|
||||
System.out.println("RandomDepthTest started.");
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
TF.newThread(() -> {
|
||||
syncPoint();
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
increment();
|
||||
}
|
||||
syncPoint();
|
||||
}).start();
|
||||
}
|
||||
create();
|
||||
syncPoint();
|
||||
syncPoint();
|
||||
int count = getCount();
|
||||
if (count != THREADS * ITERATIONS) {
|
||||
throw new RuntimeException("RandomDepthTest: Invalid Count " + count);
|
||||
}
|
||||
System.out.println("RandomDepthTest passed.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Stream.of(
|
||||
TF.newThread(new WaitNotifyTest()),
|
||||
TF.newThread(new RandomDepthTest())
|
||||
).map(t -> {
|
||||
t.start();
|
||||
return t;
|
||||
}).forEach(t -> {
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("UseObjectMonitorTableTest: Unexpected interrupt", e);
|
||||
}
|
||||
});
|
||||
|
||||
System.out.println("UseObjectMonitorTableTest passed.");
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ import jdk.test.lib.process.ProcessTools;
|
||||
public class MonitorInflationTest {
|
||||
static void analyzeOutputOn(ProcessBuilder pb) throws Exception {
|
||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||
output.shouldContain("inflate(has_locker):");
|
||||
output.shouldContain("inflate:");
|
||||
output.shouldContain("type='MonitorInflationTest$Waiter'");
|
||||
output.shouldContain("I've been waiting.");
|
||||
output.shouldHaveExitValue(0);
|
||||
|
@ -54,6 +54,8 @@ public class LockUnlock {
|
||||
|
||||
public Object lockObject1;
|
||||
public Object lockObject2;
|
||||
public volatile Object lockObject3Inflated;
|
||||
public volatile Object lockObject4Inflated;
|
||||
public int factorial;
|
||||
public int dummyInt1;
|
||||
public int dummyInt2;
|
||||
@ -62,13 +64,28 @@ public class LockUnlock {
|
||||
public void setup() {
|
||||
lockObject1 = new Object();
|
||||
lockObject2 = new Object();
|
||||
lockObject3Inflated = new Object();
|
||||
lockObject4Inflated = new Object();
|
||||
|
||||
// Inflate the lock to use an ObjectMonitor
|
||||
try {
|
||||
synchronized (lockObject3Inflated) {
|
||||
lockObject3Inflated.wait(1);
|
||||
}
|
||||
synchronized (lockObject4Inflated) {
|
||||
lockObject4Inflated.wait(1);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
dummyInt1 = 47;
|
||||
dummyInt2 = 11; // anything
|
||||
}
|
||||
|
||||
/** Perform a synchronized on a local object within a loop. */
|
||||
@Benchmark
|
||||
public void testSimpleLockUnlock() {
|
||||
public void testBasicSimpleLockUnlockLocal() {
|
||||
Object localObject = lockObject1;
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (localObject) {
|
||||
@ -78,9 +95,43 @@ public class LockUnlock {
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a synchronized on an object within a loop. */
|
||||
@Benchmark
|
||||
public void testBasicSimpleLockUnlock() {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject1) {
|
||||
dummyInt1++;
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a synchronized on a local object within a loop. */
|
||||
@Benchmark
|
||||
public void testInflatedSimpleLockUnlockLocal() {
|
||||
Object localObject = lockObject3Inflated;
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (localObject) {
|
||||
dummyInt1++;
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a synchronized on an object within a loop. */
|
||||
@Benchmark
|
||||
public void testInflatedSimpleLockUnlock() {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject3Inflated) {
|
||||
dummyInt1++;
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a recursive synchronized on a local object within a loop. */
|
||||
@Benchmark
|
||||
public void testRecursiveLockUnlock() {
|
||||
public void testBasicRecursiveLockUnlockLocal() {
|
||||
Object localObject = lockObject1;
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (localObject) {
|
||||
@ -92,9 +143,22 @@ public class LockUnlock {
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a recursive synchronized on an object within a loop. */
|
||||
@Benchmark
|
||||
public void testBasicRecursiveLockUnlock() {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject1) {
|
||||
synchronized (lockObject1) {
|
||||
dummyInt1++;
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform two synchronized after each other on the same local object. */
|
||||
@Benchmark
|
||||
public void testSerialLockUnlock() {
|
||||
public void testBasicSerialLockUnlockLocal() {
|
||||
Object localObject = lockObject1;
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (localObject) {
|
||||
@ -106,6 +170,126 @@ public class LockUnlock {
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform two synchronized after each other on the same object. */
|
||||
@Benchmark
|
||||
public void testBasicSerialLockUnlock() {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject1) {
|
||||
dummyInt1++;
|
||||
}
|
||||
synchronized (lockObject1) {
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform two synchronized after each other on the same local object. */
|
||||
@Benchmark
|
||||
public void testInflatedSerialLockUnlockLocal() {
|
||||
Object localObject = lockObject3Inflated;
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (localObject) {
|
||||
dummyInt1++;
|
||||
}
|
||||
synchronized (localObject) {
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform two synchronized after each other on the same object. */
|
||||
@Benchmark
|
||||
public void testInflatedSerialLockUnlock() {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject3Inflated) {
|
||||
dummyInt1++;
|
||||
}
|
||||
synchronized (lockObject3Inflated) {
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform two synchronized after each other on the same object. */
|
||||
@Benchmark
|
||||
public void testInflatedMultipleSerialLockUnlock() {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject3Inflated) {
|
||||
dummyInt1++;
|
||||
}
|
||||
synchronized (lockObject4Inflated) {
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform two synchronized after each other on the same object. */
|
||||
@Benchmark
|
||||
public void testInflatedMultipleRecursiveLockUnlock() {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject3Inflated) {
|
||||
dummyInt1++;
|
||||
synchronized (lockObject4Inflated) {
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a recursive-only synchronized on a local object within a loop. */
|
||||
@Benchmark
|
||||
public void testInflatedRecursiveOnlyLockUnlockLocal() {
|
||||
Object localObject = lockObject3Inflated;
|
||||
synchronized (localObject) {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (localObject) {
|
||||
dummyInt1++;
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a recursive-only synchronized on an object within a loop. */
|
||||
@Benchmark
|
||||
public void testInflatedRecursiveOnlyLockUnlock() {
|
||||
synchronized (lockObject3Inflated) {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject3Inflated) {
|
||||
dummyInt1++;
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a recursive-only synchronized on a local object within a loop. */
|
||||
@Benchmark
|
||||
public void testInflatedRecursiveLockUnlockLocal() {
|
||||
Object localObject = lockObject3Inflated;
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (localObject) {
|
||||
synchronized (localObject) {
|
||||
dummyInt1++;
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Perform a recursive-only synchronized on an object within a loop. */
|
||||
@Benchmark
|
||||
public void testInflatedRecursiveLockUnlock() {
|
||||
for (int i = 0; i < innerCount; i++) {
|
||||
synchronized (lockObject3Inflated) {
|
||||
synchronized (lockObject3Inflated) {
|
||||
dummyInt1++;
|
||||
dummyInt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs recursive synchronizations on the same local object.
|
||||
* <p/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user