diff options
author | Roman Kennke <rkennke@openjdk.org> | 2023-11-27 16:14:23 +0000 |
---|---|---|
committer | Vitaly Provodin <vitaly.provodin@jetbrains.com> | 2024-01-17 20:31:46 +0700 |
commit | a8fc42cf6c274e41fb6a0ce58cffc16137e6996e (patch) | |
tree | 08b1cbbdd046538a0a67768bf329da48a1d8b359 | |
parent | f8996f7e12f0f5ea3816f5e6045ec9ddf9e18d82 (diff) | |
download | JetBrainsRuntime-a8fc42cf6c274e41fb6a0ce58cffc16137e6996e.tar.gz |
8318895: Deoptimization results in incorrect lightweight locking stack
Backport-of: ea1ffa34192448317ce9a61a3588b0dee3a2ef44
-rw-r--r-- | src/hotspot/share/runtime/deoptimization.cpp | 17 | ||||
-rw-r--r-- | test/jdk/com/sun/jdi/EATests.java | 143 |
2 files changed, 157 insertions, 3 deletions
diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index d62100c4cf8..a05f88c9182 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -82,6 +82,7 @@ #include "runtime/stackValue.hpp" #include "runtime/stackWatermarkSet.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/synchronizer.hpp" #include "runtime/threadSMR.hpp" #include "runtime/threadWXSetters.inline.hpp" #include "runtime/vframe.hpp" @@ -1648,9 +1649,19 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArray<MonitorInf } } } - BasicLock* lock = mon_info->lock(); - ObjectSynchronizer::enter(obj, lock, deoptee_thread); - assert(mon_info->owner()->is_locked(), "object must be locked now"); + if (LockingMode == LM_LIGHTWEIGHT && exec_mode == Unpack_none) { + // 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(obj, nullptr, deoptee_thread); + assert(mon_info->owner()->is_locked(), "object must be locked now"); + ObjectMonitor* mon = ObjectSynchronizer::inflate(deoptee_thread, obj(), ObjectSynchronizer::inflate_cause_vm_internal); + assert(mon->owner() == deoptee_thread, "must be"); + } else { + BasicLock* lock = mon_info->lock(); + ObjectSynchronizer::enter(obj, lock, deoptee_thread); + assert(mon_info->owner()->is_locked(), "object must be locked now"); + } } } } diff --git a/test/jdk/com/sun/jdi/EATests.java b/test/jdk/com/sun/jdi/EATests.java index 9cdf2d285f2..1f99b2ccac6 100644 --- a/test/jdk/com/sun/jdi/EATests.java +++ b/test/jdk/com/sun/jdi/EATests.java @@ -42,6 +42,7 @@ * -XX:+WhiteBoxAPI * -Xbatch * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=1 * @run driver EATests * -XX:+UnlockDiagnosticVMOptions * -Xms256m -Xmx256m @@ -50,6 +51,7 @@ * -XX:+WhiteBoxAPI * -Xbatch * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:-EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=1 * @run driver EATests * -XX:+UnlockDiagnosticVMOptions * -Xms256m -Xmx256m @@ -58,6 +60,7 @@ * -XX:+WhiteBoxAPI * -Xbatch * -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=1 * @run driver EATests * -XX:+UnlockDiagnosticVMOptions * -Xms256m -Xmx256m @@ -66,6 +69,44 @@ * -XX:+WhiteBoxAPI * -Xbatch * -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=1 + * + * @run driver EATests + * -XX:+UnlockDiagnosticVMOptions + * -Xms256m -Xmx256m + * -Xbootclasspath/a:. + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=2 + * @run driver EATests + * -XX:+UnlockDiagnosticVMOptions + * -Xms256m -Xmx256m + * -Xbootclasspath/a:. + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:-EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=2 + * @run driver EATests + * -XX:+UnlockDiagnosticVMOptions + * -Xms256m -Xmx256m + * -Xbootclasspath/a:. + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:+DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=2 + * @run driver EATests + * -XX:+UnlockDiagnosticVMOptions + * -Xms256m -Xmx256m + * -Xbootclasspath/a:. + * -XX:CompileCommand=dontinline,*::dontinline_* + * -XX:+WhiteBoxAPI + * -Xbatch + * -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:+EliminateLocks -XX:+EliminateNestedLocks + * -XX:LockingMode=2 * * @comment Excercise -XX:+DeoptimizeObjectsALot. Mostly to prevent bit-rot because the option is meant to stress object deoptimization * with non-synthetic workloads. @@ -208,11 +249,13 @@ class EATestsTarget { // Relocking test cases new EARelockingSimpleTarget() .run(); + new EARelockingSimpleWithAccessInOtherThreadTarget() .run(); new EARelockingRecursiveTarget() .run(); new EARelockingNestedInflatedTarget() .run(); new EARelockingNestedInflated_02Target() .run(); new EARelockingArgEscapeLWLockedInCalleeFrameTarget() .run(); new EARelockingArgEscapeLWLockedInCalleeFrame_2Target() .run(); + new EARelockingArgEscapeLWLockedInCalleeFrameNoRecursiveTarget() .run(); new EAGetOwnedMonitorsTarget() .run(); new EAEntryCountTarget() .run(); new EARelockingObjectCurrentlyWaitingOnTarget() .run(); @@ -325,11 +368,13 @@ public class EATests extends TestScaffold { // Relocking test cases new EARelockingSimple() .run(this); + new EARelockingSimpleWithAccessInOtherThread() .run(this); new EARelockingRecursive() .run(this); new EARelockingNestedInflated() .run(this); new EARelockingNestedInflated_02() .run(this); new EARelockingArgEscapeLWLockedInCalleeFrame() .run(this); new EARelockingArgEscapeLWLockedInCalleeFrame_2() .run(this); + new EARelockingArgEscapeLWLockedInCalleeFrameNoRecursive() .run(this); new EAGetOwnedMonitors() .run(this); new EAEntryCount() .run(this); new EARelockingObjectCurrentlyWaitingOn() .run(this); @@ -1703,6 +1748,62 @@ class EARelockingSimpleTarget extends EATestCaseBaseTarget { ///////////////////////////////////////////////////////////////////////////// +// The debugger reads and publishes an object with eliminated locking to an instance field. +// A 2nd thread in the debuggee finds it there and changes its state using a synchronized method. +// Without eager relocking the accesses are unsynchronized which can be observed. +class EARelockingSimpleWithAccessInOtherThread extends EATestCaseBaseDebugger { + + public void runTestCase() throws Exception { + BreakpointEvent bpe = resumeTo(TARGET_TESTCASE_BASE_NAME, "dontinline_brkpt", "()V"); + printStack(bpe.thread()); + String l1ClassName = EARelockingSimpleWithAccessInOtherThreadTarget.SyncCounter.class.getName(); + ObjectReference ctr = getLocalRef(bpe.thread().frame(1), l1ClassName, "l1"); + setField(testCase, "sharedCounter", ctr); + terminateEndlessLoop(); + } +} + +class EARelockingSimpleWithAccessInOtherThreadTarget extends EATestCaseBaseTarget { + + public static class SyncCounter { + private int val; + public synchronized int inc() { return val++; } + } + + public volatile SyncCounter sharedCounter; + + @Override + public void setUp() { + super.setUp(); + doLoop = true; + Thread.ofPlatform().daemon().start(() -> { + while (doLoop) { + SyncCounter ctr = sharedCounter; + if (ctr != null) { + ctr.inc(); + } + } + }); + } + + public void dontinline_testMethod() { + SyncCounter l1 = new SyncCounter(); + synchronized (l1) { // Eliminated locking + l1.inc(); + dontinline_brkpt(); // Debugger publishes l1 to sharedCounter. + iResult = l1.inc(); // Changes by the 2nd thread will be observed if l1 + // was not relocked before passing it to the debugger. + } + } + + @Override + public int getExpectedIResult() { + return 1; + } +} + +///////////////////////////////////////////////////////////////////////////// + // Test recursive locking class EARelockingRecursiveTarget extends EATestCaseBaseTarget { @@ -1902,6 +2003,48 @@ class EARelockingArgEscapeLWLockedInCalleeFrame_2Target extends EATestCaseBaseTa ///////////////////////////////////////////////////////////////////////////// /** + * Similar to {@link EARelockingArgEscapeLWLockedInCalleeFrame_2Target}. It does + * not use recursive locking and exposed a bug in the lightweight-locking implementation. + */ +class EARelockingArgEscapeLWLockedInCalleeFrameNoRecursive extends EATestCaseBaseDebugger { + + public void runTestCase() throws Exception { + BreakpointEvent bpe = resumeTo(TARGET_TESTCASE_BASE_NAME, "dontinline_brkpt", "()V"); + printStack(bpe.thread()); + @SuppressWarnings("unused") + ObjectReference o = getLocalRef(bpe.thread().frame(2), XYVAL_NAME, "l1"); + } +} + +class EARelockingArgEscapeLWLockedInCalleeFrameNoRecursiveTarget extends EATestCaseBaseTarget { + + @Override + public void setUp() { + super.setUp(); + testMethodDepth = 2; + } + + public void dontinline_testMethod() { + XYVal l1 = new XYVal(1, 1); // NoEscape, scalar replaced + XYVal l2 = new XYVal(4, 2); // NoEscape, scalar replaced + XYVal l3 = new XYVal(5, 3); // ArgEscape + synchronized (l1) { // eliminated + synchronized (l2) { // eliminated + l3.dontinline_sync_method(this); // l3 escapes + } + } + iResult = l2.x + l2.y; + } + + @Override + public int getExpectedIResult() { + return 6; + } +} + +///////////////////////////////////////////////////////////////////////////// + +/** * Test relocking eliminated (nested) locks of an object on which the * target thread currently waits. */ |