jdk/test/hotspot/jtreg/runtime/Monitor/TestRecursiveLocking.java
SendaoYan 545d19f1fa 8349771: Replace usages of -mx and -ms in some monitor tests
Reviewed-by: jpai, stefank, dholmes
2025-02-11 12:38:19 +00:00

569 lines
19 KiB
Java

/*
* Copyright (c) 2024, 2025, 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=Xint_outer_inner
* @requires vm.flagless
* @summary Tests recursive locking in -Xint in outer then inner mode.
* @library /testlibrary /test/lib
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -Xint
* -XX:LockingMode=0
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -Xint
* -XX:LockingMode=1
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -Xint
* -XX:LockingMode=2
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*/
/*
* @test id=Xint_alternate_AB
* @requires vm.flagless
* @summary Tests recursive locking in -Xint in alternate A and B mode.
* @library /testlibrary /test/lib
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -Xint
* -XX:LockingMode=0
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -Xint
* -XX:LockingMode=1
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -Xint
* -XX:LockingMode=2
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*/
/*
* @test id=C1_outer_inner
* @requires vm.flagless
* @requires vm.compiler1.enabled
* @summary Tests recursive locking in C1 in outer then inner mode.
* @library /testlibrary /test/lib
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:TieredStopAtLevel=1
* -XX:LockingMode=0
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:TieredStopAtLevel=1
* -XX:LockingMode=1
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:TieredStopAtLevel=1
* -XX:LockingMode=2
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*/
/*
* @test id=C1_alternate_AB
* @requires vm.flagless
* @requires vm.compiler1.enabled
* @summary Tests recursive locking in C1 in alternate A and B mode.
* @library /testlibrary /test/lib
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:TieredStopAtLevel=1
* -XX:LockingMode=0
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:TieredStopAtLevel=1
* -XX:LockingMode=1
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:TieredStopAtLevel=1
* -XX:LockingMode=2
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*/
/*
* @test id=C2_outer_inner
* @requires vm.flagless
* @requires vm.compiler2.enabled
* @summary Tests recursive locking in C2 in outer then inner mode.
* @library /testlibrary /test/lib
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:-EliminateNestedLocks
* -XX:LockingMode=0
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:-EliminateNestedLocks
* -XX:LockingMode=1
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:-EliminateNestedLocks
* -XX:LockingMode=2
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 1
*/
/*
* @test id=C2_alternate_AB
* @requires vm.flagless
* @requires vm.compiler2.enabled
* @summary Tests recursive locking in C2 in alternate A and B mode.
* @library /testlibrary /test/lib
* @build jdk.test.whitebox.WhiteBox
*
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:LockingMode=0
* -XX:-EliminateNestedLocks
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:LockingMode=1
* -XX:-EliminateNestedLocks
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*
* @run main/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:LockingMode=2
* -XX:-EliminateNestedLocks
* -Xms256m -Xmx256m
* TestRecursiveLocking 5 2
*/
import jdk.test.lib.Asserts;
import jdk.test.whitebox.WhiteBox;
import jtreg.SkippedException;
public class TestRecursiveLocking {
static final WhiteBox WB = WhiteBox.getWhiteBox();
static final int flagLockingMode = WB.getIntVMFlag("LockingMode").intValue();
static final int constLockStackCapacity = WB.getLockStackCapacity();
static final int LM_MONITOR = 0;
static final int LM_LEGACY = 1;
static final int LM_LIGHTWEIGHT = 2;
static final int def_mode = 2;
static final int def_n_secs = 30;
static final SyncThread syncThread = new SyncThread();
// This SynchronizedObject class and the OUTER followed by INNER testing
// model is adapted from runtime/lockStack/TestLockStackCapacity.java.
static class SynchronizedObject {
private int counter;
synchronized void runInner(int depth, SynchronizedObject outer) {
counter++;
// Legacy mode has no lock stack, i.e., there is no limit
// on recursion, so for legacy mode we can't say that
// "outer" must be inflated here, which we can say for all
// the other locking modes.
if (flagLockingMode != LM_LEGACY) {
outer.assertInflated();
}
// We haven't reached the stack lock capacity (recursion
// level), so we shouldn't be inflated here. Except for
// monitor mode, which is always inflated.
if (flagLockingMode != LM_MONITOR) {
assertNotInflated();
}
if (depth == 1) {
return;
} else {
runInner(depth - 1, outer);
}
if (flagLockingMode != LM_MONITOR) {
assertNotInflated();
}
}
synchronized void runOuter(int depth, SynchronizedObject inner) {
counter++;
if (flagLockingMode != LM_MONITOR) {
assertNotInflated();
}
if (depth == 1) {
inner.runInner(constLockStackCapacity, this);
} else {
runOuter(depth - 1, inner);
}
if (flagLockingMode != LM_LEGACY) {
assertInflated();
}
}
// This test nests x recursive locks of INNER, in x recursive
// locks of OUTER. The number x is taken from the max number
// of elements in the lock stack.
public void runOuterInnerTest() {
final SynchronizedObject OUTER = new SynchronizedObject();
final SynchronizedObject INNER = new SynchronizedObject();
// Just checking since they are new objects:
OUTER.assertNotInflated();
INNER.assertNotInflated();
synchronized (OUTER) {
OUTER.counter++;
if (flagLockingMode != LM_MONITOR) {
OUTER.assertNotInflated();
}
INNER.assertNotInflated();
OUTER.runOuter(constLockStackCapacity - 1, INNER);
if (flagLockingMode != LM_LEGACY) {
OUTER.assertInflated();
}
if (flagLockingMode != LM_MONITOR) {
INNER.assertNotInflated();
}
}
// Verify that the nested monitors have been properly released:
syncThread.verifyCanBeSynced(OUTER);
syncThread.verifyCanBeSynced(INNER);
Asserts.assertEquals(OUTER.counter, constLockStackCapacity);
Asserts.assertEquals(INNER.counter, constLockStackCapacity);
}
synchronized void runA(int depth, SynchronizedObject B) {
counter++;
if (flagLockingMode == LM_LIGHTWEIGHT) {
// First time we lock A, A is the only one on the lock
// stack.
if (counter == 1) {
assertNotInflated();
} else {
// Second time we want to lock A, the lock stack
// looks like this [A, B]. Lightweight locking
// doesn't allow interleaving ([A, B, A]), instead
// it inflates A and removes it from the lock
// stack. Which leaves us with only [B] on the
// lock stack. After more recursions it will grow
// to [B, B ... B].
assertInflated();
}
} else if (flagLockingMode == LM_MONITOR) {
assertInflated();
}
// Call runB() at the same depth as runA's depth:
B.runB(depth, this);
}
synchronized void runB(int depth, SynchronizedObject A) {
counter++;
if (flagLockingMode != LM_MONITOR) {
// Legacy tolerates endless recursions. While testing
// lightweight we don't go deeper than the size of the
// lock stack, which in this test case will be filled
// with a number of B-elements. See comment in runA()
// above for more info.
assertNotInflated();
} else {
assertInflated();
}
if (depth == 1) {
// Reached LockStackCapacity in depth so we're done.
return;
} else {
A.runA(depth - 1, this);
}
}
// This test alternates by locking A and B.
public void runAlternateABTest() {
final SynchronizedObject A = new SynchronizedObject();
final SynchronizedObject B = new SynchronizedObject();
// Just checking since they are new objects:
A.assertNotInflated();
B.assertNotInflated();
A.runA(constLockStackCapacity, B);
// Verify that the nested monitors have been properly released:
syncThread.verifyCanBeSynced(A);
syncThread.verifyCanBeSynced(B);
Asserts.assertEquals(A.counter, constLockStackCapacity);
Asserts.assertEquals(B.counter, constLockStackCapacity);
if (flagLockingMode == LM_LEGACY) {
A.assertNotInflated();
}
// Implied else: for LM_MONITOR or LM_LIGHTWEIGHT it can be
// either inflated or not because A is not locked anymore
// and subject to deflation.
if (flagLockingMode != LM_MONITOR) {
B.assertNotInflated();
}
}
void assertNotInflated() {
Asserts.assertFalse(WB.isMonitorInflated(this));
}
void assertInflated() {
Asserts.assertTrue(WB.isMonitorInflated(this));
}
}
static void usage() {
System.err.println();
System.err.println("Usage: java TestRecursiveLocking [n_secs]");
System.err.println(" java TestRecursiveLocking n_secs [mode]");
System.err.println();
System.err.println("where:");
System.err.println(" n_secs ::= > 0");
System.err.println(" Default n_secs is " + def_n_secs + ".");
System.err.println(" mode ::= 1 - outer and inner");
System.err.println(" ::= 2 - alternate A and B");
System.err.println(" Default mode is " + def_mode + ".");
System.exit(1);
}
public static void main(String... argv) throws Exception {
int mode = def_mode;
int n_secs = def_n_secs;
if (argv.length != 0 && argv.length != 1 && argv.length != 2) {
usage();
} else if (argv.length > 0) {
try {
n_secs = Integer.parseInt(argv[0]);
if (n_secs <= 0) {
throw new NumberFormatException("Not > 0: '" + argv[0]
+ "'");
}
} catch (NumberFormatException nfe) {
System.err.println();
System.err.println(nfe);
System.err.println("ERROR: '" + argv[0]
+ "': invalid n_secs value.");
usage();
}
if (argv.length > 1) {
try {
mode = Integer.parseInt(argv[1]);
if (mode != 1 && mode != 2) {
throw new NumberFormatException("Not 1 -> 2: '"
+ argv[1] + "'");
}
} catch (NumberFormatException nfe) {
System.err.println();
System.err.println(nfe);
System.err.println("ERROR: '" + argv[1]
+ "': invalid mode value.");
usage();
}
}
}
System.out.println("INFO: LockingMode=" + flagLockingMode);
System.out.println("INFO: LockStackCapacity=" + constLockStackCapacity);
System.out.println("INFO: n_secs=" + n_secs);
System.out.println("INFO: mode=" + mode);
long loopCount = 0;
long endTime = System.currentTimeMillis() + n_secs * 1000;
syncThread.waitForStart();
while (System.currentTimeMillis() < endTime) {
loopCount++;
SynchronizedObject syncObj = new SynchronizedObject();
switch (mode) {
case 1:
syncObj.runOuterInnerTest();
break;
case 2:
syncObj.runAlternateABTest();
break;
default:
throw new RuntimeException("bad mode parameter: " + mode);
}
}
syncThread.setDone();
try {
syncThread.join();
} catch (InterruptedException ie) {
// This should not happen.
ie.printStackTrace();
}
System.out.println("INFO: main executed " + loopCount + " loops in "
+ n_secs + " seconds.");
}
}
class SyncThread extends Thread {
static final boolean verbose = false; // set to true for debugging
private boolean done = false;
private boolean haveWork = false;
private Object obj;
private Object waiter = new Object();
public void run() {
if (verbose) System.out.println("SyncThread: running.");
synchronized (waiter) {
// Let main know that we are running:
if (verbose) System.out.println("SyncThread: notify main running.");
waiter.notify();
while (!done) {
if (verbose) System.out.println("SyncThread: waiting.");
try {
waiter.wait();
} catch (InterruptedException ie) {
// This should not happen.
ie.printStackTrace();
}
if (haveWork) {
if (verbose) System.out.println("SyncThread: working.");
synchronized (obj) {
}
if (verbose) System.out.println("SyncThread: worked.");
haveWork = false;
waiter.notify();
if (verbose) System.out.println("SyncThread: notified.");
}
else if (verbose) {
System.out.println("SyncThread: notified without work.");
}
}
}
if (verbose) System.out.println("SyncThread: exiting.");
}
public void setDone() {
synchronized (waiter) {
if (verbose) System.out.println("main: set done.");
done = true;
waiter.notify();
}
}
public void verifyCanBeSynced(Object obj) {
synchronized (waiter) {
if (verbose) System.out.println("main: queueing up work.");
this.obj = obj;
haveWork = true;
if (verbose) System.out.println("main: notifying SyncThread.");
waiter.notify();
if (verbose) System.out.println("main: waiting for SyncThread.");
while (haveWork) {
try {
waiter.wait();
} catch (InterruptedException ie) {
// This should not happen.
ie.printStackTrace();
}
}
if (verbose) System.out.println("main: waited for SyncThread.");
}
}
public void waitForStart() {
synchronized (waiter) {
this.start();
// Wait for SyncThread to actually get running:
if (verbose) System.out.println("main: wait for SyncThread start.");
try {
waiter.wait();
} catch (InterruptedException ie) {
// This should not happen.
ie.printStackTrace();
}
if (verbose) System.out.println("main: waited for SyncThread start.");
}
}
}