mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
569 lines
19 KiB
Java
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.");
|
|
}
|
|
}
|
|
}
|