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:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View 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);
}
}

View 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");
}
}

View 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,
}
}

View 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);
}
}
}

View file

@ -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);
}
}

View file

@ -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");
};
}
}

View 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.");
}
}

View file

@ -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
*/

View file

@ -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();
}
}
}

View 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.");
}
}
}

View file

@ -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.");
}
}

View 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

View file

@ -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