Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8318895

Deoptimization results in incorrect lightweight locking stack

    XMLWordPrintable

Details

    • b24

    Backports

      Description

        While working on recursive-locking support for lightweight locking (-XX:LockingMode=2), we hit an assert that we have an inconsistent lock stack.

        One of the problematic tests is the following:
        ```
        class EARelockingArgEscapeLWLockedInCalleeFrame_2Target extends EATestCaseBaseTarget {
        ...
            public void dontinline_testMethod() {
                XYVal l1 = new XYVal(1, 1); // ArgEscape
                XYVal l2 = new XYVal(4, 2); // NoEscape, scalar replaced
                synchronized (l1) { // eliminated
                    synchronized (l2) { // eliminated
                        l1.dontinline_sync_method(this); // l1 escapes
                    }
                }
                iResult = l2.x + l2.y;
            }
        ```

        The test runs the above method enough time to for it to get C2 compiled. Then it attaches a debugger and adds a breakpoint deeper inside dontlinine_sync_method.

        What we see in gdb is that `l1` is an existing object that has been pushed onto the deoptee thread's locking stack because of the dontinline_sync_method call. We then try to "replay" `synchronized (l1)` and `synchronized (l2)` in Deoptimization::relock_objects. This gives us the incorrect lock order [l1, l1, l2] while the actual code has the [l1, l2, l1] lock order. This then messes up the lock stack in the unlock path when we perform our recursive lock handling.

        The lightweight code in openjdk/jdk works because the recursive lock gets inflated and removed from the lock stack. However, I can get the upstream lightweight locking to assert by removing the recursive locking in the test case:

        ```
        class EARelockingArgEscapeLWLockedInCalleeFrame_2Target extends EATestCaseBaseTarget {
        ...
            public void dontinline_testMethod() {
                XYVal l1 = new XYVal(1, 1);
                XYVal l2 = new XYVal(4, 2);
                XYVal l3 = new XYVal(5, 3);
                synchronized (l1) {
                    synchronized (l2) {
                        l3.dontinline_sync_method(this);
                    }
                }
                iResult = l2.x + l2.y;
            }
        ```

        This asserts with the following:
        ```
        # Internal Error (/home/stefank/git/jdk/open/src/hotspot/share/runtime/lockStack.inline.hpp:86), pid=265437, tid=265440
        # assert(contains(o)) failed: entry must be present: 0x00000000ffa00130
        ...
        V [libjvm.so+0x1743709] LockStack::remove(oop)+0x3e9 (lockStack.inline.hpp:86)
        V [libjvm.so+0x173f78d] ObjectSynchronizer::exit(oop, BasicLock*, JavaThread*)+0x2fd (synchronizer.cpp:590)
        V [libjvm.so+0xe7667c] InterpreterRuntime::monitorexit(BasicObjectLock*)+0x18c (interpreterRuntime.cpp:779)
        j EARelockingArgEscapeLWLockedInCalleeFrame_2Target.dontinline_testMethod()V+47
        j EATestCaseBaseTarget.run()V+64

        Attachments

          Issue Links

            Activity

              People

                rkennke Roman Kennke
                stefank Stefan Karlsson
                Votes:
                0 Vote for this issue
                Watchers:
                15 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: