Skip to content

Commit b99d2f8

Browse files
committed
8319797: Recursive lightweight locking: Runtime implementation
Reviewed-by: shade Backport-of: 5dbf13730ee2b57f089c57e9e7ee8ab65d4a67af
1 parent 517053d commit b99d2f8

File tree

13 files changed

+857
-48
lines changed

13 files changed

+857
-48
lines changed

src/hotspot/share/prims/whitebox.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
#include "runtime/interfaceSupport.inline.hpp"
7979
#include "runtime/javaCalls.hpp"
8080
#include "runtime/jniHandles.inline.hpp"
81+
#include "runtime/lockStack.hpp"
8182
#include "runtime/os.hpp"
8283
#include "runtime/stackFrameStream.inline.hpp"
8384
#include "runtime/sweeper.hpp"
@@ -1754,6 +1755,14 @@ WB_ENTRY(jboolean, WB_IsMonitorInflated(JNIEnv* env, jobject wb, jobject obj))
17541755
return (jboolean) obj_oop->mark().has_monitor();
17551756
WB_END
17561757

1758+
WB_ENTRY(jint, WB_getLockStackCapacity(JNIEnv* env))
1759+
return (jint) LockStack::CAPACITY;
1760+
WB_END
1761+
1762+
WB_ENTRY(jboolean, WB_supportsRecursiveLightweightLocking(JNIEnv* env))
1763+
return (jboolean) VM_Version::supports_recursive_lightweight_locking();
1764+
WB_END
1765+
17571766
WB_ENTRY(jboolean, WB_DeflateIdleMonitors(JNIEnv* env, jobject wb))
17581767
log_info(monitorinflation)("WhiteBox initiated DeflateIdleMonitors");
17591768
return ObjectSynchronizer::request_deflate_idle_monitors();
@@ -2549,6 +2558,8 @@ static JNINativeMethod methods[] = {
25492558
(void*)&WB_AddModuleExportsToAll },
25502559
{CC"deflateIdleMonitors", CC"()Z", (void*)&WB_DeflateIdleMonitors },
25512560
{CC"isMonitorInflated0", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsMonitorInflated },
2561+
{CC"getLockStackCapacity", CC"()I", (void*)&WB_getLockStackCapacity },
2562+
{CC"supportsRecursiveLightweightLocking", CC"()Z", (void*)&WB_supportsRecursiveLightweightLocking },
25522563
{CC"forceSafepoint", CC"()V", (void*)&WB_ForceSafepoint },
25532564
{CC"getConstantPool0", CC"(Ljava/lang/Class;)J", (void*)&WB_GetConstantPool },
25542565
{CC"getConstantPoolCacheIndexTag0", CC"()I", (void*)&WB_GetConstantPoolCacheIndexTag},

src/hotspot/share/runtime/abstract_vm_version.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -190,6 +190,9 @@ class Abstract_VM_Version: AllStatic {
190190
// Does platform support stack watermark barriers for concurrent stack processing?
191191
constexpr static bool supports_stack_watermark_barrier() { return false; }
192192

193+
// Is recursive lightweight locking implemented for this platform?
194+
constexpr static bool supports_recursive_lightweight_locking() { return false; }
195+
193196
static bool print_matching_lines_from_file(const char* filename, outputStream* st, const char* keywords_to_match[]);
194197
};
195198

src/hotspot/share/runtime/lockStack.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (c) 2022, Red Hat, Inc. All rights reserved.
33
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
4+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
45
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
56
*
67
* This code is free software; you can redistribute it and/or modify it
@@ -25,20 +26,30 @@
2526

2627
#include "precompiled.hpp"
2728
#include "memory/allocation.hpp"
29+
#include "runtime/globals.hpp"
2830
#include "runtime/lockStack.inline.hpp"
2931
#include "runtime/safepoint.hpp"
3032
#include "runtime/stackWatermark.hpp"
3133
#include "runtime/stackWatermarkSet.inline.hpp"
3234
#include "runtime/thread.hpp"
3335
#include "utilities/copy.hpp"
36+
#include "utilities/debug.hpp"
37+
#include "utilities/globalDefinitions.hpp"
3438
#include "utilities/ostream.hpp"
3539

40+
#include <type_traits>
41+
3642
const int LockStack::lock_stack_offset = in_bytes(JavaThread::lock_stack_offset());
3743
const int LockStack::lock_stack_top_offset = in_bytes(JavaThread::lock_stack_top_offset());
3844
const int LockStack::lock_stack_base_offset = in_bytes(JavaThread::lock_stack_base_offset());
3945

4046
LockStack::LockStack(JavaThread* jt) :
4147
_top(lock_stack_base_offset), _base() {
48+
// Make sure the layout of the object is compatible with the emitted code's assumptions.
49+
STATIC_ASSERT(sizeof(_bad_oop_sentinel) == oopSize);
50+
STATIC_ASSERT(sizeof(_base[0]) == oopSize);
51+
STATIC_ASSERT(std::is_standard_layout<LockStack>::value);
52+
STATIC_ASSERT(offsetof(LockStack, _bad_oop_sentinel) == offsetof(LockStack, _base) - oopSize);
4253
#ifdef ASSERT
4354
for (int i = 0; i < CAPACITY; i++) {
4455
_base[i] = NULL;
@@ -62,11 +73,21 @@ uint32_t LockStack::end_offset() {
6273
void LockStack::verify(const char* msg) const {
6374
assert(LockingMode == LM_LIGHTWEIGHT, "never use lock-stack when light weight locking is disabled");
6475
assert((_top <= end_offset()), "lockstack overflow: _top %d end_offset %d", _top, end_offset());
65-
assert((_top >= start_offset()), "lockstack underflow: _top %d end_offset %d", _top, start_offset());
76+
assert((_top >= start_offset()), "lockstack underflow: _top %d start_offset %d", _top, start_offset());
6677
if (SafepointSynchronize::is_at_safepoint() || (Thread::current()->is_Java_thread() && is_owning_thread())) {
6778
int top = to_index(_top);
6879
for (int i = 0; i < top; i++) {
6980
assert(_base[i] != NULL, "no zapped before top");
81+
if (VM_Version::supports_recursive_lightweight_locking()) {
82+
oop o = _base[i];
83+
for (; i < top - 1; i++) {
84+
// Consecutive entries may be the same
85+
if (_base[i + 1] != o) {
86+
break;
87+
}
88+
}
89+
}
90+
7091
for (int j = i + 1; j < top; j++) {
7192
assert(_base[i] != _base[j], "entries must be unique: %s", msg);
7293
}

src/hotspot/share/runtime/lockStack.hpp

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (c) 2022, Red Hat, Inc. All rights reserved.
33
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
4+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
45
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
56
*
67
* This code is free software; you can redistribute it and/or modify it
@@ -35,10 +36,12 @@ class OopClosure;
3536
class outputStream;
3637

3738
class LockStack {
39+
friend class LockStackTest;
3840
friend class VMStructs;
3941
JVMCI_ONLY(friend class JVMCIVMStructs;)
40-
private:
42+
public:
4143
static const int CAPACITY = 8;
44+
private:
4245

4346
// TODO: It would be very useful if JavaThread::lock_stack_offset() and friends were constexpr,
4447
// but this is currently not the case because we're using offset_of() which is non-constexpr,
@@ -51,6 +54,9 @@ class LockStack {
5154
// We do this instead of a simple index into the array because this allows for
5255
// efficient addressing in generated code.
5356
uint32_t _top;
57+
// The _bad_oop_sentinel acts as a sentinel value to elide underflow checks in generated code.
58+
// The correct layout is statically asserted in the constructor.
59+
const uintptr_t _bad_oop_sentinel = badOopVal;
5460
oop _base[CAPACITY];
5561

5662
// Get the owning thread of this lock-stack.
@@ -75,14 +81,35 @@ class LockStack {
7581
static uint32_t start_offset();
7682
static uint32_t end_offset();
7783

78-
// Return true if we have room to push onto this lock-stack, false otherwise.
79-
inline bool can_push() const;
84+
// Returns true if the lock-stack is full. False otherwise.
85+
inline bool is_full() const;
8086

8187
// Pushes an oop on this lock-stack.
8288
inline void push(oop o);
8389

90+
// Get the oldest oop from this lock-stack.
91+
// Precondition: This lock-stack must not be empty.
92+
inline oop bottom() const;
93+
94+
// Is the lock-stack empty.
95+
inline bool is_empty() const;
96+
97+
// Check if object is recursive.
98+
// Precondition: This lock-stack must contain the oop.
99+
inline bool is_recursive(oop o) const;
100+
101+
// Try recursive enter.
102+
// Precondition: This lock-stack must not be full.
103+
inline bool try_recursive_enter(oop o);
104+
105+
// Try recursive exit.
106+
// Precondition: This lock-stack must contain the oop.
107+
inline bool try_recursive_exit(oop o);
108+
84109
// Removes an oop from an arbitrary location of this lock-stack.
85-
inline void remove(oop o);
110+
// Precondition: This lock-stack must contain the oop.
111+
// Returns the number of oops removed.
112+
inline size_t remove(oop o);
86113

87114
// Tests whether the oop is on this lock-stack.
88115
inline bool contains(oop o) const;

src/hotspot/share/runtime/lockStack.inline.hpp

Lines changed: 123 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (c) 2022, Red Hat, Inc. All rights reserved.
33
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
4+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
45
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
56
*
67
* This code is free software; you can redistribute it and/or modify it
@@ -26,14 +27,21 @@
2627
#ifndef SHARE_RUNTIME_LOCKSTACK_INLINE_HPP
2728
#define SHARE_RUNTIME_LOCKSTACK_INLINE_HPP
2829

29-
#include "memory/iterator.hpp"
3030
#include "runtime/lockStack.hpp"
31+
32+
#include "memory/iterator.hpp"
3133
#include "runtime/safepoint.hpp"
3234
#include "runtime/stackWatermark.hpp"
3335
#include "runtime/stackWatermarkSet.inline.hpp"
3436
#include "runtime/thread.hpp"
37+
#include "runtime/vm_version.hpp"
38+
#include "utilities/align.hpp"
39+
#include "utilities/globalDefinitions.hpp"
3540

3641
inline int LockStack::to_index(uint32_t offset) {
42+
assert(is_aligned(offset, oopSize), "Bad alignment: %u", offset);
43+
assert((offset <= end_offset()), "lockstack overflow: offset %d end_offset %d", offset, end_offset());
44+
assert((offset >= start_offset()), "lockstack underflow: offset %d start_offset %d", offset, start_offset());
3745
return (offset - lock_stack_base_offset) / oopSize;
3846
}
3947

@@ -42,8 +50,8 @@ JavaThread* LockStack::get_thread() const {
4250
return reinterpret_cast<JavaThread*>(addr - lock_stack_offset);
4351
}
4452

45-
inline bool LockStack::can_push() const {
46-
return to_index(_top) < CAPACITY;
53+
inline bool LockStack::is_full() const {
54+
return to_index(_top) == CAPACITY;
4755
}
4856

4957
inline bool LockStack::is_owning_thread() const {
@@ -61,32 +69,132 @@ inline void LockStack::push(oop o) {
6169
verify("pre-push");
6270
assert(oopDesc::is_oop(o), "must be");
6371
assert(!contains(o), "entries must be unique");
64-
assert(can_push(), "must have room");
72+
assert(!is_full(), "must have room");
6573
assert(_base[to_index(_top)] == NULL, "expect zapped entry");
6674
_base[to_index(_top)] = o;
6775
_top += oopSize;
6876
verify("post-push");
6977
}
7078

71-
inline void LockStack::remove(oop o) {
79+
inline oop LockStack::bottom() const {
80+
assert(to_index(_top) > 0, "must contain an oop");
81+
return _base[0];
82+
}
83+
84+
inline bool LockStack::is_empty() const {
85+
return to_index(_top) == 0;
86+
}
87+
88+
inline bool LockStack::is_recursive(oop o) const {
89+
if (!VM_Version::supports_recursive_lightweight_locking()) {
90+
return false;
91+
}
92+
verify("pre-is_recursive");
93+
94+
// This will succeed iff there is a consecutive run of oops on the
95+
// lock-stack with a length of at least 2.
96+
97+
assert(contains(o), "at least one entry must exist");
98+
int end = to_index(_top);
99+
// Start iterating from the top because the runtime code is more
100+
// interested in the balanced locking case when the top oop on the
101+
// lock-stack matches o. This will cause the for loop to break out
102+
// in the first loop iteration if it is non-recursive.
103+
for (int i = end - 1; i > 0; i--) {
104+
if (_base[i - 1] == o && _base[i] == o) {
105+
verify("post-is_recursive");
106+
return true;
107+
}
108+
if (_base[i] == o) {
109+
// o can only occur in one consecutive run on the lock-stack.
110+
// Only one of the two oops checked matched o, so this run
111+
// must be of length 1 and thus not be recursive. Stop the search.
112+
break;
113+
}
114+
}
115+
116+
verify("post-is_recursive");
117+
return false;
118+
}
119+
120+
inline bool LockStack::try_recursive_enter(oop o) {
121+
if (!VM_Version::supports_recursive_lightweight_locking()) {
122+
return false;
123+
}
124+
verify("pre-try_recursive_enter");
125+
126+
// This will succeed iff the top oop on the stack matches o.
127+
// When successful o will be pushed to the lock-stack creating
128+
// a consecutive run at least 2 oops that matches o on top of
129+
// the lock-stack.
130+
131+
assert(!is_full(), "precond");
132+
133+
int end = to_index(_top);
134+
if (end == 0 || _base[end - 1] != o) {
135+
// Topmost oop does not match o.
136+
verify("post-try_recursive_enter");
137+
return false;
138+
}
139+
140+
_base[end] = o;
141+
_top += oopSize;
142+
verify("post-try_recursive_enter");
143+
return true;
144+
}
145+
146+
inline bool LockStack::try_recursive_exit(oop o) {
147+
if (!VM_Version::supports_recursive_lightweight_locking()) {
148+
return false;
149+
}
150+
verify("pre-try_recursive_exit");
151+
152+
// This will succeed iff the top two oops on the stack matches o.
153+
// When successful the top oop will be popped of the lock-stack.
154+
// When unsuccessful the lock may still be recursive, in which
155+
// case the locking is unbalanced. This case is handled externally.
156+
157+
assert(contains(o), "entries must exist");
158+
159+
int end = to_index(_top);
160+
if (end <= 1 || _base[end - 1] != o || _base[end - 2] != o) {
161+
// The two topmost oops do not match o.
162+
verify("post-try_recursive_exit");
163+
return false;
164+
}
165+
166+
_top -= oopSize;
167+
DEBUG_ONLY(_base[to_index(_top)] = nullptr;)
168+
verify("post-try_recursive_exit");
169+
return true;
170+
}
171+
172+
inline size_t LockStack::remove(oop o) {
72173
verify("pre-remove");
73174
assert(contains(o), "entry must be present: " PTR_FORMAT, p2i(o));
175+
74176
int end = to_index(_top);
177+
int inserted = 0;
75178
for (int i = 0; i < end; i++) {
76-
if (_base[i] == o) {
77-
int last = end - 1;
78-
for (; i < last; i++) {
79-
_base[i] = _base[i + 1];
179+
if (_base[i] != o) {
180+
if (inserted != i) {
181+
_base[inserted] = _base[i];
80182
}
81-
_top -= oopSize;
82-
#ifdef ASSERT
83-
_base[to_index(_top)] = NULL;
84-
#endif
85-
break;
183+
inserted++;
86184
}
87185
}
88-
assert(!contains(o), "entries must be unique: " PTR_FORMAT, p2i(o));
186+
187+
#ifdef ASSERT
188+
for (int i = inserted; i < end; i++) {
189+
_base[i] = nullptr;
190+
}
191+
#endif
192+
193+
uint32_t removed = end - inserted;
194+
_top -= removed * oopSize;
195+
assert(!contains(o), "entry must have been removed: " PTR_FORMAT, p2i(o));
89196
verify("post-remove");
197+
return removed;
90198
}
91199

92200
inline bool LockStack::contains(oop o) const {

src/hotspot/share/runtime/objectMonitor.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
308308
jint contentions() const;
309309
void add_to_contentions(jint value);
310310
intx recursions() const { return _recursions; }
311+
void set_recursions(size_t recursions);
311312

312313
// JVM/TI GetObjectMonitorUsage() needs this:
313314
ObjectWaiter* first_waiter() { return _WaitSet; }

src/hotspot/share/runtime/objectMonitor.inline.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -102,6 +102,12 @@ inline void ObjectMonitor::add_to_contentions(jint value) {
102102
Atomic::add(&_contentions, value);
103103
}
104104

105+
inline void ObjectMonitor::set_recursions(size_t recursions) {
106+
assert(_recursions == 0, "must be");
107+
assert(has_owner(), "must be owned");
108+
_recursions = checked_cast<intx>(recursions);
109+
}
110+
105111
// Clear _owner field; current value must match old_value.
106112
inline void ObjectMonitor::release_clear_owner(void* old_value) {
107113
#ifdef ASSERT

0 commit comments

Comments
 (0)