Skip to content

Commit

Permalink
Fix virtual thread interrupts
Browse files Browse the repository at this point in the history
In JDK19+, multiple thread objects can be associated to a J9VMThread:
a virtual thread and its carrier thread.

If a virtual thread is mounted, then the carrier thread is unmounted
and vice-versa. In such cases, J9VMThread's state should not be used
to determine if a thread object is interrupted.

Instead, Thread.interrupted is used to determine if a thread object is
interrupted.

If a mounted thread is suspended, only set Thread.interrupted to TRUE
and do not wake/interrupt the thread. This behaviour is expected in
GetThreadStateMountedTest, which is a test added for virtual threads
in JDK22. These changes are only added for virtual threads in JDK19+.

Related: eclipse-openj9#18810

Signed-off-by: Babneet Singh <[email protected]>
  • Loading branch information
babsingh committed Feb 28, 2024
1 parent d8e47c2 commit 05d83e7
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 7 deletions.
37 changes: 33 additions & 4 deletions runtime/jvmti/jvmtiHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -739,19 +739,35 @@ getThreadStateHelper(J9VMThread *currentThread, j9object_t threadObject, J9VMThr
if (vmstate & J9VMTHREAD_STATE_SUSPENDED) {
state |= JVMTI_THREAD_STATE_SUSPENDED;
}

if (vmstate & J9VMTHREAD_STATE_INTERRUPTED) {
state |= JVMTI_THREAD_STATE_INTERRUPTED;
}

#if JAVA_SPEC_VERSION >= 19
/* Based on the isSuspendedInternal field, set the JVMTI
* thread state to suspended for the corresponding thread.
/* Based on the isSuspendedInternal field, set the JVMTI thread state to suspended for
* the corresponding thread.
*/
if (0 != J9OBJECT_U32_LOAD(currentThread, threadObject, currentThread->javaVM->isSuspendedInternalOffset)) {
state |= JVMTI_THREAD_STATE_SUSPENDED;
} else {
state &= ~JVMTI_THREAD_STATE_SUSPENDED;
}
#endif /* JAVA_SPEC_VERSION >= 19 */
if (vmstate & J9VMTHREAD_STATE_INTERRUPTED) {

/* Thread.deadInterrupt is Thread.interrupted in OJDK's Thread implementation. In JDK19+,
* OJDK's Thread implementation is used, and multiple thread objects can be associated to
* a J9VMThread: a virtual thread and its carrier thread. If a virtual thread is mounted,
* then the carrier thread is unmounted and vice-versa. In such cases, J9VMThread's state
* should not be used to determine if a thread object is interrupted. Instead,
* Thread.interrupted is used to determine if a thread object is interrupted.
*/
if (J9VMJAVALANGTHREAD_DEADINTERRUPT(currentThread, threadObject)) {
state |= JVMTI_THREAD_STATE_INTERRUPTED;
} else {
state &= ~JVMTI_THREAD_STATE_INTERRUPTED;
}
#endif /* JAVA_SPEC_VERSION >= 19 */

#if defined(J9VM_INTERP_ATOMIC_FREE_JNI)
if (vmThread->inNative) {
state |= JVMTI_THREAD_STATE_IN_NATIVE;
Expand Down Expand Up @@ -891,6 +907,19 @@ getVirtualThreadState(J9VMThread *currentThread, jthread thread)
} else {
rc &= ~JVMTI_THREAD_STATE_SUSPENDED;
}

/* Thread.deadInterrupt is Thread.interrupted in OJDK's Thread implementation. In JDK19+,
* OJDK's Thread implementation is used, and multiple thread objects can be associated to
* a J9VMThread: a virtual thread and its carrier thread. If a virtual thread is mounted,
* then the carrier thread is unmounted and vice-versa. In such cases, J9VMThread's state
* should not be used to determine if a thread object is interrupted. Instead,
* Thread.interrupted is used to determine if a thread object is interrupted.
*/
if (J9VMJAVALANGTHREAD_DEADINTERRUPT(currentThread, vThreadObject)) {
rc |= JVMTI_THREAD_STATE_INTERRUPTED;
} else {
rc &= ~JVMTI_THREAD_STATE_INTERRUPTED;
}
releaseVMThread(currentThread, targetThread, thread);
} else {
/* This is unreachable. */
Expand Down
40 changes: 37 additions & 3 deletions runtime/oti/VMHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1410,8 +1410,24 @@ class VM_VMHelpers
{
J9VMThread *targetThread = J9VMJAVALANGTHREAD_THREADREF(currentThread, threadObject);
bool result = false;
#if JAVA_SPEC_VERSION >= 19
/* Check if the mounted thread is suspended. */
U_32 isSuspended = 0;
if (NULL != targetThread) {
isSuspended = J9OBJECT_U32_LOAD(currentThread, targetThread->threadObject, currentThread->javaVM->isSuspendedInternalOffset);
}
#endif /* JAVA_SPEC_VERSION >= 19 */
/* If the thread is alive, ask the OS thread. Otherwise, answer false. */
if (J9VMJAVALANGTHREAD_STARTED(currentThread, threadObject) && (NULL != targetThread)) {
if ((NULL != targetThread)
&& J9VMJAVALANGTHREAD_STARTED(currentThread, threadObject)
#if JAVA_SPEC_VERSION >= 19
/* Thread.deadInterrupt is Thread.interrupted in OJDK's Thread implementation.
* In JDK19+, OJDK's Thread implementation is used. If the mounted thread is
* suspended, use Thread.interrupted to derive if the thread is interrupted.
*/
&& (0 == isSuspended)
#endif /* JAVA_SPEC_VERSION >= 19 */
) {
if (omrthread_interrupted(targetThread->osThread)) {
result = true;
}
Expand Down Expand Up @@ -2043,8 +2059,26 @@ class VM_VMHelpers
threadInterruptImpl(J9VMThread *currentThread, j9object_t targetObject)
{
J9VMThread *targetThread = J9VMJAVALANGTHREAD_THREADREF(currentThread, targetObject);
if (J9VMJAVALANGTHREAD_STARTED(currentThread, targetObject) && (NULL != targetThread)) {
void (*sidecarInterruptFunction)(J9VMThread*) = currentThread->javaVM->sidecarInterruptFunction;
J9JavaVM *vm = currentThread->javaVM;
#if JAVA_SPEC_VERSION >= 19
/* Check if the mounted thread is suspended. */
U_32 isSuspended = 0;
if (NULL != targetThread) {
isSuspended = J9OBJECT_U32_LOAD(currentThread, targetThread->threadObject, vm->isSuspendedInternalOffset);
}
#endif /* JAVA_SPEC_VERSION >= 19 */
if ((NULL != targetThread)
&& J9VMJAVALANGTHREAD_STARTED(currentThread, targetObject)
#if JAVA_SPEC_VERSION >= 19
/* Thread.deadInterrupt is Thread.interrupted in OJDK's Thread implementation.
* In JDK19+, OJDK's Thread implementation is used. If the mounted thread is
* suspended, only set Thread.interrupted to TRUE and do not wake/interrupt
* the thread.
*/
&& (0 == isSuspended)
#endif /* JAVA_SPEC_VERSION >= 19 */
) {
void (*sidecarInterruptFunction)(J9VMThread*) = vm->sidecarInterruptFunction;
if (NULL != sidecarInterruptFunction) {
sidecarInterruptFunction(targetThread);
}
Expand Down

0 comments on commit 05d83e7

Please sign in to comment.