JDK-8346792 : serviceability/jvmti/vthread/GetThreadState/GetThreadState.java testObjectWaitMillis failed
  • Type: Bug
  • Component: hotspot
  • Sub-Component: jvmti
  • Affected Version: 25
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2024-12-24
  • Updated: 2025-02-10
  • Resolved: 2025-02-05
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 25
25 b09Fixed
Related Reports
Relates :  
Description
GetThreadStateTest::testObjectWaitMillis (pinned=false) failed once 

STARTED    GetThreadStateTest::testObjectWaitMillis '[2] false'
  await state=0x1a1 (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_IN_OBJECT_WAIT) ...
  thread state=0x1a1 (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_IN_OBJECT_WAIT)
  expect state=0x401 (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) ...
  thread state=0x5 (JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE)
org.opentest4j.AssertionFailedError: expected: <1025> but was: <5>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
	at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
	at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:145)
	at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:531)
	at GetThreadStateTest.check(GetThreadStateTest.java:353)
	at GetThreadStateTest.testObjectWaitMillis(GetThreadStateTest.java:245)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:186)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:186)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:186)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1024)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:153)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:176)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:636)
	at java.base/java.util.stream.ReferencePipeline$7$1FlatMap.accept(ReferencePipeline.java:294)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1716)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:153)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:176)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:636)
	at java.base/java.util.stream.ReferencePipeline$7$1FlatMap.accept(ReferencePipeline.java:294)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1716)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:153)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:176)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:636)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
FAILED     GetThreadStateTest::testObjectWaitMillis '[2] false'
Comments
Changeset: b9b62a02 Branch: master Author: Serguei Spitsyn <sspitsyn@openjdk.org> Date: 2025-02-05 16:03:39 +0000 URL: https://git.openjdk.org/jdk/commit/b9b62a02488ee9c1a5a7a9ede87505781dfc0f73
05-02-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/23126 Date: 2025-01-15 08:15:40 +0000
15-01-2025

Thanks, Patricio! I was able to reproduce this issue. But a delay in this case needs to be much bigger than in JDK-8345543 case: diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 0f288baf0dd..3897f45b099 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -1685,6 +1685,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) { freeze_result result; ContinuationEntry* ce = current->last_continuation(); if (ce != nullptr && ce->is_virtual_thread()) { + os::naked_short_sleep(100); // DBG-TMP result = Continuation::try_preempt(current, ce->cont_oop(current)); if (result == freeze_ok) { VThreadWait(current, millis);
11-01-2025

The root of the issue is similar to JDK-8345543, but instead of JavaThreadBlockedOnMonitorEnterState, the interaction here is with its equivalent for Object.wait, JavaThreadInObjectWaitState. We change the Thread.State of the carrier to TIMED_WAITING before we try preempting the vthread. So we can see the Thread.State of the vthread as TIMED_WAITING before even calling try_preempt() (when the state is RUNNING we return the Thread.State of the carrier). Then when we check for JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, we caught the vthread in afterYield() still with the transitional state of TIMED_WAITING, which is mapped to Thread.State.RUNNABLE. The issue can also be easily reproduced by adding a delay between JavaThreadInObjectWaitState and try_preempt().
24-12-2024

Initial analysis from Alan: A virtual thread waits in timed-Object.wait. The main thread waits until the JVMTI GetThreadState returns the virtual thread state as: JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | JVMTI_THREAD_STATE_IN_OBJECT_WAIT So VirtualThread.state must be TIMED_WAIT. The main thread invoke Object.notifyAll. The only waiter is the virtual thread so this will change the VirtualThread.state to BLOCKED. The main thread calls JVMTI GetThreadState and expects the thread state to be JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER but somehow it is RUNNABLE. There's a bug or timing window somewhere, not immediately obvious (to me). testObjectWaitMillis enters the monitor twice, it's benign, probably a cut 'n paste issue. I added GetThreadStateTest as a comprehensive unit test for this function some time ago. AFAIK, it hasn't cause any issues. I can't see any history that it has failed previously.
24-12-2024