From d3a478cf757495a8a5c6144bfa95f9350cea25e1 Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Mon, 29 Sep 2025 23:57:38 -0400 Subject: [PATCH 1/4] v1 --- test/hotspot/jtreg/ProblemList-Virtual.txt | 1 - .../Thread/AsyncExceptionOnMonitorEnter.java | 41 ++++++++++--------- .../runtime/Thread/AsyncExceptionTest.java | 17 ++++---- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/test/hotspot/jtreg/ProblemList-Virtual.txt b/test/hotspot/jtreg/ProblemList-Virtual.txt index 3c74d7bf81658..44fd6122a78e4 100644 --- a/test/hotspot/jtreg/ProblemList-Virtual.txt +++ b/test/hotspot/jtreg/ProblemList-Virtual.txt @@ -90,6 +90,5 @@ vmTestbase/nsk/jdi/ThreadReference/isSuspended/issuspended002/TestDescription.ja gc/arguments/TestNewSizeThreadIncrease.java 0000000 generic-all gc/g1/TestSkipRebuildRemsetPhase.java 0000000 generic-all runtime/ErrorHandling/MachCodeFramesInErrorFile.java 0000000 generic-all -runtime/Thread/AsyncExceptionOnMonitorEnter.java 0000000 generic-all runtime/Thread/StopAtExit.java 0000000 generic-all runtime/handshake/HandshakeWalkStackTest.java 0000000 generic-all diff --git a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java index 8446ffb20fe2f..bdaddaff80132 100644 --- a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java +++ b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java @@ -44,9 +44,13 @@ public class AsyncExceptionOnMonitorEnter extends Thread { public static native int exitRawMonitor(); public static native void destroyRawMonitor(); + // Avoid using CountDownLatch or similar objects that require unparking the + // main thread. Otherwise, if the main thread is run as a virtual thread, the + // async exception could be sent while the target is still executing FJP logic. + public volatile boolean started = false; + public volatile boolean gotMonitor = false; + private static Object o1 = new Object(); - private static boolean firstWorker = true; - private static Semaphore sem = new Semaphore(0); @Override public void run() { @@ -59,11 +63,9 @@ public void run() { public void testWithJavaMonitor() { try { + started = true; synchronized (o1) { - if (firstWorker) { - firstWorker = false; - sem.release(); - } + gotMonitor = true; Thread.sleep(1000); } } catch (ThreadDeath td) { @@ -74,20 +76,16 @@ public void testWithJavaMonitor() { public void testWithJVMTIRawMonitor() { - boolean savedFirst = false; + started = true; try { int retCode = enterRawMonitor(); - if (retCode != 0 && firstWorker) { + if (retCode != 0) { throw new RuntimeException("error in JVMTI RawMonitorEnter: retCode=" + retCode); } - if (firstWorker) { - firstWorker = false; - savedFirst = true; - sem.release(); - } - Thread.sleep(1000); + gotMonitor = true; + Thread.sleep(500); retCode = exitRawMonitor(); - if (retCode != 0 && savedFirst) { + if (retCode != 0) { throw new RuntimeException("error in JVMTI RawMonitorExit: retCode=" + retCode); } } catch (ThreadDeath td) { @@ -133,15 +131,18 @@ public static void main(String[] args) { AsyncExceptionOnMonitorEnter worker2 = new AsyncExceptionOnMonitorEnter(); try { - // Start firstWorker worker and wait until monitor is acquired - firstWorker = true; + // Start first worker and wait until monitor is acquired worker1.start(); - sem.acquire(); + while (!worker1.gotMonitor) { + Thread.sleep(1); + } // Start second worker and allow some time for target to block on monitorenter // before executing Thread.stop() worker2.start(); - Thread.sleep(300); + while (!worker2.started) { + Thread.sleep(10); + } while (true) { JVMTIUtils.stopThread(worker2); @@ -150,6 +151,8 @@ public static void main(String[] args) { // not released worker2 will deadlock on enter JVMTIUtils.stopThread(worker1); } + // Give time to throw exception + Thread.sleep(10); if (!worker1.isAlive() && !worker2.isAlive()) { // Done with Thread.stop() calls since diff --git a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionTest.java b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionTest.java index 52fc4ca1d567f..8bf1560064842 100644 --- a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionTest.java +++ b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionTest.java @@ -49,9 +49,11 @@ public class AsyncExceptionTest extends Thread { private final static int DEF_TIME_MAX = 30; // default max # secs to test private final static String PROG_NAME = "AsyncExceptionTest"; - public CountDownLatch startSyncObj = new CountDownLatch(1); + // Avoid using CountDownLatch or similar objects that require unparking the + // main thread. Otherwise, if the main thread is run as a virtual thread, the + // async exception could be sent while the target is still executing FJP logic. + public volatile boolean started = false; - private boolean firstEntry = true; private boolean receivedThreadDeathinInternal1 = false; private boolean receivedThreadDeathinInternal2 = false; private volatile RuntimeException error = null; @@ -76,6 +78,7 @@ public void run() { } public void internalRun1() { + started = true; try { while (!receivedThreadDeathinInternal2) { internalRun2(); @@ -90,12 +93,6 @@ public void internalRun2() { Integer myLocalCount = 1; Integer myLocalCount2 = 1; - if (firstEntry) { - // Tell main thread we have started. - startSyncObj.countDown(); - firstEntry = false; - } - while(myLocalCount > 0) { myLocalCount2 = (myLocalCount % 3) / 2; myLocalCount -= 1; @@ -128,7 +125,9 @@ public static void main(String[] args) { thread.start(); try { // Wait for the worker thread to get going. - thread.startSyncObj.await(); + while (!thread.started) { + Thread.sleep(1); + } // Send async exception and wait until it is thrown JVMTIUtils.stopThread(thread); thread.join(); From 0f566c09d7eed6398ac2ebe4bf2b1a68705ee9bb Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Thu, 2 Oct 2025 16:31:21 -0400 Subject: [PATCH 2/4] David's comments --- .../Thread/AsyncExceptionOnMonitorEnter.java | 15 ++++++++++----- .../jtreg/runtime/Thread/AsyncExceptionTest.java | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java index bdaddaff80132..422978b70bcc5 100644 --- a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java +++ b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java @@ -76,14 +76,14 @@ public void testWithJavaMonitor() { public void testWithJVMTIRawMonitor() { - started = true; try { + started = true; int retCode = enterRawMonitor(); if (retCode != 0) { throw new RuntimeException("error in JVMTI RawMonitorEnter: retCode=" + retCode); } gotMonitor = true; - Thread.sleep(500); + Thread.sleep(1000); retCode = exitRawMonitor(); if (retCode != 0) { throw new RuntimeException("error in JVMTI RawMonitorExit: retCode=" + retCode); @@ -91,6 +91,8 @@ public void testWithJVMTIRawMonitor() { } catch (ThreadDeath td) { } catch (InterruptedException e) { throw new Error("Unexpected: " + e); + } finally { + exitRawMonitor(); } } @@ -144,12 +146,15 @@ public static void main(String[] args) { Thread.sleep(10); } + boolean first_iteration = true; while (true) { JVMTIUtils.stopThread(worker2); - if (TEST_MODE != 1) { - // Don't stop() worker1 with JVMTI raw monitors since if the monitor is - // not released worker2 will deadlock on enter + if (first_iteration || TEST_MODE != 1) { + // Don't stop() worker1 with JVMTI raw monitors more than once because + // the exception could be thrown at the finally clause before releasing + // the monitor, causing worker2 to deadlock on enter. JVMTIUtils.stopThread(worker1); + first_iteration = false; } // Give time to throw exception Thread.sleep(10); diff --git a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionTest.java b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionTest.java index 8bf1560064842..c6e78da48d3f3 100644 --- a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionTest.java +++ b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionTest.java @@ -78,8 +78,8 @@ public void run() { } public void internalRun1() { - started = true; try { + started = true; while (!receivedThreadDeathinInternal2) { internalRun2(); } From 7a010ed2ed69718d318baa93a77dd574ac87c028 Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Fri, 3 Oct 2025 10:56:43 -0400 Subject: [PATCH 3/4] Remove finally block --- .../Thread/AsyncExceptionOnMonitorEnter.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java index 422978b70bcc5..e3cfe64dca123 100644 --- a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java +++ b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java @@ -83,7 +83,7 @@ public void testWithJVMTIRawMonitor() { throw new RuntimeException("error in JVMTI RawMonitorEnter: retCode=" + retCode); } gotMonitor = true; - Thread.sleep(1000); + Thread.sleep(500); retCode = exitRawMonitor(); if (retCode != 0) { throw new RuntimeException("error in JVMTI RawMonitorExit: retCode=" + retCode); @@ -91,8 +91,6 @@ public void testWithJVMTIRawMonitor() { } catch (ThreadDeath td) { } catch (InterruptedException e) { throw new Error("Unexpected: " + e); - } finally { - exitRawMonitor(); } } @@ -146,15 +144,12 @@ public static void main(String[] args) { Thread.sleep(10); } - boolean first_iteration = true; while (true) { JVMTIUtils.stopThread(worker2); - if (first_iteration || TEST_MODE != 1) { - // Don't stop() worker1 with JVMTI raw monitors more than once because - // the exception could be thrown at the finally clause before releasing - // the monitor, causing worker2 to deadlock on enter. + if (TEST_MODE != 1) { + // Don't stop() worker1 with JVMTI raw monitors since if the monitor is + // not released worker2 will deadlock on enter JVMTIUtils.stopThread(worker1); - first_iteration = false; } // Give time to throw exception Thread.sleep(10); From 930906daa2a5b9e44cc4e001b44a4fd4fca844c3 Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Fri, 3 Oct 2025 10:57:43 -0400 Subject: [PATCH 4/4] Remove extra space --- .../jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java index e3cfe64dca123..409313b9626ec 100644 --- a/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java +++ b/test/hotspot/jtreg/runtime/Thread/AsyncExceptionOnMonitorEnter.java @@ -147,7 +147,7 @@ public static void main(String[] args) { while (true) { JVMTIUtils.stopThread(worker2); if (TEST_MODE != 1) { - // Don't stop() worker1 with JVMTI raw monitors since if the monitor is + // Don't stop() worker1 with JVMTI raw monitors since if the monitor is // not released worker2 will deadlock on enter JVMTIUtils.stopThread(worker1); }