JDK-8360702 : runtime/Thread/AsyncExceptionTest.java timed out
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 25
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2025-06-26
  • Updated: 2025-12-09
  • Resolved: 2025-12-04
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 26
26 b27Fixed
Related Reports
Relates :  
Description
AsyncExceptionTest timed out on macosx-aarch64 with the following in JDK25 ATR run:

----------messages:(9/709)----------
command: main -Xcomp -XX:CompileCommand=dontinline,AsyncExceptionTest::internalRun2 -XX:CompileCommand=compileonly,AsyncExceptionTest::internalRun1 -XX:CompileCommand=compileonly,AsyncExceptionTest::internalRun2 AsyncExceptionTest
reason: User specified action: run main/othervm -Xcomp -XX:CompileCommand=dontinline,AsyncExceptionTest::internalRun2 -XX:CompileCommand=compileonly,AsyncExceptionTest::internalRun1 -XX:CompileCommand=compileonly,AsyncExceptionTest::internalRun2 AsyncExceptionTest 
started: Sun Jun 15 08:18:52 GMT 2025
Mode: othervm [/othervm specified]
Process id: 56164
Timeout information:
--- Timeout information end.
finished: Sun Jun 15 08:29:17 GMT 2025
elapsed time (seconds): 624.853
----------configuration:(0/0)----------
----------System.out:(5/319)----------
CompileCommand: dontinline AsyncExceptionTest.internalRun2 bool dontinline = true
CompileCommand: compileonly AsyncExceptionTest.internalRun1 bool compileonly = true
CompileCommand: compileonly AsyncExceptionTest.internalRun2 bool compileonly = true
About to execute for 30 seconds.
Timeout signalled after 480 seconds
----------System.err:(5/704)----------
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::loadLibrary has been called by jvmti.JVMTIUtils in an unnamed module (file:<TRUNCATED>/testoutput/test-support/jtreg_open_test_hotspot_jtreg_tier1_runtime/classes/0/runtime/Thread/AsyncExceptionTest.d/)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled


Comments
Changeset: 6f03c780 Branch: master Author: Patricio Chilano Mateo <pchilanomate@openjdk.org> Date: 2025-12-04 15:00:09 +0000 URL: https://git.openjdk.org/jdk/commit/6f03c7808de2b07b1e501d05b1bb7d5bfde5e393
04-12-2025

Slightly different failure mode but I'm going to assume this relates to the same issue: ---------System.out:(4/283)---------- CompileCommand: dontinline AsyncExceptionTest.internalRun2 bool dontinline = true CompileCommand: compileonly AsyncExceptionTest.internalRun1 bool compileonly = true CompileCommand: compileonly AsyncExceptionTest.internalRun2 bool compileonly = true About to execute for 30 seconds. ----------System.err:(18/1552)---------- WARNING: A restricted method in java.lang.System has been called WARNING: java.lang.System::loadLibrary has been called by jvmti.JVMTIUtils in an unnamed module (file:/opt/mach5/mesos/work_dir/slaves/da1065b5-7b94-4f0d-85e9-a3a252b9a32e-S11762/frameworks/1735e8a2-a1db-478c-8104-60c8b0af87dd-0196/executors/79a0ea77-9f7a-4280-af9d-8540b0606529/runs/05cc6cc7-07db-4b54-aaaa-bc3a2f87ac94/testoutput/test-support/jtreg_open_test_hotspot_jtreg_hotspot_runtime_no_cds/classes/3/runtime/Thread/AsyncExceptionTest.d/) WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module WARNING: Restricted methods will be blocked in a future release unless native access is enabled java.lang.RuntimeException: Thread did not exit. receivedThreadDeathinInternal1=false; receivedThreadDeathinInternal2=false at AsyncExceptionTest.main(AsyncExceptionTest.java:145) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:565) at com.sun.javatest.regtest.agent.MainWrapper$MainTask.run(MainWrapper.java:138) at java.base/java.lang.Thread.run(Thread.java:1516)
02-12-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/27582 Date: 2025-09-30 16:20:11 +0000
30-09-2025

These tests have always been problematic: 129: // Wait for the worker thread to get going. 130: thread.startSyncObj.await(); 131: // Send async exception and wait until it is thrown 132: JVMTIUtils.stopThread(thread); we need the target thread to have enough time to return from dountDown() so we don't hit the system Java code with the ThreadDeath. With virtual thread support the amount of code executed in the return path of countDown() is now quite significant so it is far easier to get the async exception in a bad place. > We can sync using a volatile boolean instead Yes - other tests using JVMTI stopThread use volatiles and spinning to ensure the target has reached the "safe" zone.
30-09-2025

Extra debugging shows that in the failing cases the ThreadDeath exception is always thrown somewhere during the call to ForkJoinPool$WorkQueue.push. Here are some of the caught stacktraces: at: at jdk.internal.misc.Unsafe.getAndAddInt(java.base@23-internal/Unsafe.java:2449) at java.util.concurrent.ForkJoinPool$WorkQueue.unlockPhase(java.base@23-internal/ForkJoinPool.java:1238) at java.util.concurrent.ForkJoinPool$WorkQueue.push(java.base@23-internal/ForkJoinPool.java:1297) at java.util.concurrent.ForkJoinPool.poolSubmit(java.base@23-internal/ForkJoinPool.java:2644) at java.util.concurrent.ForkJoinPool.execute(java.base@23-internal/ForkJoinPool.java:3152) at java.lang.VirtualThread.submitRunContinuation(java.base@23-internal/VirtualThread.java:264) at java.lang.VirtualThread.unpark(java.base@23-internal/VirtualThread.java:755) at java.lang.System$2.unparkVirtualThread(java.base@23-internal/System.java:2752) at jdk.internal.misc.VirtualThreads.unpark(java.base@23-internal/VirtualThreads.java:93) at java.util.concurrent.locks.LockSupport.unpark(java.base@23-internal/LockSupport.java:179) at java.util.concurrent.locks.AbstractQueuedSynchronizer.signalNext(java.base@23-internal/AbstractQueuedSynchronizer.java:645) at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(java.base@23-internal/AbstractQueuedSynchronizer.java:1147) at java.util.concurrent.CountDownLatch.countDown(java.base@23-internal/CountDownLatch.java:290) at AsyncExceptionTest.internalRun2(AsyncExceptionTest.java:95) at AsyncExceptionTest.internalRun1(AsyncExceptionTest.java:81) at AsyncExceptionTest.run(AsyncExceptionTest.java:62) at jdk.internal.misc.Unsafe.putReference(java.base@23-internal/Native Method) at java.util.concurrent.ForkJoinPool$WorkQueue.push(java.base@23-internal/ForkJoinPool.java:1290) at java.util.concurrent.ForkJoinPool.poolSubmit(java.base@23-internal/ForkJoinPool.java:2644) at java.util.concurrent.ForkJoinPool.execute(java.base@23-internal/ForkJoinPool.java:3152) at java.lang.VirtualThread.submitRunContinuation(java.base@23-internal/VirtualThread.java:264) at java.lang.VirtualThread.unpark(java.base@23-internal/VirtualThread.java:755) at java.lang.System$2.unparkVirtualThread(java.base@23-internal/System.java:2752) at jdk.internal.misc.VirtualThreads.unpark(java.base@23-internal/VirtualThreads.java:93) at java.util.concurrent.locks.LockSupport.unpark(java.base@23-internal/LockSupport.java:179) at java.util.concurrent.locks.AbstractQueuedSynchronizer.signalNext(java.base@23-internal/AbstractQueuedSynchronizer.java:645) at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(java.base@23-internal/AbstractQueuedSynchronizer.java:1147) at java.util.concurrent.CountDownLatch.countDown(java.base@23-internal/CountDownLatch.java:290) at AsyncExceptionTest.internalRun2(AsyncExceptionTest.java:95)(nmethod 0x0000ffff6c317388) at AsyncExceptionTest.internalRun1(AsyncExceptionTest.java:81)(nmethod 0x0000ffff6c316d88) at AsyncExceptionTest.run(AsyncExceptionTest.java:62) at jdk.internal.misc.Unsafe.compareAndSetInt(java.base@23-internal/Native Method) at jdk.internal.misc.Unsafe.weakCompareAndSetInt(java.base@23-internal/Unsafe.java:1521) at jdk.internal.misc.Unsafe.getAndAddInt(java.base@23-internal/Unsafe.java:2450) at java.util.concurrent.ForkJoinPool$WorkQueue.unlockPhase(java.base@23-internal/ForkJoinPool.java:1238) at java.util.concurrent.ForkJoinPool$WorkQueue.push(java.base@23-internal/ForkJoinPool.java:1297) at java.util.concurrent.ForkJoinPool.poolSubmit(java.base@23-internal/ForkJoinPool.java:2644) at java.util.concurrent.ForkJoinPool.execute(java.base@23-internal/ForkJoinPool.java:3152) at java.lang.VirtualThread.submitRunContinuation(java.base@23-internal/VirtualThread.java:264) at java.lang.VirtualThread.unpark(java.base@23-internal/VirtualThread.java:755) at java.lang.System$2.unparkVirtualThread(java.base@23-internal/System.java:2752) at jdk.internal.misc.VirtualThreads.unpark(java.base@23-internal/VirtualThreads.java:93) at java.util.concurrent.locks.LockSupport.unpark(java.base@23-internal/LockSupport.java:179) at java.util.concurrent.locks.AbstractQueuedSynchronizer.signalNext(java.base@23-internal/AbstractQueuedSynchronizer.java:645) at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(java.base@23-internal/AbstractQueuedSynchronizer.java:1147) at java.util.concurrent.CountDownLatch.countDown(java.base@23-internal/CountDownLatch.java:290) at AsyncExceptionTest.internalRun2(AsyncExceptionTest.java:95)(nmethod 0x0000ffff9c317388) at AsyncExceptionTest.internalRun1(AsyncExceptionTest.java:81)(nmethod 0x0000ffff9c316d88) at AsyncExceptionTest.run(AsyncExceptionTest.java:62) I’m guessing that because we don't complete the unlockPhase step we leave the FJP in an inconsistent state. Note: It’s not always the case that if we throw the ThreadDeath exception at those places the test will hang though. I see instances where the target throws the exception at those same places but the test passes. In any case we should fix the test to avoid terminating the target at these critical places. We can sync using a volatile boolean instead of the startSyncObj CountDownLatch. Test AsyncExceptionOnMonitorEnter.java should be revised too.
29-09-2025

I could reproduce this issue locally on AArch64 by running several instances of AsyncExceptionTest.java in parallel. Between the hangs I observe locally and the ones linked in mach5 we have a couple of different failure modes, but all point to an issue with ForkJoinPool. I could reproduce it as far back as JDK-8322732. Reverting the repo to that commit, reproduces the issue with a release build within a few minutes (several hours with fastdebug build). Before JDK-8322732, I can no longer reproduce it even after running the instances for more than a day. Note: To reproduce it, the changes to AsyncExceptionTest.java introduced in JDK-8356648 need to be applied. That change removes the Thread.sleep call, allowing many more loop iterations. Otherwise I cannot reproduce the hang. Also, I’m always running the second task from that test (not the one using -XX:+StressCompiledExceptionHandlers). If I revert my local repo to the state at 8322732’s commit, the failure mode I get is the worker thread being stuck in ForkJoinPool.submissionQueue trying to unpark the main virtual thread (also seen in mach5): "Thread-28585" #28620 daemon prio=5 tid=0x0000ffff2c054130 nid=2459204 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE JavaThread state: _thread_in_java - jdk.internal.misc.Unsafe.putInt(java.lang.Object, long, int) @bci=0 (Interpreted frame) - java.util.concurrent.ThreadLocalRandom.advanceProbe(int) @bci=30, line=266 (Interpreted frame) - java.util.concurrent.ForkJoinPool.submissionQueue(int) @bci=164, line=2621 (Interpreted frame) - java.util.concurrent.ForkJoinPool.poolSubmit(boolean, java.util.concurrent.ForkJoinTask) @bci=45, line=2642 (Interpreted frame) - java.util.concurrent.ForkJoinPool.execute(java.lang.Runnable) @bci=24, line=3152 (Interpreted frame) - java.lang.VirtualThread.submitRunContinuation() @bci=8, line=264 (Interpreted frame) - java.lang.VirtualThread.unpark() @bci=99, line=755 (Interpreted frame) - java.lang.System$2.unparkVirtualThread(java.lang.Thread) @bci=13, line=2752 (Interpreted frame) - jdk.internal.misc.VirtualThreads.unpark(java.lang.Thread) @bci=4, line=93 (Compiled frame) - java.util.concurrent.locks.LockSupport.unpark(java.lang.Thread) @bci=12, line=179 (Compiled frame) - java.util.concurrent.locks.AbstractQueuedSynchronizer.signalNext(java.util.concurrent.locks.AbstractQueuedSynchronizer$Node) @bci=30, line=645 (Compiled frame) - java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(int) @bci=12, line=1147 (Compiled frame) - java.util.concurrent.CountDownLatch.countDown() @bci=5, line=290 (Compiled frame) - AsyncExceptionTest.internalRun2() @bci=21, line=95 (Compiled frame) - AsyncExceptionTest.internalRun1() @bci=8, line=81 (Compiled frame) - AsyncExceptionTest.run() @bci=1, line=62 (Interpreted frame) Adding debug output shows that the thread ends up in an infinite loop in submissionQueue with q.tryLockPhase() always returning false. I can also reproduce the hang with more recent versions of the codebase, it just takes more time to fail (varies from tens of minutes to hours). The failure mode I get in this case is the main virtual being stuck in beginCompensatedBlock in the call to System.out.println: "ForkJoinPool-1-worker-1" #28 daemon prio=5 tid=0x0000ffff7c13ae80 nid=2719166 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE JavaThread state: _thread_in_java - java.util.concurrent.ForkJoinPool.tryCompensate(long) @bci=0, line=2188 (Interpreted frame) - java.util.concurrent.ForkJoinPool.beginCompensatedBlock() @bci=5, line=4346 (Interpreted frame) - java.util.concurrent.ForkJoinPool$1.beginCompensatedBlock(java.util.concurrent.ForkJoinPool) @bci=1, line=4412 (Interpreted frame) - jdk.internal.misc.CarrierThread$ForkJoinPools.beginCompensatedBlock(java.util.concurrent.ForkJoinPool) @bci=4, line=131 (Interpreted frame) - jdk.internal.misc.CarrierThread.beginBlocking() @bci=84, line=77 (Interpreted frame) - jdk.internal.misc.Blocker.begin() @bci=32, line=70 (Interpreted frame) - java.lang.System$Out.write(byte[], int, int) @bci=0, line=1908 (Interpreted frame) - java.io.BufferedOutputStream.flushBuffer() @bci=20, line=123 (Interpreted frame) - java.io.BufferedOutputStream.flush() @bci=1, line=203 (Interpreted frame) - locked <0x00000000adc64128> (a java.io.BufferedOutputStream) - java.io.PrintStream.write(byte[], int, int) @bci=30, line=541 (Interpreted frame) - locked <0x00000000adc64100> (a java.io.PrintStream) - sun.nio.cs.StreamEncoder.writeBytes() @bci=81, line=220 (Interpreted frame) - sun.nio.cs.StreamEncoder.implFlushBuffer() @bci=11, line=315 (Interpreted frame) - sun.nio.cs.StreamEncoder.flushBuffer() @bci=15, line=101 (Interpreted frame) - locked <0x00000000adc68558> (a java.io.OutputStreamWriter) - java.io.OutputStreamWriter.flushBuffer() @bci=4, line=179 (Interpreted frame) - java.io.PrintStream.writeln(java.lang.String) @bci=34, line=690 (Interpreted frame) - locked <0x00000000adc64100> (a java.io.PrintStream) - java.io.PrintStream.println(java.lang.String) @bci=14, line=1004 (Interpreted frame) - AsyncExceptionTest.main(java.lang.String[]) @bci=195, line=154 (Interpreted frame) Adding debug output shows that the thread ends up in an infinite loop in beginCompensatedBlock because tryCompensate always returns -1.
26-09-2025

Looks like this test has timed out before. LInked 8284303. And reassigning to Patricio.
02-07-2025

This is cool that the failure handler and jhsdb jstack work. Most threads are waiting for work, while this one is doing stuff. I think it's a simple timeout. "VirtualThread-unblocker" #25 daemon prio=5 tid=0x0000000120026a00 nid=26883 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE JavaThread state: _thread_in_java 0x00000001374f869c * jdk.internal.misc.Unsafe.putInt(java.lang.Object, long, int) bci:0 (Interpreted frame) 0x00000001374f48e0 * java.util.concurrent.ThreadLocalRandom.advanceProbe(int) bci:35 line:276 (Interpreted frame) 0x00000001374f4760 * java.util.concurrent.ForkJoinPool.submissionQueue(int, boolean) bci:203 line:2602 (Interpreted frame) 0x00000001374f4700 * java.util.concurrent.ForkJoinPool.poolSubmit(boolean, java.util.concurrent.ForkJoinTask) bci:51 line:2623 (Interpreted frame) 0x00000001374f4700 * java.util.concurrent.ForkJoinPool.execute(java.lang.Runnable) bci:27 line:3179 (Interpreted frame) 0x00000001374f4d60 * java.lang.VirtualThread.submitRunContinuation(java.util.concurrent.Executor, boolean) bci:50 line:340 (Interpreted frame) 0x00000001374f48e0 * java.lang.VirtualThread.submitRunContinuation() bci:6 line:382 (Interpreted frame) 0x00000001374f48e0 * java.lang.VirtualThread.unblock() bci:49 line:880 (Interpreted frame) 0x00000001374f48e0 * java.lang.VirtualThread.unblockVirtualThreads() bci:65 line:1516 (Interpreted frame) 0x00000001374f48e0 * java.lang.VirtualThread$$Lambda+0x00000e0001005878.run() bci:0 (Interpreted frame) 0x00000001374f4d60 * java.lang.Thread.runWith(java.lang.Object, java.lang.Runnable) bci:5 line:1487 (Interpreted frame) 0x00000001374f48e0 * java.lang.Thread.run() bci:19 line:1474 (Interpreted frame) 0x00000001374f48e0 * jdk.internal.misc.InnocuousThread.run() bci:20 line:148 (Interpreted frame) 0x00000001374f0154 <StubRoutines (initialstubs)> 0x0000000103f9b8c8 __ZN9JavaCalls11call_helperEP9JavaValueRK12methodHandleP17JavaCallArgumentsP10JavaThread + 0x3e0 0x0000000103f9a7e8 __ZN9JavaCalls12call_virtualEP9JavaValueP5KlassP6SymbolS5_P17JavaCallArgumentsP10JavaThread + 0x164 0x0000000103f9a8b4 __ZN9JavaCalls12call_virtualEP9JavaValue6HandleP5KlassP6SymbolS6_P10JavaThread + 0x64 0x0000000104075550 __ZL12thread_entryP10JavaThreadS0_ + 0x9c 0x0000000103fafb1c __ZN10JavaThread17thread_main_innerEv + 0xa8 0x00000001044c88a0 __ZN6Thread8call_runEv + 0xc8 0x000000010433e654 __ZL19thread_native_entryP6Thread + 0x118 0x0000000180752034 ????????
02-07-2025

There is nothing obvious going on. From the thread dumps it seems the only thread still active is the VirtualThread-unblocker. It is unclear whether this thread is not making progress, or whether things are just slower than expected for some reason. The test runs under Xcomp mode so that could be disturbing something as well. The test history doesn't show anything interesting either. There is one timeout in the Loom CI testing but the timeout in that case is over a vastly greater amount of time, and the thread dumps show a lot of active threads.
27-06-2025