diff --git a/runtime/jvmti/jvmtiHelpers.cpp b/runtime/jvmti/jvmtiHelpers.cpp index 3f0b28d1373..31265d77a66 100644 --- a/runtime/jvmti/jvmtiHelpers.cpp +++ b/runtime/jvmti/jvmtiHelpers.cpp @@ -115,6 +115,7 @@ getVMThread(J9VMThread *currentThread, jthread thread, J9VMThread **vmThreadPtr, BOOLEAN isThreadAlive = FALSE; #if JAVA_SPEC_VERSION >= 19 BOOLEAN isVirtualThread = FALSE; + J9InternalVMFunctions const * const vmfuncs = vm->internalVMFunctions; #endif /* JAVA_SPEC_VERSION >= 19 */ if (NULL == thread) { @@ -155,14 +156,20 @@ getVMThread(J9VMThread *currentThread, jthread thread, J9VMThread **vmThreadPtr, if (isVirtualThread) { jint vthreadState = 0; j9object_t carrierThread = NULL; - vm->internalVMFunctions->acquireVThreadInspector(currentThread, thread, TRUE); + + vmfuncs->acquireVThreadInspector(currentThread, thread, TRUE); + /* Re-fetch threadObject since acquireVThreadInspector can release and reacquire VM access. */ threadObject = J9_JNI_UNWRAP_REFERENCE(thread); - vthreadState = J9VMJAVALANGVIRTUALTHREAD_STATE(currentThread, threadObject); carrierThread = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CARRIERTHREAD(currentThread, threadObject); - if (NULL != carrierThread) { + + if ((NULL != carrierThread) + && vmfuncs->isVThreadFullyMounted(currentThread, threadObject) + ) { targetThread = J9VMJAVALANGTHREAD_THREADREF(currentThread, carrierThread); } + + vthreadState = J9VMJAVALANGVIRTUALTHREAD_STATE(currentThread, threadObject); isThreadAlive = (JVMTI_VTHREAD_STATE_NEW != vthreadState) && (JVMTI_VTHREAD_STATE_TERMINATED != vthreadState); } else #endif /* JAVA_SPEC_VERSION >= 19 */ @@ -175,7 +182,7 @@ getVMThread(J9VMThread *currentThread, jthread thread, J9VMThread **vmThreadPtr, if (OMR_ARE_ANY_BITS_SET(flags, J9JVMTI_GETVMTHREAD_ERROR_ON_DEAD_THREAD)) { #if JAVA_SPEC_VERSION >= 19 if (isVirtualThread) { - vm->internalVMFunctions->releaseVThreadInspector(currentThread, thread); + vmfuncs->releaseVThreadInspector(currentThread, thread); } #endif /* JAVA_SPEC_VERSION >= 19 */ omrthread_monitor_exit(vm->vmThreadListMutex); diff --git a/runtime/jvmti/jvmtiThread.c b/runtime/jvmti/jvmtiThread.c index 16c159d36ee..3bfac32f1ef 100644 --- a/runtime/jvmti/jvmtiThread.c +++ b/runtime/jvmti/jvmtiThread.c @@ -1139,10 +1139,9 @@ resumeThread(J9VMThread *currentThread, jthread thread) J9JavaVM *vm = currentThread->javaVM; j9object_t threadObject = J9_JNI_UNWRAP_REFERENCE(thread); /* The J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND will be cleared only - * if the thread is resumed and it has been mounted - * i.e. threadObject == targetThread->threadObject. + * if the thread is resumed and it has been mounted. */ - if ((NULL != targetThread) && (threadObject == targetThread->threadObject)) + if (NULL != targetThread) #endif /* JAVA_SPEC_VERSION >= 19 */ { if (OMR_ARE_ANY_BITS_SET(targetThread->publicFlags, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND)) { @@ -1341,12 +1340,17 @@ jvmtiSuspendResumeCallBack(J9VMThread *vmThread, J9MM_IterateObjectDescriptor *o J9JavaVM *vm = vmThread->javaVM; J9VMThread *targetThread = NULL; j9object_t carrierThread = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CARRIERTHREAD(vmThread, vthread); + BOOLEAN isFullyMounted = FALSE; if (NULL != carrierThread) { targetThread = J9VMJAVALANGTHREAD_THREADREF(vmThread, carrierThread); + if (NULL != targetThread) { + isFullyMounted = vm->internalVMFunctions->isVThreadFullyMounted(vmThread, vthread); + } Assert_JVMTI_notNull(targetThread); } if (data->is_suspend) { - if ((NULL != targetThread) && (vthread == targetThread->threadObject)) { + /* Suspend the virtual thread. */ + if ((NULL != targetThread) && isFullyMounted) { if (OMR_ARE_NO_BITS_SET(targetThread->publicFlags, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND | J9_PUBLIC_FLAGS_STOPPED)) { setHaltFlag(targetThread, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND); Trc_JVMTI_threadSuspended(targetThread); @@ -1355,7 +1359,7 @@ jvmtiSuspendResumeCallBack(J9VMThread *vmThread, J9MM_IterateObjectDescriptor *o J9OBJECT_U32_STORE(vmThread, vthread, vm->isSuspendedInternalOffset, 1); } else { /* Resume the virtual thread. */ - if ((NULL != targetThread) && (vthread == targetThread->threadObject)) { + if ((NULL != targetThread) && isFullyMounted) { if (OMR_ARE_ANY_BITS_SET(targetThread->publicFlags, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND)) { clearHaltFlag(targetThread, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND); Trc_JVMTI_threadResumed(targetThread); diff --git a/runtime/jvmti/suspendhelper.cpp b/runtime/jvmti/suspendhelper.cpp index 921035e0848..197ffe0aaed 100644 --- a/runtime/jvmti/suspendhelper.cpp +++ b/runtime/jvmti/suspendhelper.cpp @@ -62,7 +62,7 @@ suspendThread(J9VMThread *currentThread, jthread thread, BOOLEAN allowNull, BOOL /* The J9 PUBLIC FLAGS HALT THREAD JAVA SUSPEND flag will be set * if the thread is mounted. */ - if ((NULL != targetThread) && (threadObject == targetThread->threadObject)) + if (NULL != targetThread) #endif /* JAVA_SPEC_VERSION >= 19 */ { if (OMR_ARE_ANY_BITS_SET(targetThread->publicFlags, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND)) { diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 9cc165fd758..b281360b9f4 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -5059,6 +5059,7 @@ typedef struct J9InternalVMFunctions { UDATA (*walkAllStackFrames)(struct J9VMThread *currentThread, J9StackWalkState *walkState); BOOLEAN (*acquireVThreadInspector)(struct J9VMThread *currentThread, jobject thread, BOOLEAN spin); void (*releaseVThreadInspector)(struct J9VMThread *currentThread, jobject thread); + BOOLEAN (*isVThreadFullyMounted)(struct J9VMThread *currentThread, j9object_t vThread); #endif /* JAVA_SPEC_VERSION >= 19 */ UDATA (*checkArgsConsumed)(struct J9JavaVM * vm, struct J9PortLibrary* portLibrary, struct J9VMInitArgs* j9vm_args); #if defined(J9VM_ZOS_3164_INTEROPERABILITY) && (JAVA_SPEC_VERSION >= 17) diff --git a/runtime/oti/vm_api.h b/runtime/oti/vm_api.h index 189afae7142..ca90e8eee28 100644 --- a/runtime/oti/vm_api.h +++ b/runtime/oti/vm_api.h @@ -4518,6 +4518,17 @@ acquireVThreadInspector(J9VMThread *currentThread, jobject thread, BOOLEAN spin) */ void releaseVThreadInspector(J9VMThread *currentThread, jobject thread); + +/** + * @brief Check if the virtual thread is fully mounted. If a virtual thread + * is not provided, FALSE is returned. + * + * @param currentThread current thread + * @param vThread the virtual thread + * @return TRUE if it is mounted and FALSE if unmounted + */ +BOOLEAN +isVThreadFullyMounted(J9VMThread *currentThread, j9object_t vThread); #endif /* JAVA_SPEC_VERSION >= 19 */ /* ---------------- hookableAsync.c ---------------- */ diff --git a/runtime/vm/ContinuationHelpers.cpp b/runtime/vm/ContinuationHelpers.cpp index 2b5931b4fda..ef172038424 100644 --- a/runtime/vm/ContinuationHelpers.cpp +++ b/runtime/vm/ContinuationHelpers.cpp @@ -585,4 +585,17 @@ releaseVThreadInspector(J9VMThread *currentThread, jobject thread) vthreadInspectorCount = J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset); } } + +BOOLEAN +isVThreadFullyMounted(J9VMThread *currentThread, j9object_t vThread) +{ + if (IS_JAVA_LANG_VIRTUALTHREAD(currentThread, vThread)) { + /* Return FALSE if a virtual thread is not provided. */ + return FALSE; + } + + j9object_t continuationObj = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CONT(currentThread, vThread); + ContinuationState continuationState = J9VMJDKINTERNALVMCONTINUATION_STATE(currentThread, continuationObj); + return VM_ContinuationHelpers::isFullyMounted(continuationState); +} } /* extern "C" */ diff --git a/runtime/vm/intfunc.c b/runtime/vm/intfunc.c index 0575aa8b5d2..ce753b75987 100644 --- a/runtime/vm/intfunc.c +++ b/runtime/vm/intfunc.c @@ -443,6 +443,7 @@ J9InternalVMFunctions J9InternalFunctions = { walkAllStackFrames, acquireVThreadInspector, releaseVThreadInspector, + isVThreadFullyMounted, #endif /* JAVA_SPEC_VERSION >= 19 */ checkArgsConsumed, #if defined(J9VM_ZOS_3164_INTEROPERABILITY) && (JAVA_SPEC_VERSION >= 17)