undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
47
test/hotspot/jtreg/runtime/Monitor/CompleteExit.java
Normal file
47
test/hotspot/jtreg/runtime/Monitor/CompleteExit.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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 CompleteExit
|
||||
* @summary This does a sanity test of the poll in the native wrapper.
|
||||
* @requires os.family == "linux"
|
||||
* @library /testlibrary /test/lib
|
||||
* @build CompleteExit
|
||||
* @run main/native CompleteExit
|
||||
*/
|
||||
|
||||
public class CompleteExit {
|
||||
public static native void testIt(Object o1, Object o2);
|
||||
|
||||
static volatile Object o1 = new Object();
|
||||
static volatile Object o2 = new Object();
|
||||
|
||||
static {
|
||||
System.loadLibrary("CompleteExit");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
testIt(o1, o2);
|
||||
}
|
||||
}
|
||||
91
test/hotspot/jtreg/runtime/Monitor/ConcurrentDeflation.java
Normal file
91
test/hotspot/jtreg/runtime/Monitor/ConcurrentDeflation.java
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.whitebox.WhiteBox;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8318757
|
||||
* @summary Test concurrent monitor deflation by MonitorDeflationThread and thread dumping
|
||||
* @library /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:GuaranteedAsyncDeflationInterval=2000 -XX:+WhiteBoxAPI ConcurrentDeflation
|
||||
*/
|
||||
|
||||
public class ConcurrentDeflation {
|
||||
static final WhiteBox WB = WhiteBox.getWhiteBox();
|
||||
public static final long TOTAL_RUN_TIME_NS = 10_000_000_000L;
|
||||
public static Object[] monitors = new Object[1000];
|
||||
public static int monitorCount;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Thread threadDumper = new Thread(() -> dumpThreads());
|
||||
threadDumper.start();
|
||||
Thread monitorCreator = new Thread(() -> createMonitors());
|
||||
monitorCreator.start();
|
||||
|
||||
threadDumper.join();
|
||||
monitorCreator.join();
|
||||
}
|
||||
|
||||
static private void dumpThreads() {
|
||||
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
|
||||
int dumpCount = 0;
|
||||
long startTime = System.nanoTime();
|
||||
while (System.nanoTime() - startTime < TOTAL_RUN_TIME_NS) {
|
||||
threadBean.dumpAllThreads(true, false);
|
||||
dumpCount++;
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
System.out.println("Dumped all thread info " + dumpCount + " times");
|
||||
}
|
||||
|
||||
static private void createMonitors() {
|
||||
int index = 0;
|
||||
long startTime = System.nanoTime();
|
||||
while (System.nanoTime() - startTime < TOTAL_RUN_TIME_NS) {
|
||||
index = index++ % 1000;
|
||||
monitors[index] = new Object();
|
||||
synchronized (monitors[index]) {
|
||||
try {
|
||||
// Force inflation
|
||||
monitors[index].wait(1);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
monitorCount++;
|
||||
}
|
||||
}
|
||||
System.out.println("Created " + monitorCount + " monitors");
|
||||
}
|
||||
}
|
||||
355
test/hotspot/jtreg/runtime/Monitor/DeflationIntervalsTest.java
Normal file
355
test/hotspot/jtreg/runtime/Monitor/DeflationIntervalsTest.java
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/*
|
||||
* Copyright Amazon.com Inc. 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.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* @test id=defaults
|
||||
* @bug 8305994 8306825
|
||||
* @summary Test the deflation intervals options
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest defaults
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=allIntervalsZero
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest allIntervalsZero
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=allThresholdsZero
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest allThresholdsZero
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=guaranteed_noThresholdMUDT_noSafepoint
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest guaranteed_noThresholdMUDT_noSafepoint
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=guaranteed_noThresholdMUDT_safepoint
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest guaranteed_noThresholdMUDT_safepoint
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=guaranteed_noThresholdADI_noSafepoint
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest guaranteed_noThresholdADI_noSafepoint
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=guaranteed_noThresholdADI_safepoint
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest guaranteed_noThresholdADI_safepoint
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=noGuaranteedGADT_threshold_noSafepoint
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest noGuaranteedGADT_threshold_noSafepoint
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=noGuaranteedGADT_threshold_safepoint
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest noGuaranteedGADT_threshold_safepoint
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=guaranteed_threshold_noSafepoint
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest guaranteed_threshold_noSafepoint
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=guaranteed_threshold_safepoint
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run driver DeflationIntervalsTest guaranteed_threshold_safepoint
|
||||
*/
|
||||
|
||||
public class DeflationIntervalsTest {
|
||||
|
||||
public static class Test {
|
||||
// Inflate a lot of monitors, so that threshold heuristics definitely fires
|
||||
private static final int MONITORS = 10_000;
|
||||
|
||||
// Use a handful of threads to inflate the monitors, to eat the cost of
|
||||
// wait(1) calls. This can be larger than available parallelism, since threads
|
||||
// would be time-waiting.
|
||||
private static final int THREADS = 16;
|
||||
|
||||
private static Thread[] threads;
|
||||
private static Object[] monitors;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
monitors = new Object[MONITORS];
|
||||
threads = new Thread[THREADS];
|
||||
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
int monStart = t * MONITORS / THREADS;
|
||||
int monEnd = (t + 1) * MONITORS / THREADS;
|
||||
threads[t] = new Thread(() -> {
|
||||
for (int m = monStart; m < monEnd; m++) {
|
||||
Object o = new Object();
|
||||
synchronized (o) {
|
||||
try {
|
||||
o.wait(1);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
monitors[m] = o;
|
||||
}
|
||||
});
|
||||
threads[t].start();
|
||||
}
|
||||
|
||||
for (Thread t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(10_000);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length < 1) {
|
||||
throw new IllegalArgumentException("Expect the test label");
|
||||
}
|
||||
|
||||
String test = args[0];
|
||||
switch (test) {
|
||||
case "defaults":
|
||||
// Try with all defaults
|
||||
test(Disabled.NO, Guaranteed.MAYBE, Threshold.MAYBE);
|
||||
break;
|
||||
|
||||
case "allIntervalsZero":
|
||||
// Try with all deflation intervals at zero
|
||||
test(Disabled.YES, Guaranteed.NO, Threshold.NO,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=0",
|
||||
"-XX:AsyncDeflationInterval=0",
|
||||
"-XX:GuaranteedSafepointInterval=0"
|
||||
);
|
||||
break;
|
||||
|
||||
case "allThresholdsZero":
|
||||
// Try with all heuristics thresholds at zero
|
||||
test(Disabled.NO, Guaranteed.MAYBE, Threshold.NO,
|
||||
"-XX:MonitorUsedDeflationThreshold=0"
|
||||
);
|
||||
break;
|
||||
|
||||
// Try with guaranteed interval only enabled, threshold heuristics disabled via MUDT,
|
||||
// with and without guaranteed safepoints
|
||||
|
||||
case "guaranteed_noThresholdMUDT_noSafepoint":
|
||||
test(Disabled.NO, Guaranteed.YES, Threshold.NO,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=100",
|
||||
"-XX:MonitorUsedDeflationThreshold=0",
|
||||
"-XX:GuaranteedSafepointInterval=0"
|
||||
);
|
||||
break;
|
||||
|
||||
case "guaranteed_noThresholdMUDT_safepoint":
|
||||
test(Disabled.NO, Guaranteed.YES, Threshold.NO,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=100",
|
||||
"-XX:MonitorUsedDeflationThreshold=0"
|
||||
);
|
||||
break;
|
||||
|
||||
// Try with guaranteed interval only enabled, threshold heuristics disabled via ADI
|
||||
// with and without guaranteed safepoints
|
||||
|
||||
case "guaranteed_noThresholdADI_noSafepoint":
|
||||
test(Disabled.NO, Guaranteed.YES, Threshold.NO,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=100",
|
||||
"-XX:AsyncDeflationInterval=0",
|
||||
"-XX:GuaranteedSafepointInterval=0"
|
||||
);
|
||||
break;
|
||||
|
||||
case "guaranteed_noThresholdADI_safepoint":
|
||||
test(Disabled.NO, Guaranteed.YES, Threshold.NO,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=100",
|
||||
"-XX:AsyncDeflationInterval=0"
|
||||
);
|
||||
break;
|
||||
|
||||
// Try with only threshold heuristics, guaranteed is disabled with GADT
|
||||
// with and without guaranteed safepoints
|
||||
|
||||
case "noGuaranteedGADT_threshold_noSafepoint":
|
||||
test(Disabled.NO, Guaranteed.NO, Threshold.YES,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=0",
|
||||
"-XX:MonitorUsedDeflationThreshold=1",
|
||||
"-XX:GuaranteedSafepointInterval=0"
|
||||
);
|
||||
break;
|
||||
|
||||
case "noGuaranteedGADT_threshold_safepoint":
|
||||
test(Disabled.NO, Guaranteed.NO, Threshold.YES,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=0",
|
||||
"-XX:MonitorUsedDeflationThreshold=1"
|
||||
);
|
||||
break;
|
||||
|
||||
// Try with both threshold heuristics and guaranteed interval enabled
|
||||
// with and without guaranteed safepoints
|
||||
|
||||
case "guaranteed_threshold_noSafepoint":
|
||||
test(Disabled.NO, Guaranteed.YES, Threshold.YES,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=5000",
|
||||
"-XX:MonitorUsedDeflationThreshold=1",
|
||||
"-XX:GuaranteedSafepointInterval=0"
|
||||
);
|
||||
break;
|
||||
|
||||
case "guaranteed_threshold_safepoint":
|
||||
// Try with both threshold heuristics and guaranteed interval enabled
|
||||
test(Disabled.NO, Guaranteed.YES, Threshold.YES,
|
||||
"-XX:GuaranteedAsyncDeflationInterval=5000",
|
||||
"-XX:MonitorUsedDeflationThreshold=1"
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown test: " + test);
|
||||
}
|
||||
}
|
||||
|
||||
static final String MSG_THRESHOLD = "Async deflation needed: monitors used are above the threshold";
|
||||
static final String MSG_GUARANTEED = "Async deflation needed: guaranteed interval";
|
||||
static final String MSG_DISABLED = "Async deflation is disabled";
|
||||
|
||||
public static void test(Disabled disabled, Guaranteed guaranteed, Threshold threshold, String... args) throws Exception {
|
||||
List<String> opts = new ArrayList<>();
|
||||
opts.add("-Xmx128M");
|
||||
opts.add("-XX:+UnlockDiagnosticVMOptions");
|
||||
opts.add("-Xlog:monitorinflation=info");
|
||||
opts.addAll(Arrays.asList(args));
|
||||
opts.add("DeflationIntervalsTest$Test");
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(opts);
|
||||
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
|
||||
oa.shouldHaveExitValue(0);
|
||||
|
||||
switch (disabled) {
|
||||
case YES: oa.shouldContain(MSG_DISABLED); break;
|
||||
case NO: oa.shouldNotContain(MSG_DISABLED); break;
|
||||
case MAYBE: break;
|
||||
}
|
||||
|
||||
switch (threshold) {
|
||||
case YES: oa.shouldContain(MSG_THRESHOLD); break;
|
||||
case NO: oa.shouldNotContain(MSG_THRESHOLD); break;
|
||||
case MAYBE: break;
|
||||
}
|
||||
|
||||
switch (guaranteed) {
|
||||
case YES: oa.shouldContain(MSG_GUARANTEED); break;
|
||||
case NO: oa.shouldNotContain(MSG_GUARANTEED); break;
|
||||
case MAYBE: break;
|
||||
}
|
||||
|
||||
if (threshold == Threshold.YES || guaranteed == Guaranteed.YES) {
|
||||
assertDeflations(oa);
|
||||
} else if (threshold == Threshold.NO && guaranteed == Guaranteed.NO) {
|
||||
assertNoDeflations(oa);
|
||||
} else {
|
||||
// Don't know
|
||||
}
|
||||
}
|
||||
|
||||
static final String MSG_FINAL_AUDIT = "Starting the final audit";
|
||||
static final String MSG_BEGIN_DEFLATING = "begin deflating";
|
||||
|
||||
private static void assertNoDeflations(OutputAnalyzer oa) {
|
||||
for (String line : oa.asLines()) {
|
||||
if (line.contains(MSG_FINAL_AUDIT)) {
|
||||
// Final deflations started, with no prior deflations, good.
|
||||
return;
|
||||
}
|
||||
if (line.contains(MSG_BEGIN_DEFLATING)) {
|
||||
// Deflations detected before final ones, bad
|
||||
oa.reportDiagnosticSummary();
|
||||
throw new IllegalStateException("FAILED");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertDeflations(OutputAnalyzer oa) {
|
||||
for (String line : oa.asLines()) {
|
||||
if (line.contains(MSG_FINAL_AUDIT)) {
|
||||
// Final deflations started, with no prior deflations, bad.
|
||||
oa.reportDiagnosticSummary();
|
||||
throw new IllegalStateException("FAILED");
|
||||
}
|
||||
if (line.contains(MSG_BEGIN_DEFLATING)) {
|
||||
// Deflations detected before final ones, good
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Disabled {
|
||||
YES,
|
||||
NO,
|
||||
MAYBE,
|
||||
}
|
||||
|
||||
enum Threshold {
|
||||
YES,
|
||||
NO,
|
||||
MAYBE,
|
||||
}
|
||||
|
||||
enum Guaranteed {
|
||||
YES,
|
||||
NO,
|
||||
MAYBE,
|
||||
}
|
||||
|
||||
}
|
||||
178
test/hotspot/jtreg/runtime/Monitor/MonitorUnlinkBatchTest.java
Normal file
178
test/hotspot/jtreg/runtime/Monitor/MonitorUnlinkBatchTest.java
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Copyright Amazon.com Inc. 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.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* @test id=defaults
|
||||
* @bug 8319048
|
||||
* @summary Test the MonitorUnlinkBatch options
|
||||
* @library /test/lib
|
||||
* @run driver MonitorUnlinkBatchTest defaults
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=legal
|
||||
* @library /test/lib
|
||||
* @run driver MonitorUnlinkBatchTest legal
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=illegal
|
||||
* @library /test/lib
|
||||
* @run driver MonitorUnlinkBatchTest illegal
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=aggressive
|
||||
* @library /test/lib
|
||||
* @run driver MonitorUnlinkBatchTest aggressive
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=lazy
|
||||
* @library /test/lib
|
||||
* @run driver MonitorUnlinkBatchTest lazy
|
||||
*/
|
||||
|
||||
|
||||
public class MonitorUnlinkBatchTest {
|
||||
|
||||
public static class Test {
|
||||
// Inflate a lot of monitors, so that threshold heuristics definitely fires
|
||||
private static final int MONITORS = 10_000;
|
||||
|
||||
// Use a handful of threads to inflate the monitors, to eat the cost of
|
||||
// wait(1) calls. This can be larger than available parallelism, since threads
|
||||
// would be time-waiting.
|
||||
private static final int THREADS = 16;
|
||||
|
||||
private static Thread[] threads;
|
||||
private static Object[] monitors;
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
monitors = new Object[MONITORS];
|
||||
threads = new Thread[THREADS];
|
||||
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
int monStart = t * MONITORS / THREADS;
|
||||
int monEnd = (t + 1) * MONITORS / THREADS;
|
||||
threads[t] = new Thread(() -> {
|
||||
for (int m = monStart; m < monEnd; m++) {
|
||||
Object o = new Object();
|
||||
synchronized (o) {
|
||||
try {
|
||||
o.wait(1);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
monitors[m] = o;
|
||||
}
|
||||
});
|
||||
threads[t].start();
|
||||
}
|
||||
|
||||
for (Thread t : threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(10_000);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length < 1) {
|
||||
throw new IllegalArgumentException("Expect the test label");
|
||||
}
|
||||
|
||||
String test = args[0];
|
||||
switch (test) {
|
||||
case "defaults":
|
||||
test("");
|
||||
break;
|
||||
|
||||
case "legal":
|
||||
// Legal, even if not useful settings
|
||||
test("",
|
||||
"-XX:MonitorDeflationMax=100000",
|
||||
"-XX:MonitorUnlinkBatch=100001"
|
||||
);
|
||||
break;
|
||||
|
||||
case "illegal":
|
||||
// Quick tests that should fail on JVM flags verification.
|
||||
test("outside the allowed range",
|
||||
"-XX:MonitorUnlinkBatch=-1"
|
||||
);
|
||||
test("outside the allowed range",
|
||||
"-XX:MonitorUnlinkBatch=0"
|
||||
);
|
||||
break;
|
||||
|
||||
case "aggressive":
|
||||
// The smallest batch possible.
|
||||
test("",
|
||||
"-XX:MonitorUnlinkBatch=1"
|
||||
);
|
||||
break;
|
||||
|
||||
case "lazy":
|
||||
// The largest batch possible.
|
||||
test("",
|
||||
"-XX:MonitorDeflationMax=1000000",
|
||||
"-XX:MonitorUnlinkBatch=1000000"
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown test: " + test);
|
||||
}
|
||||
}
|
||||
|
||||
public static void test(String msg, String... args) throws Exception {
|
||||
List<String> opts = new ArrayList<>();
|
||||
opts.add("-Xmx128M");
|
||||
opts.add("-XX:+UnlockDiagnosticVMOptions");
|
||||
opts.add("-XX:GuaranteedAsyncDeflationInterval=100");
|
||||
opts.addAll(Arrays.asList(args));
|
||||
opts.add("MonitorUnlinkBatchTest$Test");
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(opts);
|
||||
OutputAnalyzer oa = new OutputAnalyzer(pb.start());
|
||||
if (msg.isEmpty()) {
|
||||
oa.shouldHaveExitValue(0);
|
||||
} else {
|
||||
oa.shouldNotHaveExitValue(0);
|
||||
oa.shouldContain(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2024, 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.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8226416
|
||||
* @summary Test the MonitorUsedDeflationThreshold and NoAsyncDeflationProgressMax options.
|
||||
* @requires vm.flagless
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib
|
||||
* @run driver MonitorUsedDeflationThresholdTest
|
||||
*/
|
||||
|
||||
public class MonitorUsedDeflationThresholdTest {
|
||||
public static final int DELAY_SECS = 10;
|
||||
public static int inflate_count = 0;
|
||||
public static Object[] monitors;
|
||||
|
||||
public static void do_work(int count) {
|
||||
System.out.println("Recursion count=" + count);
|
||||
if (count > inflate_count) {
|
||||
System.out.println("Exceeded inflate_count=" + inflate_count);
|
||||
|
||||
System.out.println("Delaying for " + DELAY_SECS + " secs.");
|
||||
try {
|
||||
Thread.sleep(DELAY_SECS * 1000);
|
||||
} catch (InterruptedException ie) {
|
||||
// ignore InterruptedException
|
||||
}
|
||||
System.out.println("Done delaying for " + DELAY_SECS + " secs.");
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized(monitors[count]) {
|
||||
try {
|
||||
monitors[count].wait(1); // force inflation
|
||||
} catch (InterruptedException ie) {
|
||||
// ignore InterruptedException
|
||||
}
|
||||
do_work(count + 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void usage() {
|
||||
System.err.println("Usage: java " +
|
||||
"MonitorUsedDeflationThresholdTest inflate_count");
|
||||
}
|
||||
|
||||
|
||||
private static ProcessBuilder processCommand(String loggingLevel) {
|
||||
return ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
// Test doesn't need much Java heap:
|
||||
"-Xmx100M",
|
||||
// AvgMonitorsPerThreadEstimate == 1 means we'll start with
|
||||
// an in_use_list_ceiling of <n-threads> plus a couple of
|
||||
// of monitors for threads that call Object.wait().
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:AvgMonitorsPerThreadEstimate=1",
|
||||
// MonitorUsedDeflationThreshold == 10 means we'll request
|
||||
// deflations when 10% of monitors are used rather than the
|
||||
// default 90%. This should allow the test to tolerate a burst
|
||||
// of used monitors by threads not under this test's control.
|
||||
"-XX:MonitorUsedDeflationThreshold=10",
|
||||
// Enable monitorinflation logging so we can see that
|
||||
// MonitorUsedDeflationThreshold and
|
||||
// NoAsyncDeflationProgressMaxoption are working.
|
||||
"-Xlog:monitorinflation=" + loggingLevel,
|
||||
// Run the test with inflate_count == 33 since that
|
||||
// reproduced the bug with JDK13. With inflate_count == 33, an
|
||||
// initial ceiling == 12 and MonitorUsedDeflationThreshold == 10,
|
||||
// we should hit NoAsyncDeflationProgressMax at least 3 times.
|
||||
"MonitorUsedDeflationThresholdTest", "33");
|
||||
}
|
||||
|
||||
private static void testProcess1() throws Exception {
|
||||
ProcessBuilder pb = processCommand("info");
|
||||
|
||||
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
|
||||
output_detail.shouldHaveExitValue(0);
|
||||
|
||||
// This mesg means:
|
||||
// - AvgMonitorsPerThreadEstimate == 1 reduced in_use_list_ceiling
|
||||
// to a small number.
|
||||
// - and we crossed MonitorUsedDeflationThreshold:
|
||||
output_detail.shouldMatch("begin deflating: .*");
|
||||
System.out.println("Found beginning of a deflation cycle.");
|
||||
|
||||
// This mesg means we hit NoAsyncDeflationProgressMax and
|
||||
// had to adjust the in_use_list_ceiling:
|
||||
String too_many = output_detail.firstMatch("Too many deflations without progress; .*", 0);
|
||||
if (too_many == null) {
|
||||
output_detail.reportDiagnosticSummary();
|
||||
throw new RuntimeException("Did not find too_many string in output.\n");
|
||||
}
|
||||
System.out.println("too_many='" + too_many + "'");
|
||||
// Uncomment the following line for dumping test output in passing runs:
|
||||
// output_detail.reportDiagnosticSummary();
|
||||
|
||||
System.out.println("PASSED.");
|
||||
}
|
||||
|
||||
private static void testProcess2() throws Exception {
|
||||
ProcessBuilder pb = processCommand("debug");
|
||||
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
|
||||
output_detail.shouldHaveExitValue(0);
|
||||
|
||||
// Test that logging reports in_use_list with each iteration of the deflation thread in debug mode.
|
||||
// but not monitor details with each report
|
||||
output_detail.shouldMatch( ".debug..monitorinflation. Checking in_use_list:");
|
||||
output_detail.shouldNotMatch(".debug..monitorinflation. .*is_busy");
|
||||
|
||||
// Reporting stats at exit is in Info mode, and contains monitor details
|
||||
output_detail.shouldMatch(".info ..monitorinflation. Checking in_use_list:");
|
||||
output_detail.shouldMatch(".info ..monitorinflation. .*is_busy");
|
||||
|
||||
System.out.println("PASSED.");
|
||||
}
|
||||
|
||||
private static void testProcess3() throws Exception {
|
||||
ProcessBuilder pb = processCommand("trace");
|
||||
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
|
||||
output_detail.shouldHaveExitValue(0);
|
||||
|
||||
// Test that logging reports in_use_list with each iteration of the deflation thread in debug mode.
|
||||
// and monitor details with each report
|
||||
output_detail.shouldMatch(".debug..monitorinflation. Checking in_use_list:");
|
||||
output_detail.shouldMatch(".trace..monitorinflation. .*is_busy");
|
||||
|
||||
// Reporting stats at exit is in Info mode, and contains monitor details
|
||||
output_detail.shouldMatch(".info ..monitorinflation. Checking in_use_list:");
|
||||
output_detail.shouldMatch(".info ..monitorinflation. .*is_busy");
|
||||
|
||||
System.out.println("PASSED.");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
// Without args we invoke the tests in a java sub-process.
|
||||
testProcess1();
|
||||
testProcess2();
|
||||
testProcess3();
|
||||
return;
|
||||
}
|
||||
// else we are the exec'd java subprocess, so run the actual test:
|
||||
|
||||
try {
|
||||
inflate_count = Integer.decode(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
usage();
|
||||
throw new RuntimeException("ERROR: '" + args[0] +
|
||||
"': bad inflate_count.");
|
||||
}
|
||||
|
||||
System.out.println("Hello from MonitorUsedDeflationThresholdTest!");
|
||||
System.out.println("inflate_count=" + inflate_count);
|
||||
|
||||
monitors = new Object[inflate_count + 1];
|
||||
for (int i = 1; i <= inflate_count; i++) {
|
||||
monitors[i] = new Object();
|
||||
}
|
||||
do_work(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @bug 8320515
|
||||
* @summary This test checks that ObjectMonitors with dead objects don't
|
||||
* cause asserts, crashes, or failures when various sub-systems
|
||||
* in the JVM find them.
|
||||
* @library /testlibrary /test/lib
|
||||
* @modules jdk.management
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=DetachThread
|
||||
* @requires os.family != "windows" & os.family != "aix"
|
||||
* @run main/othervm/native MonitorWithDeadObjectTest 0
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=DumpThreadsBeforeDetach
|
||||
* @comment Temporarily exclude on Musl-C debug until JDK-8366133 is fixed.
|
||||
* @requires os.family != "windows" & os.family != "aix" & (!vm.musl | !vm.debug)
|
||||
* @run main/othervm/native MonitorWithDeadObjectTest 1
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=DumpThreadsAfterDetach
|
||||
* @requires os.family != "windows" & os.family != "aix"
|
||||
* @run main/othervm/native MonitorWithDeadObjectTest 2
|
||||
*/
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
|
||||
public class MonitorWithDeadObjectTest {
|
||||
public static native void createMonitorWithDeadObject();
|
||||
public static native void createMonitorWithDeadObjectDumpThreadsBeforeDetach();
|
||||
|
||||
static {
|
||||
System.loadLibrary("MonitorWithDeadObjectTest");
|
||||
}
|
||||
|
||||
private static void dumpThreadsWithLockedMonitors() {
|
||||
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
|
||||
threadBean.dumpAllThreads(true, false);
|
||||
}
|
||||
|
||||
private static void testDetachThread() {
|
||||
// Create an ObjectMonitor with a dead object from an attached thread.
|
||||
// This used to provoke an assert in DetachCurrentThread.
|
||||
createMonitorWithDeadObject();
|
||||
}
|
||||
|
||||
private static void testDumpThreadsBeforeDetach() {
|
||||
// Create an ObjectMonitor with a dead object from an attached thread
|
||||
// and perform a thread dump before detaching the thread.
|
||||
createMonitorWithDeadObjectDumpThreadsBeforeDetach();
|
||||
}
|
||||
|
||||
private static void testDumpThreadsAfterDetach() {
|
||||
createMonitorWithDeadObject();
|
||||
|
||||
// The thread dumping code used to not tolerate monitors with dead
|
||||
// objects and the detach code used to not unlock these monitors, so
|
||||
// test that we don't end up with a bug where these monitors are not
|
||||
// unlocked and then passed to the thread dumping code.
|
||||
dumpThreadsWithLockedMonitors();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int test = Integer.parseInt(args[0]);
|
||||
switch (test) {
|
||||
case 0: testDetachThread(); break;
|
||||
case 1: testDumpThreadsBeforeDetach(); break;
|
||||
case 2: testDumpThreadsAfterDetach(); break;
|
||||
default: throw new RuntimeException("Unknown test");
|
||||
};
|
||||
}
|
||||
}
|
||||
83
test/hotspot/jtreg/runtime/Monitor/NonOwnerOps.java
Normal file
83
test/hotspot/jtreg/runtime/Monitor/NonOwnerOps.java
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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
|
||||
* @bug 8229212
|
||||
* @summary Verify that monitor operations by a non-owner thread throw
|
||||
* IllegalMonitorStateException.
|
||||
* @run main NonOwnerOps
|
||||
*/
|
||||
|
||||
public class NonOwnerOps {
|
||||
public static void main(String[] args) {
|
||||
int error_count = 0;
|
||||
Object obj;
|
||||
|
||||
obj = new Object();
|
||||
try {
|
||||
obj.wait();
|
||||
System.err.println("ERROR: wait() by non-owner thread did not " +
|
||||
"throw IllegalMonitorStateException.");
|
||||
error_count++;
|
||||
} catch (InterruptedException ie) {
|
||||
System.err.println("ERROR: wait() by non-owner thread threw " +
|
||||
"InterruptedException which is not expected.");
|
||||
error_count++;
|
||||
} catch (IllegalMonitorStateException imse) {
|
||||
System.out.println("wait() by non-owner thread threw the " +
|
||||
"expected IllegalMonitorStateException:");
|
||||
System.out.println(" " + imse);
|
||||
}
|
||||
|
||||
obj = new Object();
|
||||
try {
|
||||
obj.notify();
|
||||
System.err.println("ERROR: notify() by non-owner thread did not " +
|
||||
"throw IllegalMonitorStateException.");
|
||||
error_count++;
|
||||
} catch (IllegalMonitorStateException imse) {
|
||||
System.out.println("notify() by non-owner thread threw the " +
|
||||
"expected IllegalMonitorStateException:");
|
||||
System.out.println(" " + imse);
|
||||
}
|
||||
|
||||
obj = new Object();
|
||||
try {
|
||||
obj.notifyAll();
|
||||
System.err.println("ERROR: notifyAll() by non-owner thread did " +
|
||||
"not throw IllegalMonitorStateException.");
|
||||
error_count++;
|
||||
} catch (IllegalMonitorStateException imse) {
|
||||
System.out.println("notifyAll() by non-owner thread threw the " +
|
||||
"expected IllegalMonitorStateException:");
|
||||
System.out.println(" " + imse);
|
||||
}
|
||||
|
||||
if (error_count != 0) {
|
||||
throw new RuntimeException("Test failed with " + error_count +
|
||||
" errors.");
|
||||
}
|
||||
System.out.println("Test PASSED.");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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/timeout=240 -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -Xint
|
||||
* -Xms256m -Xmx256m
|
||||
* TestRecursiveLocking 120 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/timeout=240 -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -Xint
|
||||
* -Xms256m -Xmx256m
|
||||
* TestRecursiveLocking 120 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/timeout=240 -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -XX:TieredStopAtLevel=1
|
||||
* -Xms256m -Xmx256m
|
||||
* TestRecursiveLocking 120 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/timeout=240 -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -XX:TieredStopAtLevel=1
|
||||
* -Xms256m -Xmx256m
|
||||
* TestRecursiveLocking 120 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/timeout=240 -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -XX:-EliminateNestedLocks
|
||||
* -Xms256m -Xmx256m
|
||||
* TestRecursiveLocking 120 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/timeout=240 -Xbootclasspath/a:.
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
|
||||
* -XX:-EliminateNestedLocks
|
||||
* -Xms256m -Xmx256m
|
||||
* TestRecursiveLocking 120 2
|
||||
*/
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2023, 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.
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8242263
|
||||
* @summary Exercise DiagnoseSyncOnValueBasedClasses diagnostic flag
|
||||
* @requires vm.flagless
|
||||
* @requires vm.flavor != "zero"
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=180000 SyncOnValueBasedClassTest
|
||||
*/
|
||||
|
||||
public class SyncOnValueBasedClassTest {
|
||||
static final int LOOP_COUNT = 3000;
|
||||
static final int THREAD_COUNT = 2;
|
||||
static String[] fatalTests[];
|
||||
static String[] logTests[];
|
||||
static List<Object> testObjects = new ArrayList<Object>();
|
||||
|
||||
private static final String[] specificFlags[] = {
|
||||
{"-Xint"},
|
||||
{"-Xcomp", "-XX:TieredStopAtLevel=1"},
|
||||
{"-Xcomp", "-XX:-TieredCompilation"},
|
||||
};
|
||||
|
||||
private static void initTestObjects() {
|
||||
testObjects.add(Character.valueOf('H'));
|
||||
testObjects.add(Boolean.valueOf(true));
|
||||
testObjects.add(Byte.valueOf((byte)0x40));
|
||||
testObjects.add(Short.valueOf((short)0x4000));
|
||||
testObjects.add(Integer.valueOf(0x40000000));
|
||||
testObjects.add(Long.valueOf(0x4000000000000000L));
|
||||
testObjects.add(Float.valueOf(1.20f));
|
||||
testObjects.add(Double.valueOf(1.2345));
|
||||
}
|
||||
|
||||
private static void generateTests() {
|
||||
initTestObjects();
|
||||
String[] commonFatalTestsFlags = {"-XX:+UnlockDiagnosticVMOptions", "-XX:-CreateCoredumpOnCrash", "-XX:DiagnoseSyncOnValueBasedClasses=1"};
|
||||
fatalTests = new String[specificFlags.length * testObjects.size()][];
|
||||
for (int i = 0; i < specificFlags.length; i++) {
|
||||
for (int j = 0; j < testObjects.size(); j++) {
|
||||
int index = i * testObjects.size() + j;
|
||||
fatalTests[index] = Stream.of(commonFatalTestsFlags, specificFlags[i], new String[] {"SyncOnValueBasedClassTest$FatalTest", Integer.toString(j)})
|
||||
.flatMap(Stream::of)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
String[] commonLogTestsFlags = {"-XX:+UnlockDiagnosticVMOptions", "-XX:DiagnoseSyncOnValueBasedClasses=2"};
|
||||
logTests = new String[specificFlags.length][];
|
||||
for (int i = 0; i < specificFlags.length; i++) {
|
||||
logTests[i] = Stream.of(commonLogTestsFlags, specificFlags[i], new String[] {"SyncOnValueBasedClassTest$LogTest"})
|
||||
.flatMap(Stream::of)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
generateTests();
|
||||
for (int i = 0; i < fatalTests.length; i++) {
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(fatalTests[i]);
|
||||
OutputAnalyzer output = ProcessTools.executeProcess(pb);
|
||||
output.shouldContain("fatal error: Synchronizing on object");
|
||||
output.shouldNotContain("synchronization on value based class did not fail");
|
||||
output.shouldNotHaveExitValue(0);
|
||||
}
|
||||
for (int i = 0; i < logTests.length; i++) {
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(logTests[i]);
|
||||
OutputAnalyzer output = ProcessTools.executeProcess(pb);
|
||||
output.shouldHaveExitValue(0);
|
||||
checkOutput(output);
|
||||
}
|
||||
virtualThreadTests();
|
||||
}
|
||||
|
||||
private static void checkOutput(OutputAnalyzer output) {
|
||||
String out = output.getOutput();
|
||||
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Character.*"));
|
||||
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Boolean.*"));
|
||||
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Byte.*"));
|
||||
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Short.*"));
|
||||
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Integer.*"));
|
||||
assertTrue(out.matches("(?s).*Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Long.*"));
|
||||
String[] res = out.split("Synchronizing on object 0[xX][0-9a-fA-F]+ of klass java\\.lang\\.Float\\R");
|
||||
assertTrue(res.length - 1 == (LOOP_COUNT * THREAD_COUNT + 1), res.length - 1);
|
||||
}
|
||||
|
||||
private static void assertTrue(boolean condition) {
|
||||
if (!condition) {
|
||||
throw new RuntimeException("No synchronization matches");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertTrue(boolean condition, int count) {
|
||||
if (!condition) {
|
||||
throw new RuntimeException("Synchronization count was " + count);
|
||||
}
|
||||
}
|
||||
|
||||
static class FatalTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
initTestObjects();
|
||||
synchronized (testObjects.get(Integer.valueOf(args[0]))) {
|
||||
throw new RuntimeException("synchronization on value based class did not fail");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class LogTest implements Runnable {
|
||||
private static long sharedCounter = 0L;
|
||||
private static Float sharedLock1 = 0.0f;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
initTestObjects();
|
||||
for (Object obj : testObjects) {
|
||||
synchronized (obj) {
|
||||
sharedCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
LogTest test = new LogTest();
|
||||
Thread[] threads = new Thread[THREAD_COUNT];
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
threads[i] = new Thread(test);
|
||||
threads[i].start();
|
||||
}
|
||||
for (Thread t : threads) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < LOOP_COUNT; i++) {
|
||||
synchronized (sharedLock1) {
|
||||
sharedCounter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Very basic sanity tests to show things work for virtual threads too.
|
||||
private static void virtualThreadTests() throws Exception {
|
||||
final String[] vtTest = { "-XX:+UnlockDiagnosticVMOptions", "-XX:-CreateCoredumpOnCrash",
|
||||
"", "SyncOnValueBasedClassTest$VTTest" };
|
||||
// Fatal test
|
||||
vtTest[2] = "-XX:DiagnoseSyncOnValueBasedClasses=1";
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(vtTest);
|
||||
OutputAnalyzer output = ProcessTools.executeProcess(pb);
|
||||
output.shouldContain("fatal error: Synchronizing on object");
|
||||
output.shouldNotContain("synchronization on value based class did not fail");
|
||||
output.shouldNotHaveExitValue(0);
|
||||
|
||||
// Log test
|
||||
vtTest[2] = "-XX:DiagnoseSyncOnValueBasedClasses=2";
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder(vtTest);
|
||||
output = ProcessTools.executeProcess(pb);
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldContain("Synchronizing on object");
|
||||
output.shouldContain("synchronization on value based class did not fail");
|
||||
}
|
||||
|
||||
static class VTTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
var thread = Thread.ofVirtual().start(() -> {
|
||||
synchronized (Character.valueOf('H')) {
|
||||
System.out.println("synchronization on value based class did not fail");
|
||||
}
|
||||
});
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
444
test/hotspot/jtreg/runtime/Monitor/TestRecursiveLocking.java
Normal file
444
test/hotspot/jtreg/runtime/Monitor/TestRecursiveLocking.java
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* 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
|
||||
* -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
|
||||
* -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
|
||||
* -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
|
||||
* -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
|
||||
* -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:-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 constLockStackCapacity = WB.getLockStackCapacity();
|
||||
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++;
|
||||
|
||||
// There is limit on recursion, so "outer" must be
|
||||
// inflated here.
|
||||
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.
|
||||
assertNotInflated();
|
||||
if (depth == 1) {
|
||||
return;
|
||||
} else {
|
||||
runInner(depth - 1, outer);
|
||||
}
|
||||
assertNotInflated();
|
||||
}
|
||||
|
||||
synchronized void runOuter(int depth, SynchronizedObject inner) {
|
||||
counter++;
|
||||
|
||||
assertNotInflated();
|
||||
if (depth == 1) {
|
||||
inner.runInner(constLockStackCapacity, this);
|
||||
} else {
|
||||
runOuter(depth - 1, inner);
|
||||
}
|
||||
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++;
|
||||
|
||||
OUTER.assertNotInflated();
|
||||
INNER.assertNotInflated();
|
||||
OUTER.runOuter(constLockStackCapacity - 1, INNER);
|
||||
OUTER.assertInflated();
|
||||
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++;
|
||||
|
||||
|
||||
// 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]. Fast 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();
|
||||
}
|
||||
|
||||
|
||||
// Call runB() at the same depth as runA's depth:
|
||||
B.runB(depth, this);
|
||||
}
|
||||
|
||||
synchronized void runB(int depth, SynchronizedObject A) {
|
||||
counter++;
|
||||
|
||||
|
||||
// Legacy tolerates endless recursions. While testing 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();
|
||||
|
||||
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);
|
||||
|
||||
// Here A can be either inflated or not because A is not
|
||||
// locked anymore and subject to deflation.
|
||||
|
||||
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: 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Copyright (c) 2024, 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=NormalDeflation
|
||||
* @summary A collection of small tests using synchronized, wait, notify to try
|
||||
* and achieve good cheap coverage of UseObjectMonitorTable.
|
||||
* @library /test/lib
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
||||
* -XX:+UseObjectMonitorTable
|
||||
* UseObjectMonitorTableTest
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=ExtremeDeflation
|
||||
* @summary Run the same tests but with deflation running constantly.
|
||||
* @library /test/lib
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
|
||||
* -XX:GuaranteedAsyncDeflationInterval=1
|
||||
* -XX:+UseObjectMonitorTable
|
||||
* UseObjectMonitorTableTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
import java.lang.Runnable;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class UseObjectMonitorTableTest {
|
||||
static final ThreadFactory TF = Executors.defaultThreadFactory();
|
||||
|
||||
static class WaitNotifyTest implements Runnable {
|
||||
static final int ITERATIONS = 10_000;
|
||||
static final int THREADS = 10;
|
||||
final WaitNotifySyncChannel startLatchChannel = new WaitNotifySyncChannel();
|
||||
final WaitNotifySyncChannel endLatchChannel = new WaitNotifySyncChannel();
|
||||
int count = 0;
|
||||
|
||||
static class WaitNotifyCountDownLatch {
|
||||
int latch;
|
||||
WaitNotifyCountDownLatch(int count) {
|
||||
latch = count;
|
||||
}
|
||||
synchronized void await() {
|
||||
while (latch != 0) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("WaitNotifyTest: Unexpected interrupt", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized void countDown() {
|
||||
if (latch != 0) {
|
||||
latch--;
|
||||
if (latch == 0) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static class WaitNotifySyncChannel extends WaitNotifyCountDownLatch {
|
||||
WaitNotifyCountDownLatch object;
|
||||
WaitNotifySyncChannel() { super(0); }
|
||||
synchronized void send(WaitNotifyCountDownLatch object, int count) {
|
||||
await();
|
||||
latch = count;
|
||||
this.object = object;
|
||||
notifyAll();
|
||||
}
|
||||
synchronized WaitNotifyCountDownLatch receive() {
|
||||
while (latch == 0) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("WaitNotifyTest: Unexpected interrupt", e);
|
||||
}
|
||||
}
|
||||
countDown();
|
||||
return object;
|
||||
}
|
||||
}
|
||||
synchronized int getCount() {
|
||||
return count;
|
||||
}
|
||||
synchronized void increment() {
|
||||
count++;
|
||||
}
|
||||
public void run() {
|
||||
System.out.println("WaitNotifyTest started.");
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
TF.newThread(() -> {
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
startLatchChannel.receive().await();
|
||||
increment();
|
||||
endLatchChannel.receive().countDown();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
WaitNotifyCountDownLatch startLatch = new WaitNotifyCountDownLatch(1);
|
||||
WaitNotifyCountDownLatch endLatch = new WaitNotifyCountDownLatch(THREADS);
|
||||
int count = getCount();
|
||||
if (count != i * THREADS) {
|
||||
throw new RuntimeException("WaitNotifyTest: Invalid Count " + count +
|
||||
" pre-iteration " + i);
|
||||
}
|
||||
startLatchChannel.send(startLatch, 10);
|
||||
startLatch.countDown();
|
||||
endLatchChannel.send(endLatch, 10);
|
||||
endLatch.await();
|
||||
}
|
||||
int count = getCount();
|
||||
if (count != ITERATIONS * THREADS) {
|
||||
throw new RuntimeException("WaitNotifyTest: Invalid Count " + count);
|
||||
}
|
||||
System.out.println("WaitNotifyTest passed.");
|
||||
}
|
||||
}
|
||||
|
||||
static class RandomDepthTest implements Runnable {
|
||||
static final int THREADS = 10;
|
||||
static final int ITERATIONS = 10_000;
|
||||
static final int MAX_DEPTH = 20;
|
||||
static final int MAX_RECURSION_COUNT = 10;
|
||||
static final double RECURSION_CHANCE = .25;
|
||||
final Random random = Utils.getRandomInstance();
|
||||
final Locker lockers[] = new Locker[MAX_DEPTH];
|
||||
final CyclicBarrier syncBarrier = new CyclicBarrier(THREADS + 1);
|
||||
int count = 0;
|
||||
|
||||
class Locker {
|
||||
final int depth;
|
||||
Locker(int depth) {
|
||||
this.depth = depth;
|
||||
}
|
||||
synchronized int getCount() {
|
||||
if (depth == MAX_DEPTH) {
|
||||
return count;
|
||||
}
|
||||
return lockers[depth].getCount();
|
||||
}
|
||||
synchronized void increment(int recursion_count) {
|
||||
if (recursion_count != MAX_RECURSION_COUNT &&
|
||||
random.nextDouble() < RECURSION_CHANCE) {
|
||||
this.increment(recursion_count + 1);
|
||||
return;
|
||||
}
|
||||
if (depth == MAX_DEPTH) {
|
||||
count++;
|
||||
return;
|
||||
}
|
||||
lockers[depth + random.nextInt(MAX_DEPTH - depth)].increment(recursion_count);
|
||||
}
|
||||
synchronized Locker create() {
|
||||
if (depth != MAX_DEPTH) {
|
||||
lockers[depth] = (new Locker(depth + 1)).create();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
int getCount() {
|
||||
return lockers[0].getCount();
|
||||
}
|
||||
void increment() {
|
||||
lockers[random.nextInt(MAX_DEPTH)].increment(0);
|
||||
}
|
||||
void create() {
|
||||
lockers[0] = (new Locker(1)).create();
|
||||
}
|
||||
void syncPoint() {
|
||||
try {
|
||||
syncBarrier.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("RandomDepthTest: Unexpected interrupt", e);
|
||||
} catch (BrokenBarrierException e) {
|
||||
throw new RuntimeException("RandomDepthTest: Unexpected broken barrier", e);
|
||||
}
|
||||
}
|
||||
public void run() {
|
||||
System.out.println("RandomDepthTest started.");
|
||||
for (int t = 0; t < THREADS; t++) {
|
||||
TF.newThread(() -> {
|
||||
syncPoint();
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
increment();
|
||||
}
|
||||
syncPoint();
|
||||
}).start();
|
||||
}
|
||||
create();
|
||||
syncPoint();
|
||||
syncPoint();
|
||||
int count = getCount();
|
||||
if (count != THREADS * ITERATIONS) {
|
||||
throw new RuntimeException("RandomDepthTest: Invalid Count " + count);
|
||||
}
|
||||
System.out.println("RandomDepthTest passed.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Stream.of(
|
||||
TF.newThread(new WaitNotifyTest()),
|
||||
TF.newThread(new RandomDepthTest())
|
||||
).map(t -> {
|
||||
t.start();
|
||||
return t;
|
||||
}).forEach(t -> {
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("UseObjectMonitorTableTest: Unexpected interrupt", e);
|
||||
}
|
||||
});
|
||||
|
||||
System.out.println("UseObjectMonitorTableTest passed.");
|
||||
}
|
||||
}
|
||||
78
test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c
Normal file
78
test/hotspot/jtreg/runtime/Monitor/libCompleteExit.c
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define die(x) do { printf("%s:%s\n",x , __func__); perror(x); exit(EXIT_FAILURE); } while (0)
|
||||
|
||||
#ifndef _Included_CompleteExit
|
||||
#define _Included_CompleteExit
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static JavaVM* jvm;
|
||||
static pthread_t attacher;
|
||||
|
||||
static jobject t1, t2;
|
||||
|
||||
static void* do_test() {
|
||||
JNIEnv* env;
|
||||
int res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);
|
||||
if (res != JNI_OK) die("AttachCurrentThread");
|
||||
|
||||
if ((*env)->MonitorEnter(env, t1) != 0) die("MonitorEnter");
|
||||
if ((*env)->MonitorEnter(env, t2) != 0) die("MonitorEnter");
|
||||
|
||||
if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) die("DetachCurrentThread");
|
||||
pthread_exit(NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: CompleteExit
|
||||
* Method: startThread
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_CompleteExit_testIt(JNIEnv* env, jclass jc, jobject o1, jobject o2) {
|
||||
void* ret;
|
||||
pthread_attr_t attr;
|
||||
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
|
||||
t1 = (*env)->NewGlobalRef(env, o1);
|
||||
t2 = (*env)->NewGlobalRef(env, o2);
|
||||
|
||||
if (pthread_attr_init(&attr) != 0) die("pthread_attr_init");
|
||||
if (pthread_create(&attacher, &attr, do_test, NULL) != 0) die("pthread_create");
|
||||
if (pthread_join(attacher, &ret) != 0) die("pthread_join");
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 2023, 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.
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static JavaVM* jvm;
|
||||
static pthread_t attacher;
|
||||
|
||||
#define die(x) do { printf("%s:%s\n",x , __func__); perror(x); exit(EXIT_FAILURE); } while (0)
|
||||
|
||||
static void check_exception(JNIEnv* env, const char* msg) {
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
fprintf(stderr, "Error: %s", msg);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
#define check(env, what, msg) \
|
||||
check_exception((env), (msg)); \
|
||||
do { \
|
||||
if ((what) == 0) { \
|
||||
fprintf(stderr, #what "is null: %s", (msg)); \
|
||||
exit(-2); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static jobject create_object(JNIEnv* env) {
|
||||
jclass clazz = (*env)->FindClass(env, "java/lang/Object");
|
||||
check(env, clazz, "No class");
|
||||
|
||||
jmethodID constructor = (*env)->GetMethodID(env, clazz, "<init>", "()V");
|
||||
check(env, constructor, "No constructor");
|
||||
|
||||
jobject obj = (*env)->NewObject(env, clazz, constructor);
|
||||
check(env, constructor, "No object");
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static void system_gc(JNIEnv* env) {
|
||||
jclass clazz = (*env)->FindClass(env, "java/lang/System");
|
||||
check(env, clazz, "No class");
|
||||
|
||||
jmethodID method = (*env)->GetStaticMethodID(env, clazz, "gc", "()V");
|
||||
check(env, method, "No method");
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, clazz, method);
|
||||
check_exception(env, "Calling System.gc()");
|
||||
}
|
||||
|
||||
static void thread_dump_with_locked_monitors(JNIEnv* env) {
|
||||
jclass ManagementFactoryClass = (*env)->FindClass(env, "java/lang/management/ManagementFactory");
|
||||
check(env, ManagementFactoryClass, "No ManagementFactory class");
|
||||
|
||||
jmethodID getThreadMXBeanMethod = (*env)->GetStaticMethodID(env, ManagementFactoryClass, "getThreadMXBean", "()Ljava/lang/management/ThreadMXBean;");
|
||||
check(env, getThreadMXBeanMethod, "No getThreadMXBean method");
|
||||
|
||||
jobject threadBean = (*env)->CallStaticObjectMethod(env, ManagementFactoryClass, getThreadMXBeanMethod);
|
||||
check(env, threadBean, "Calling getThreadMXBean()");
|
||||
|
||||
jclass ThreadMXBeanClass = (*env)->FindClass(env, "java/lang/management/ThreadMXBean");
|
||||
check(env, ThreadMXBeanClass, "No ThreadMXBean class");
|
||||
|
||||
jmethodID dumpAllThreadsMethod = (*env)->GetMethodID(env, ThreadMXBeanClass, "dumpAllThreads", "(ZZ)[Ljava/lang/management/ThreadInfo;");
|
||||
check(env, dumpAllThreadsMethod, "No dumpAllThreads method");
|
||||
|
||||
// The 'lockedMonitors == true' is what causes the monitor with a dead object to be examined.
|
||||
jobject array = (*env)->CallObjectMethod(env, threadBean, dumpAllThreadsMethod, JNI_TRUE /* lockedMonitors */, JNI_FALSE /* lockedSynchronizers*/);
|
||||
check(env, array, "Calling dumpAllThreads(true, false)");
|
||||
}
|
||||
|
||||
static void create_monitor_with_dead_object(JNIEnv* env) {
|
||||
jobject obj = create_object(env);
|
||||
|
||||
if ((*env)->MonitorEnter(env, obj) != 0) die("MonitorEnter");
|
||||
|
||||
// Drop the last strong reference to the object associated with the monitor.
|
||||
// The monitor only keeps a weak reference to the object.
|
||||
(*env)->DeleteLocalRef(env, obj);
|
||||
|
||||
// Let the GC clear the weak reference to the object.
|
||||
system_gc(env);
|
||||
}
|
||||
|
||||
static void* create_monitor_with_dead_object_in_thread(void* arg) {
|
||||
JNIEnv* env;
|
||||
int res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);
|
||||
if (res != JNI_OK) die("AttachCurrentThread");
|
||||
|
||||
// Make the correct incantation to create a monitor with a dead object.
|
||||
create_monitor_with_dead_object(env);
|
||||
|
||||
// DetachCurrentThread will try to unlock held monitors. This has been a
|
||||
// source of at least two bugs:
|
||||
// - When the object reference in the monitor was cleared, the monitor
|
||||
// iterator code would skip it, preventing it from being unlocked when
|
||||
// the owner thread detached, leaving it lingering in the system.
|
||||
// - When the monitor iterator API was rewritten the code was changed to
|
||||
// assert that we didn't have "owned" monitors with dead objects. This
|
||||
// test provokes that situation and that asserts.
|
||||
if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) die("DetachCurrentThread");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* create_monitor_with_dead_object_and_dump_threads_in_thread(void* arg) {
|
||||
JNIEnv* env;
|
||||
int res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);
|
||||
if (res != JNI_OK) die("AttachCurrentThread");
|
||||
|
||||
// Make the correct incantation to create a monitor with a dead object.
|
||||
create_monitor_with_dead_object(env);
|
||||
|
||||
// Perform a thread dump that checks for all thread's monitors.
|
||||
// That code didn't expect the monitor iterators to return monitors
|
||||
// with dead objects and therefore asserted/crashed.
|
||||
thread_dump_with_locked_monitors(env);
|
||||
|
||||
if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) die("DetachCurrentThread");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_MonitorWithDeadObjectTest_createMonitorWithDeadObject(JNIEnv* env, jclass jc) {
|
||||
void* ret;
|
||||
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
|
||||
if (pthread_create(&attacher, NULL, create_monitor_with_dead_object_in_thread, NULL) != 0) die("pthread_create");
|
||||
if (pthread_join(attacher, &ret) != 0) die("pthread_join");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_MonitorWithDeadObjectTest_createMonitorWithDeadObjectDumpThreadsBeforeDetach(JNIEnv* env, jclass jc) {
|
||||
void* ret;
|
||||
|
||||
(*env)->GetJavaVM(env, &jvm);
|
||||
|
||||
if (pthread_create(&attacher, NULL, create_monitor_with_dead_object_and_dump_threads_in_thread, NULL) != 0) die("pthread_create");
|
||||
if (pthread_join(attacher, &ret) != 0) die("pthread_join");
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue