Skip to content

Commit 817edcb

Browse files
Xiaolong Pengshipilev
Xiaolong Peng
authored andcommitted
8331411: Shenandoah: Reconsider spinning duration in ShenandoahLock
Reviewed-by: shade, kdnilsen, wkemper
1 parent bffc848 commit 817edcb

File tree

2 files changed

+36
-25
lines changed

2 files changed

+36
-25
lines changed

src/hotspot/share/gc/shenandoah/shenandoahLock.cpp

+29-20
Original file line numberDiff line numberDiff line change
@@ -32,40 +32,49 @@
3232
#include "runtime/javaThread.hpp"
3333
#include "runtime/os.inline.hpp"
3434

35-
// These are inline variants of Thread::SpinAcquire with optional blocking in VM.
36-
37-
class ShenandoahNoBlockOp : public StackObj {
38-
public:
39-
ShenandoahNoBlockOp(JavaThread* java_thread) {
40-
assert(java_thread == nullptr, "Should not pass anything");
41-
}
42-
};
43-
4435
void ShenandoahLock::contended_lock(bool allow_block_for_safepoint) {
4536
Thread* thread = Thread::current();
4637
if (allow_block_for_safepoint && thread->is_Java_thread()) {
47-
contended_lock_internal<ThreadBlockInVM>(JavaThread::cast(thread));
38+
contended_lock_internal<true>(JavaThread::cast(thread));
4839
} else {
49-
contended_lock_internal<ShenandoahNoBlockOp>(nullptr);
40+
contended_lock_internal<false>(nullptr);
5041
}
5142
}
5243

53-
template<typename BlockOp>
44+
template<bool ALLOW_BLOCK>
5445
void ShenandoahLock::contended_lock_internal(JavaThread* java_thread) {
55-
int ctr = 0;
56-
int yields = 0;
46+
assert(!ALLOW_BLOCK || java_thread != nullptr, "Must have a Java thread when allowing block.");
47+
// Spin this much on multi-processor, do not spin on multi-processor.
48+
int ctr = os::is_MP() ? 0xFF : 0;
49+
// Apply TTAS to avoid more expensive CAS calls if the lock is still held by other thread.
5750
while (Atomic::load(&_state) == locked ||
5851
Atomic::cmpxchg(&_state, unlocked, locked) != unlocked) {
59-
if ((++ctr & 0xFFF) == 0) {
60-
BlockOp block(java_thread);
61-
if (yields > 5) {
62-
os::naked_short_sleep(1);
52+
if (ctr > 0 && !SafepointSynchronize::is_synchronizing()) {
53+
// Lightly contended, spin a little if no safepoint is pending.
54+
SpinPause();
55+
ctr--;
56+
} else if (ALLOW_BLOCK) {
57+
ThreadBlockInVM block(java_thread);
58+
if (SafepointSynchronize::is_synchronizing()) {
59+
// If safepoint is pending, we want to block and allow safepoint to proceed.
60+
// Normally, TBIVM above would block us in its destructor.
61+
//
62+
// But that blocking only happens when TBIVM knows the thread poll is armed.
63+
// There is a window between announcing a safepoint and arming the thread poll
64+
// during which trying to continuously enter TBIVM is counter-productive.
65+
// Under high contention, we may end up going in circles thousands of times.
66+
// To avoid it, we wait here until local poll is armed and then proceed
67+
// to TBVIM exit for blocking. We do not SpinPause, but yield to let
68+
// VM thread to arm the poll sooner.
69+
while (SafepointSynchronize::is_synchronizing() &&
70+
!SafepointMechanism::local_poll_armed(java_thread)) {
71+
os::naked_yield();
72+
}
6373
} else {
6474
os::naked_yield();
65-
yields++;
6675
}
6776
} else {
68-
SpinPause();
77+
os::naked_yield();
6978
}
7079
}
7180
}

src/hotspot/share/gc/shenandoah/shenandoahLock.hpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,22 @@ class ShenandoahLock {
3737
shenandoah_padding(0);
3838
volatile LockState _state;
3939
shenandoah_padding(1);
40-
volatile Thread* _owner;
40+
Thread* volatile _owner;
4141
shenandoah_padding(2);
4242

43-
template<typename BlockOp>
43+
template<bool ALLOW_BLOCK>
4444
void contended_lock_internal(JavaThread* java_thread);
45-
4645
public:
4746
ShenandoahLock() : _state(unlocked), _owner(nullptr) {};
4847

4948
void lock(bool allow_block_for_safepoint) {
5049
assert(Atomic::load(&_owner) != Thread::current(), "reentrant locking attempt, would deadlock");
5150

52-
// Try to lock fast, or dive into contended lock handling.
53-
if (Atomic::cmpxchg(&_state, unlocked, locked) != unlocked) {
51+
if ((allow_block_for_safepoint && SafepointSynchronize::is_synchronizing()) ||
52+
(Atomic::cmpxchg(&_state, unlocked, locked) != unlocked)) {
53+
// 1. Java thread, and there is a pending safepoint. Dive into contended locking
54+
// immediately without trying anything else, and block.
55+
// 2. Fast lock fails, dive into contended lock handling.
5456
contended_lock(allow_block_for_safepoint);
5557
}
5658

0 commit comments

Comments
 (0)