diff --git a/jcl/src/java.base/share/classes/jdk/internal/vm/Continuation.java b/jcl/src/java.base/share/classes/jdk/internal/vm/Continuation.java
index 1f9d4d28004..8d962e64cf0 100644
--- a/jcl/src/java.base/share/classes/jdk/internal/vm/Continuation.java
+++ b/jcl/src/java.base/share/classes/jdk/internal/vm/Continuation.java
@@ -34,6 +34,7 @@
*/
public class Continuation {
private long vmRef; /* J9VMContinuation */
+ protected Thread vthread; /* Parent VirtualThread */
private final ContinuationScope scope;
private final Runnable runnable;
diff --git a/runtime/compiler/control/HookedByTheJit.cpp b/runtime/compiler/control/HookedByTheJit.cpp
index c3f7b95d5e5..3c3232513a0 100644
--- a/runtime/compiler/control/HookedByTheJit.cpp
+++ b/runtime/compiler/control/HookedByTheJit.cpp
@@ -34,6 +34,7 @@
#include "mmhook.h"
#include "mmomrhook.h"
#include "vmaccess.h"
+#include "HeapIteratorAPI.h"
#include "codegen/CodeGenerator.hpp"
#include "compile/CompilationTypes.hpp"
#include "compile/Method.hpp"
@@ -6596,8 +6597,46 @@ static UDATA jitReleaseCodeStackWalkFrame(J9VMThread *vmThread, J9StackWalkState
return J9_STACKWALK_KEEP_ITERATING;
}
-static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFunctionPtr condYield = NULL)
+static jvmtiIterationControl jitWalkContinuationCallBack(J9VMThread *vmThread, J9MM_IterateObjectDescriptor *object, void *userData)
+ {
+ J9InternalVMFunctions *vmFuncs = vmThread->javaVM->internalVMFunctions;
+ J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, object->object);
+ if (NULL != continuation)
+ {
+ bool yieldHappened = false;
+ if ((continuation->dropFlags & 0x1) ? false : true)
+ {
+ J9StackWalkState walkState;
+ walkState.flags = J9_STACKWALK_ITERATE_HIDDEN_JIT_FRAMES | J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_SKIP_INLINES;
+ walkState.skipCount = 0;
+ walkState.frameWalkFunction = jitReleaseCodeStackWalkFrame;
+ vmFuncs->walkContinuationStackFrames(vmThread, continuation, &walkState);
+ continuation->dropFlags = 0x1;
+ condYieldFromGCFunctionPtr condYield = (condYieldFromGCFunctionPtr)userData;
+ yieldHappened = condYield(vmThread->omrVMThread, J9_GC_METRONOME_UTILIZATION_COMPONENT_JIT);
+ }
+
+ if (yieldHappened)
+ {
+ /* Stop the iteration, will resume later. */
+ return JVMTI_ITERATION_ABORT;
+ }
+ }
+ return JVMTI_ITERATION_CONTINUE;
+ }
+static jvmtiIterationControl jitResetContinuationFlag(J9VMThread *vmThread, J9MM_IterateObjectDescriptor *object, void *userData)
+ {
+ J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, object->object);
+ if (NULL != continuation)
+ {
+ continuation->dropFlags = 0;
+ }
+
+ return JVMTI_ITERATION_CONTINUE;
+ }
+
+static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFunctionPtr condYield = NULL)
{
J9VMThread *vmThread = (J9VMThread *)omrVMThread->_language_vmthread;
J9JITConfig *jitConfig = vmThread->javaVM->jitConfig;
@@ -6612,6 +6651,7 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu
#if JAVA_SPEC_VERSION >= 19
J9JavaVM *vm = vmThread->javaVM;
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ PORT_ACCESS_FROM_VMC(vmThread);
if (isRealTimeGC && !TR::Options::getCmdLineOptions()->getOption(TR_DisableIncrementalCCR))
{
bool yieldHappened = false;
@@ -6652,34 +6692,13 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu
}
while (yieldHappened);
- if (NULL != vm->liveVirtualThreadList)
+ do
{
- do
- {
- yieldHappened = false;
- /* Skip the root, which is a dummy virtual thread and global ref. */
- j9object_t walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, *(vm->liveVirtualThreadList), vm->virtualThreadLinkNextOffset);
- while ((*(vm->liveVirtualThreadList) != walkVirtualThread) && !yieldHappened)
- {
- j9object_t contObject = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CONT(vmThread, walkVirtualThread);
- J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, contObject);
- if ((continuation->dropFlags & 0x1) ? false : true)
- {
- J9StackWalkState walkState;
- walkState.flags = J9_STACKWALK_ITERATE_HIDDEN_JIT_FRAMES | J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_SKIP_INLINES;
- walkState.skipCount = 0;
- walkState.frameWalkFunction = jitReleaseCodeStackWalkFrame;
- vmFuncs->walkContinuationStackFrames(vmThread, continuation, &walkState);
- continuation->dropFlags |= 0x1;
- yieldHappened = condYield(omrVMThread, J9_GC_METRONOME_UTILIZATION_COMPONENT_JIT);
- }
-
- if (!yieldHappened)
- walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, walkVirtualThread, vm->virtualThreadLinkNextOffset);
- }
- }
- while (yieldHappened);
+ jvmtiIterationControl rc = vm->memoryManagerFunctions->j9mm_iterate_all_continuation_objects(vmThread, PORTLIB, 0, jitWalkContinuationCallBack, (void*)condYield);
+ if (JVMTI_ITERATION_ABORT == rc)
+ yieldHappened = true;
}
+ while (yieldHappened);
} else {
J9StackWalkState walkState;
walkState.flags = J9_STACKWALK_ITERATE_HIDDEN_JIT_FRAMES | J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_SKIP_INLINES;
@@ -6802,31 +6821,14 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu
do
{
thr->dropFlags &=0x0;
-#if JAVA_SPEC_VERSION >= 19
- if (NULL != thr->currentContinuation) {
- thr->currentContinuation->dropFlags &=0x0;
- }
-#endif /* JAVA_SPEC_VERSION >= 19 */
thr = thr->linkNext;
}
while (thr != vmThread);
#if JAVA_SPEC_VERSION >= 19
- if (NULL != vm->liveVirtualThreadList)
- {
- /* Skip the root, which is a dummy virtual thread and global ref. */
- j9object_t walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, *(vm->liveVirtualThreadList), vm->virtualThreadLinkNextOffset);
- while (*(vm->liveVirtualThreadList) != walkVirtualThread)
- {
- j9object_t contObject = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CONT(vmThread, walkVirtualThread);
- J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, contObject);
- continuation->dropFlags &= 0x0;
- walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, walkVirtualThread, vm->virtualThreadLinkNextOffset);
- }
- }
+ vm->memoryManagerFunctions->j9mm_iterate_all_continuation_objects(vmThread, PORTLIB, 0, jitResetContinuationFlag, NULL);
#endif /* JAVA_SPEC_VERSION >= 19 */
}
-
}
static void jitHookReleaseCodeGlobalGCEnd(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData)
diff --git a/runtime/j9vm/javanextvmi.cpp b/runtime/j9vm/javanextvmi.cpp
index ac4bb099d92..229e6279e48 100644
--- a/runtime/j9vm/javanextvmi.cpp
+++ b/runtime/j9vm/javanextvmi.cpp
@@ -247,6 +247,31 @@ JVM_IsPreviewEnabled(void)
return isPreviewEnabled;
}
+static void
+enterVThreadTransitionCritical(J9VMThread *currentThread, jobject thread)
+{
+ J9JavaVM *vm = currentThread->javaVM;
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ MM_ObjectAccessBarrierAPI objectAccessBarrier = MM_ObjectAccessBarrierAPI(currentThread);
+ j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+
+ while(!objectAccessBarrier.inlineMixedObjectCompareAndSwapU64(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, 0, (U_64)-1)) {
+ /* Thread is being inspected or unmounted, wait. */
+ vmFuncs->internalExitVMToJNI(currentThread);
+ VM_AtomicSupport::yieldCPU();
+ /* After wait, the thread may suspend here. */
+ vmFuncs->internalEnterVMFromJNI(currentThread);
+ threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+ }
+}
+
+static void
+exitVThreadTransitionCritical(J9VMThread *currentThread, j9object_t vthread)
+{
+ Assert_SC_true(-1 == J9OBJECT_I64_LOAD(currentThread, vthread, currentThread->javaVM->virtualThreadInspectorCountOffset));
+ J9OBJECT_I64_STORE(currentThread, vthread, currentThread->javaVM->virtualThreadInspectorCountOffset, 0);
+}
+
JNIEXPORT void JNICALL
JVM_VirtualThreadMountBegin(JNIEnv *env, jobject thread, jboolean firstMount)
{
@@ -257,13 +282,12 @@ JVM_VirtualThreadMountBegin(JNIEnv *env, jobject thread, jboolean firstMount)
Trc_SC_VirtualThreadMountBegin_Entry(currentThread, thread, firstMount);
vmFuncs->internalEnterVMFromJNI(currentThread);
- f_monitorEnter(vm->liveVirtualThreadListMutex);
j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
-
Assert_SC_true(IS_JAVA_LANG_VIRTUALTHREAD(currentThread, threadObj));
if (TrcEnabled_Trc_SC_VirtualThread_Info) {
j9object_t continuationObj = J9VMJAVALANGVIRTUALTHREAD_CONT(currentThread, threadObj);
+ J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, continuationObj);
Trc_SC_VirtualThread_Info(
currentThread,
threadObj,
@@ -271,30 +295,11 @@ JVM_VirtualThreadMountBegin(JNIEnv *env, jobject thread, jboolean firstMount)
J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset),
J9VMJAVALANGVIRTUALTHREAD_CARRIERTHREAD(currentThread, threadObj),
continuationObj,
- J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, continuationObj));
- }
-
- while (vm->inspectingLiveVirtualThreadList
- || (0 != J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset))
- ) {
- /* Thread is being inspected or unmounted, wait. */
- vmFuncs->internalExitVMToJNI(currentThread);
- f_monitorWait(vm->liveVirtualThreadListMutex);
- /* Release the monitor before suspending. */
- f_monitorExit(vm->liveVirtualThreadListMutex);
- /* After wait, the thread may suspend here. */
- vmFuncs->internalEnterVMFromJNI(currentThread);
- /* Acquire the monitor after resuming from suspend. */
- f_monitorEnter(vm->liveVirtualThreadListMutex);
- threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+ continuation);
}
- /* Prevent inspectors from inspecting this thread during stack swap and mount by locking from notifyJvmtiMountBegin
- * to notifyJvmtiMountEnd. See getVMThread() in jvmtiHelpers.c.
- */
- J9OBJECT_I64_STORE(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, -1);
+ enterVThreadTransitionCritical(currentThread, thread);
- f_monitorExit(vm->liveVirtualThreadListMutex);
vmFuncs->internalExitVMToJNI(currentThread);
Trc_SC_VirtualThreadMountBegin_Exit(currentThread, thread, firstMount);
@@ -329,71 +334,13 @@ JVM_VirtualThreadMountEnd(JNIEnv *env, jobject thread, jboolean firstMount)
J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, continuationObj));
}
- /* On some occassions, J9AllocateObject acquires exclusive VM access. It is invoked without acquiring
- * liveVirtualThreadListMutex to prevent a deadlock.
- */
- if (NULL == vm->liveVirtualThreadList) {
- Assert_SC_true(firstMount);
- J9Class *virtualThreadClass = J9OBJECT_CLAZZ(currentThread, J9_JNI_UNWRAP_REFERENCE(thread));
- J9MemoryManagerFunctions *mmFuncs = vm->memoryManagerFunctions;
-
- /* Allocate empty virtual thread and create a global reference to it as root for the linked list.
- * This prevents the root reference from becoming stale if the GC moves the object.
- */
- rootVirtualThread = mmFuncs->J9AllocateObject(currentThread, virtualThreadClass, J9_GC_ALLOCATE_OBJECT_NON_INSTRUMENTABLE);
- if (NULL == rootVirtualThread) {
- vmFuncs->setHeapOutOfMemoryError(currentThread);
- goto release1;
- }
-
- /* Re-fetch as the memory allocation above may have moved the object. */
- threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
-
- J9VMJAVALANGVIRTUALTHREAD_SET_STATE(currentThread, rootVirtualThread, J9VM_VIRTUALTHREAD_ROOT_NODE_STATE);
- }
-
- f_monitorEnter(vm->liveVirtualThreadListMutex);
+ /* Allow thread to be inspected again. */
+ exitVThreadTransitionCritical(currentThread, threadObj);
if (firstMount) {
- if (NULL == vm->liveVirtualThreadList) {
- Assert_SC_true(NULL != rootVirtualThread);
-
- jobject globalRef = vmFuncs->j9jni_createGlobalRef((JNIEnv *)currentThread, rootVirtualThread, JNI_FALSE);
- if (NULL == globalRef) {
- vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0);
- goto release2;
- }
-
- Trc_SC_VirtualThread_RootNodeSet(currentThread, globalRef);
- vm->liveVirtualThreadList = (j9object_t *)globalRef;
- /* Set linkNext/linkPrevious to itself. */
- J9OBJECT_OBJECT_STORE(currentThread, rootVirtualThread, vm->virtualThreadLinkNextOffset, rootVirtualThread);
- J9OBJECT_OBJECT_STORE(currentThread, rootVirtualThread, vm->virtualThreadLinkPreviousOffset, rootVirtualThread);
- }
-
- {
- j9object_t threadPrev = J9OBJECT_OBJECT_LOAD(currentThread, threadObj, vm->virtualThreadLinkPreviousOffset);
- j9object_t threadNext = J9OBJECT_OBJECT_LOAD(currentThread, threadObj, vm->virtualThreadLinkNextOffset);
-
- /* Non-null previous and next elements in the list suggest that the thread has
- * already been added to the list. Only add to the list if the previous and
- * next elements in the list are null.
- */
- Assert_SC_true((NULL == threadPrev) && (NULL == threadNext));
- j9object_t root = *(vm->liveVirtualThreadList);
- j9object_t rootPrev = J9OBJECT_OBJECT_LOAD(currentThread, root, vm->virtualThreadLinkPreviousOffset);
-
- /* Add thread to the end of the list. */
- J9OBJECT_OBJECT_STORE(currentThread, threadObj, vm->virtualThreadLinkNextOffset, root);
- J9OBJECT_OBJECT_STORE(currentThread, threadObj, vm->virtualThreadLinkPreviousOffset, rootPrev);
- J9OBJECT_OBJECT_STORE(currentThread, rootPrev, vm->virtualThreadLinkNextOffset, threadObj);
- J9OBJECT_OBJECT_STORE(currentThread, root, vm->virtualThreadLinkPreviousOffset, threadObj);
- }
+ TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_STARTED(vm->hookInterface, currentThread);
}
-
- /* Allow thread to be inspected again. */
- Assert_SC_true(-1 == J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset));
- J9OBJECT_I64_STORE(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, 0);
+ TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_MOUNT(vm->hookInterface, currentThread);
/* If isSuspendedByJVMTI is non-zero, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND is set
* in currentThread->publicFlags while resetting isSuspendedByJVMTI to zero. During
@@ -404,19 +351,6 @@ JVM_VirtualThreadMountEnd(JNIEnv *env, jobject thread, jboolean firstMount)
vmFuncs->setHaltFlag(currentThread, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND);
}
- f_monitorNotifyAll(vm->liveVirtualThreadListMutex);
-
- /* J9Hooks can be run since no errors were encountered. */
- runJ9Hooks = TRUE;
-release2:
- f_monitorExit(vm->liveVirtualThreadListMutex);
-release1:
- if (runJ9Hooks) {
- if (firstMount) {
- TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_STARTED(vm->hookInterface, currentThread);
- }
- TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_MOUNT(vm->hookInterface, currentThread);
- }
vmFuncs->internalExitVMToJNI(currentThread);
Trc_SC_VirtualThreadMountEnd_Exit(currentThread, thread, firstMount);
@@ -432,7 +366,6 @@ JVM_VirtualThreadUnmountBegin(JNIEnv *env, jobject thread, jboolean lastUnmount)
Trc_SC_VirtualThreadUnmountBegin_Entry(currentThread, thread, lastUnmount);
vmFuncs->internalEnterVMFromJNI(currentThread);
- f_monitorEnter(vm->liveVirtualThreadListMutex);
j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
Assert_SC_true(IS_JAVA_LANG_VIRTUALTHREAD(currentThread, threadObj));
@@ -449,51 +382,18 @@ JVM_VirtualThreadUnmountBegin(JNIEnv *env, jobject thread, jboolean lastUnmount)
J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, continuationObj));
}
- while (vm->inspectingLiveVirtualThreadList
- || (0 != J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset))
- ) {
- /* Thread is being inspected, wait. */
- vmFuncs->internalExitVMToJNI(currentThread);
- f_monitorWait(vm->liveVirtualThreadListMutex);
- /* Release the monitor before suspending. */
- f_monitorExit(vm->liveVirtualThreadListMutex);
- /* After wait, the thread may suspend here. */
- vmFuncs->internalEnterVMFromJNI(currentThread);
- /* Acquire the monitor after resuming from suspend. */
- f_monitorEnter(vm->liveVirtualThreadListMutex);
- threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
- }
-
- /* Prevent inspectors from inspecting this thread during stack swap and unmount by locking from notifyJvmtiUnmountBegin
- * to notifyJvmtiUnmountEnd. See getVMThread() in jvmtiHelpers.c.
- */
- J9OBJECT_I64_STORE(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, -1);
-
+ TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_UNMOUNT(vm->hookInterface, currentThread);
if (lastUnmount) {
- if (NULL != vm->liveVirtualThreadList) {
- j9object_t threadPrev = J9OBJECT_OBJECT_LOAD(currentThread, threadObj, vm->virtualThreadLinkPreviousOffset);
- j9object_t threadNext = J9OBJECT_OBJECT_LOAD(currentThread, threadObj, vm->virtualThreadLinkNextOffset);
-
- /* Non-null previous and next elements in the list suggest that the thread has
- * been added to the list. Only remove from the list if the previous and next
- * elements in the list are non-null.
- */
- Assert_SC_true((NULL != threadPrev) && (NULL != threadNext));
- /* Remove thread from the list. The root will never be removed. */
- J9OBJECT_OBJECT_STORE(currentThread, threadPrev, vm->virtualThreadLinkNextOffset, threadNext);
- J9OBJECT_OBJECT_STORE(currentThread, threadNext, vm->virtualThreadLinkPreviousOffset, threadPrev);
-
- /* Reset previous and next fields in the thread object to null. */
- J9OBJECT_OBJECT_STORE(currentThread, threadObj, vm->virtualThreadLinkNextOffset, NULL);
- J9OBJECT_OBJECT_STORE(currentThread, threadObj, vm->virtualThreadLinkPreviousOffset, NULL);
- }
+ TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_END(vm->hookInterface, currentThread);
}
- f_monitorExit(vm->liveVirtualThreadListMutex);
-
- TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_UNMOUNT(vm->hookInterface, currentThread);
+ enterVThreadTransitionCritical(currentThread, thread);
if (lastUnmount) {
- TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_END(vm->hookInterface, currentThread);
+ /* Re-fetch reference as enterVThreadTransitionCritical may release VMAccess. */
+ threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+ j9object_t continuationObj = J9VMJAVALANGVIRTUALTHREAD_CONT(currentThread, threadObj);
+ /* Add reverse link from Continuation object to VirtualThread object, this let JVMTI code */
+ J9VMJDKINTERNALVMCONTINUATION_SET_VTHREAD(currentThread, continuationObj, NULL);
}
vmFuncs->internalExitVMToJNI(currentThread);
@@ -510,7 +410,6 @@ JVM_VirtualThreadUnmountEnd(JNIEnv *env, jobject thread, jboolean lastUnmount)
Trc_SC_VirtualThreadUnmountEnd_Entry(currentThread, thread, lastUnmount);
vmFuncs->internalEnterVMFromJNI(currentThread);
- f_monitorEnter(vm->liveVirtualThreadListMutex);
j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
Assert_SC_true(IS_JAVA_LANG_VIRTUALTHREAD(currentThread, threadObj));
@@ -535,11 +434,8 @@ JVM_VirtualThreadUnmountEnd(JNIEnv *env, jobject thread, jboolean lastUnmount)
}
/* Allow thread to be inspected again. */
- Assert_SC_true(-1 == J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset));
- J9OBJECT_I64_STORE(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, 0);
- f_monitorNotifyAll(vm->liveVirtualThreadListMutex);
+ exitVThreadTransitionCritical(currentThread, threadObj);
- f_monitorExit(vm->liveVirtualThreadListMutex);
vmFuncs->internalExitVMToJNI(currentThread);
Trc_SC_VirtualThreadUnmountEnd_Exit(currentThread, thread, lastUnmount);
diff --git a/runtime/jcl/common/jclcinit.c b/runtime/jcl/common/jclcinit.c
index 8e544f93cfc..4bd537a2684 100644
--- a/runtime/jcl/common/jclcinit.c
+++ b/runtime/jcl/common/jclcinit.c
@@ -650,16 +650,6 @@ initializeRequiredClasses(J9VMThread *vmThread, char* dllName)
return 1;
}
- /* Points to the next VirtualThread in the liveVirtualThreadList. */
- if (0 != vmFuncs->addHiddenInstanceField(vm, "java/lang/VirtualThread", "linkNext", "Ljava/lang/VirtualThread;", &vm->virtualThreadLinkNextOffset)) {
- return 1;
- }
-
- /* Points to the previous VirtualThread in the liveVirtualThreadList. */
- if (0 != vmFuncs->addHiddenInstanceField(vm, "java/lang/VirtualThread", "linkPrevious", "Ljava/lang/VirtualThread;", &vm->virtualThreadLinkPreviousOffset)) {
- return 1;
- }
-
/* Counter to track if the virtual thread is being inspected by JVMTI. */
if (0 != vmFuncs->addHiddenInstanceField(vm, "java/lang/VirtualThread", "inspectorCount", "J", &vm->virtualThreadInspectorCountOffset)) {
return 1;
diff --git a/runtime/jcl/common/thread.cpp b/runtime/jcl/common/thread.cpp
index 39074fde159..5dd617e2005 100644
--- a/runtime/jcl/common/thread.cpp
+++ b/runtime/jcl/common/thread.cpp
@@ -339,17 +339,18 @@ Java_java_lang_Thread_getStackTraceImpl(JNIEnv *env, jobject rcv)
#if JAVA_SPEC_VERSION >= 19
BOOLEAN releaseInspector = FALSE;
if (IS_JAVA_LANG_VIRTUALTHREAD(currentThread, receiverObject)) {
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
+ /* Do not spin when acquiring access, if acquire failed, return NULL.
+ * The caller of getStackTraceImpl will handle if should retry or get stack using unmounted path.
+ */
+ if (!vmFuncs->acquireVThreadInspector(currentThread, rcv, FALSE)) {
+ goto done;
+ }
j9object_t carrierThread = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CARRIERTHREAD(currentThread, receiverObject);
- I_64 vthreadInspectorCount = J9OBJECT_I64_LOAD(currentThread, receiverObject, vm->virtualThreadInspectorCountOffset);
-
/* Ensure virtual thread is mounted and not during transition. */
- if ((NULL != carrierThread) && (vthreadInspectorCount >= 0)) {
- J9OBJECT_I64_STORE(currentThread, receiverObject, vm->virtualThreadInspectorCountOffset, vthreadInspectorCount + 1);
+ if (NULL != carrierThread) {
releaseInspector = TRUE;
- }
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
- if (!releaseInspector) {
+ } else {
+ vmFuncs->releaseVThreadInspector(currentThread, rcv);
goto done;
}
/* Gets targetThread from the carrierThread object. */
@@ -370,16 +371,7 @@ Java_java_lang_Thread_getStackTraceImpl(JNIEnv *env, jobject rcv)
if (releaseInspector) {
receiverObject = J9_JNI_UNWRAP_REFERENCE(rcv);
/* Release the virtual thread (allow it to die) now that we are no longer inspecting it. */
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
- I_64 vthreadInspectorCount = J9OBJECT_I64_LOAD(currentThread, receiverObject, vm->virtualThreadInspectorCountOffset);
- Assert_JCL_true(vthreadInspectorCount > 0);
- vthreadInspectorCount -= 1;
- J9OBJECT_I64_STORE(currentThread, receiverObject, vm->virtualThreadInspectorCountOffset, vthreadInspectorCount);
-
- if (!vm->inspectingLiveVirtualThreadList && (0 == vthreadInspectorCount)) {
- omrthread_monitor_notify_all(vm->liveVirtualThreadListMutex);
- }
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ vmFuncs->releaseVThreadInspector(currentThread, rcv);
}
done:
#endif /* JAVA_SPEC_VERSION >= 19 */
diff --git a/runtime/jvmti/jvmtiHelpers.c b/runtime/jvmti/jvmtiHelpers.c
index be24440e853..983a1b6dfa5 100644
--- a/runtime/jvmti/jvmtiHelpers.c
+++ b/runtime/jvmti/jvmtiHelpers.c
@@ -143,15 +143,7 @@ getVMThread(J9VMThread *currentThread, jthread thread, J9VMThread **vmThreadPtr,
#if JAVA_SPEC_VERSION >= 19
isVirtualThread = IS_JAVA_LANG_VIRTUALTHREAD(currentThread, threadObject);
if (isVirtualThread) {
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
-
- while (J9OBJECT_I64_LOAD(currentThread, threadObject, vm->virtualThreadInspectorCountOffset) < 0) {
- /* Thread is currently in the process of mounting/unmounting, wait. */
- vm->internalVMFunctions->internalExitVMToJNI(currentThread);
- omrthread_monitor_wait(vm->liveVirtualThreadListMutex);
- vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
- threadObject = J9_JNI_UNWRAP_REFERENCE(thread);
- }
+ vm->internalVMFunctions->acquireVThreadInspector(currentThread, thread, TRUE);
jint vthreadState = J9VMJAVALANGVIRTUALTHREAD_STATE(currentThread, threadObject);
j9object_t carrierThread = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CARRIERTHREAD(currentThread, threadObject);
@@ -170,7 +162,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) {
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ vm->internalVMFunctions->releaseVThreadInspector(currentThread, thread);
}
#endif /* JAVA_SPEC_VERSION >= 19 */
omrthread_monitor_exit(vm->vmThreadListMutex);
@@ -182,14 +174,7 @@ getVMThread(J9VMThread *currentThread, jthread thread, J9VMThread **vmThreadPtr,
if (NULL != targetThread) {
targetThread->inspectorCount += 1;
}
-#if JAVA_SPEC_VERSION >= 19
- if (isVirtualThread) {
- I_64 vthreadInspectorCount = J9OBJECT_I64_LOAD(currentThread, threadObject, vm->virtualThreadInspectorCountOffset) + 1;
- Assert_JVMTI_true(vthreadInspectorCount > 0);
- J9OBJECT_I64_STORE(currentThread, threadObject, vm->virtualThreadInspectorCountOffset, vthreadInspectorCount);
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
- }
-#endif /* JAVA_SPEC_VERSION >= 19 */
+
omrthread_monitor_exit(vm->vmThreadListMutex);
#if JAVA_SPEC_VERSION >= 19
@@ -211,18 +196,7 @@ releaseVMThread(J9VMThread *currentThread, J9VMThread *targetThread, jthread thr
if (NULL != thread) {
j9object_t threadObject = J9_JNI_UNWRAP_REFERENCE(thread);
if ((currentThread->threadObject != threadObject) && IS_JAVA_LANG_VIRTUALTHREAD(currentThread, threadObject)) {
- J9JavaVM *vm = currentThread->javaVM;
- I_64 vthreadInspectorCount = 0;
- /* Release the virtual thread (allow it to die) now that we are no longer inspecting it. */
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
- vthreadInspectorCount = J9OBJECT_I64_LOAD(currentThread, threadObject, vm->virtualThreadInspectorCountOffset);
- Assert_JVMTI_true(vthreadInspectorCount > 0);
- vthreadInspectorCount -= 1;
- J9OBJECT_I64_STORE(currentThread, threadObject, vm->virtualThreadInspectorCountOffset, vthreadInspectorCount);
- if (!vm->inspectingLiveVirtualThreadList && (0 == vthreadInspectorCount)) {
- omrthread_monitor_notify_all(vm->liveVirtualThreadListMutex);
- }
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ currentThread->javaVM->internalVMFunctions->releaseVThreadInspector(currentThread, thread);
}
}
#endif /* JAVA_SPEC_VERSION >= 19 */
diff --git a/runtime/jvmti/jvmtiThread.c b/runtime/jvmti/jvmtiThread.c
index 33cf6ede64b..765b625c1bc 100644
--- a/runtime/jvmti/jvmtiThread.c
+++ b/runtime/jvmti/jvmtiThread.c
@@ -36,6 +36,19 @@ static jvmtiError resumeThread(J9VMThread *currentThread, jthread thread);
static UDATA wrappedAgentThreadStart(J9PortLibrary *portLib, void *entryArg);
static void ownedMonitorIterator(J9VMThread *currentThread, J9StackWalkState *walkState, j9object_t *slot, const void *stackLocation);
+#if JAVA_SPEC_VERSION >= 19
+#include "HeapIteratorAPI.h"
+
+typedef struct jvmtiVThreadCallBackData {
+ const jthread *except_list;
+ jint except_count;
+ BOOLEAN is_suspend;
+ BOOLEAN suspend_current_thread;
+} jvmtiVThreadCallBackData;
+
+static jvmtiIterationControl jvmtiSuspendResumeCallBack(J9VMThread *vmThread, J9MM_IterateObjectDescriptor *object, void *userData);
+#endif /* JAVA_SPEC_VERSION >= 19 */
+
jvmtiError JNICALL
jvmtiGetThreadState(jvmtiEnv *env,
jthread thread,
@@ -1289,6 +1302,58 @@ jvmtiGetCurrentThread(jvmtiEnv *env,
}
#if JAVA_SPEC_VERSION >= 19
+static jvmtiIterationControl
+jvmtiSuspendResumeCallBack(J9VMThread *vmThread, J9MM_IterateObjectDescriptor *object, void *userData)
+{
+ j9object_t vthread = J9VMJDKINTERNALVMCONTINUATION_VTHREAD(vmThread, object->object);
+ if (NULL != vthread) {
+ jvmtiVThreadCallBackData *data = (jvmtiVThreadCallBackData*)userData;
+ BOOLEAN is_excepted = FALSE;
+ for (jint i = 0; i < data->except_count; ++i) {
+ if (vthread == J9_JNI_UNWRAP_REFERENCE(data->except_list[i])) {
+ is_excepted = TRUE;
+ break;
+ }
+ }
+ if (!is_excepted) {
+ J9JavaVM *vm = vmThread->javaVM;
+ J9VMThread *targetThread = NULL;
+ j9object_t carrierThread = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CARRIERTHREAD(vmThread, vthread);
+ if (NULL != carrierThread) {
+ targetThread = J9VMJAVALANGTHREAD_THREADREF(vmThread, carrierThread);
+ Assert_JVMTI_notNull(targetThread);
+ }
+ if (data->is_suspend) {
+ if (NULL != targetThread) {
+ 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);
+ }
+ } else {
+ /* NULL carrier thread imply the virtual threadis unmounted. */
+ if (0 == J9OBJECT_U32_LOAD(vmThread, vthread, vm->isSuspendedByJVMTIOffset)) {
+ J9OBJECT_U32_STORE(vmThread, vthread, vm->isSuspendedByJVMTIOffset, 1);
+ }
+ }
+ } else {
+ /* Resume the virtual thread. */
+ if (NULL != targetThread) {
+ 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);
+ }
+ } else {
+ /* targetThread is NULL only for virtual threads. */
+ if (0 != J9OBJECT_U32_LOAD(currentThread, vthread, vm->isSuspendedByJVMTIOffset)) {
+ J9OBJECT_U32_STORE(currentThread, vthread, vm->isSuspendedByJVMTIOffset, 0);
+ }
+ }
+ }
+ }
+ }
+ return JVMTI_ITERATION_CONTINUE;
+}
+
jvmtiError JNICALL
jvmtiSuspendAllVirtualThreads(jvmtiEnv *env,
jint except_count,
@@ -1303,8 +1368,9 @@ jvmtiSuspendAllVirtualThreads(jvmtiEnv *env,
rc = getCurrentVMThread(vm, ¤tThread);
if (JVMTI_ERROR_NONE == rc) {
jint i = 0;
- BOOLEAN currentThreadEverSuspended = FALSE;
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ PORT_ACCESS_FROM_JAVAVM(vm);
+ jvmtiVThreadCallBackData data = {except_list, except_count, TRUE, FALSE};
vmFuncs->internalEnterVMFromJNI(currentThread);
@@ -1327,47 +1393,12 @@ jvmtiSuspendAllVirtualThreads(jvmtiEnv *env,
}
/* Walk all virtual threads. */
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
- while (vm->inspectingLiveVirtualThreadList) {
- /* Virtual thread list is being inspected, wait. */
- vmFuncs->internalExitVMToJNI(currentThread);
- omrthread_monitor_wait(vm->liveVirtualThreadListMutex);
- vmFuncs->internalEnterVMFromJNI(currentThread);
- }
- vm->inspectingLiveVirtualThreadList = TRUE;
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
- if (NULL != vm->liveVirtualThreadList) {
- j9object_t root = *(vm->liveVirtualThreadList);
- /* Skip the root, which is a dummy virtual thread and global ref. */
- j9object_t walkVirtualThread = J9OBJECT_OBJECT_LOAD(currentThread, root, vm->virtualThreadLinkNextOffset);
- do {
- BOOLEAN suspend = TRUE;
- for (i = 0; i < except_count; ++i) {
- if (walkVirtualThread == J9_JNI_UNWRAP_REFERENCE(except_list[i])) {
- suspend = FALSE;
- break;
- }
- }
- if (suspend) {
- BOOLEAN currentThreadSuspended = FALSE;
- JNIEnv *jniEnv = (JNIEnv *)currentThread;
- jobject virtualThreadRef = vmFuncs->j9jni_createLocalRef(jniEnv, walkVirtualThread);
- /* Ignore errors if the virtual thread is already suspended. */
- suspendThread(currentThread, (jthread)virtualThreadRef, FALSE, ¤tThreadSuspended);
- walkVirtualThread = J9_JNI_UNWRAP_REFERENCE(virtualThreadRef);
- vmFuncs->j9jni_deleteLocalRef(jniEnv, virtualThreadRef);
- currentThreadEverSuspended |= currentThreadSuspended;
- }
- walkVirtualThread = J9OBJECT_OBJECT_LOAD(currentThread, walkVirtualThread, vm->virtualThreadLinkNextOffset);
- } while (root != walkVirtualThread);
- }
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
- vm->inspectingLiveVirtualThreadList = FALSE;
- omrthread_monitor_notify_all(vm->liveVirtualThreadListMutex);
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ vmFuncs->acquireExclusiveVMAccess(currentThread);
+ vm->memoryManagerFunctions->j9mm_iterate_all_continuation_objects(currentThread, PORTLIB, 0, jvmtiSuspendResumeCallBack, (void*)&data);
+ vmFuncs->releaseExclusiveVMAccess(currentThread);
/* If the current thread appeared in the list (and was marked as suspended), block now until the thread is resumed. */
- if (currentThreadEverSuspended) {
+ if (data.suspend_current_thread) {
vmFuncs->internalExitVMToJNI(currentThread);
setHaltFlag(currentThread, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND);
vmFuncs->internalEnterVMFromJNI(currentThread);
@@ -1394,6 +1425,8 @@ jvmtiResumeAllVirtualThreads(jvmtiEnv *env,
if (rc == JVMTI_ERROR_NONE) {
jint i = 0;
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ PORT_ACCESS_FROM_JAVAVM(vm);
+ jvmtiVThreadCallBackData data = {except_list, except_count, FALSE, FALSE};
vmFuncs->internalEnterVMFromJNI(currentThread);
@@ -1416,38 +1449,9 @@ jvmtiResumeAllVirtualThreads(jvmtiEnv *env,
}
/* Walk all virtual threads. */
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
- while (vm->inspectingLiveVirtualThreadList) {
- /* Virtual thread list is being inspected, wait. */
- vmFuncs->internalExitVMToJNI(currentThread);
- omrthread_monitor_wait(vm->liveVirtualThreadListMutex);
- vmFuncs->internalEnterVMFromJNI(currentThread);
- }
- vm->inspectingLiveVirtualThreadList = TRUE;
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
- if (NULL != vm->liveVirtualThreadList) {
- j9object_t root = *(vm->liveVirtualThreadList);
- /* Skip the root, which is a dummy virtual thread and global ref. */
- j9object_t walkVirtualThread = J9OBJECT_OBJECT_LOAD(currentThread, root, vm->virtualThreadLinkNextOffset);
- do {
- BOOLEAN resume = TRUE;
- for (i = 0; i < except_count; ++i) {
- if (walkVirtualThread == J9_JNI_UNWRAP_REFERENCE(except_list[i])) {
- resume = FALSE;
- break;
- }
- }
- if (resume) {
- /* Ignore errors if the virtual thread is already resumed. */
- resumeThread(currentThread, (jthread)&walkVirtualThread);
- }
- walkVirtualThread = J9OBJECT_OBJECT_LOAD(currentThread, walkVirtualThread, vm->virtualThreadLinkNextOffset);
- } while (root != walkVirtualThread);
- }
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
- vm->inspectingLiveVirtualThreadList = FALSE;
- omrthread_monitor_notify_all(vm->liveVirtualThreadListMutex);
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ vmFuncs->acquireExclusiveVMAccess(currentThread);
+ vm->memoryManagerFunctions->j9mm_iterate_all_continuation_objects(currentThread, PORTLIB, 0, jvmtiSuspendResumeCallBack, (void*)&data);
+ vmFuncs->releaseExclusiveVMAccess(currentThread);
done:
vmFuncs->internalExitVMToJNI(currentThread);
}
diff --git a/runtime/oti/VMHelpers.hpp b/runtime/oti/VMHelpers.hpp
index 69086e6fb93..ce15768c208 100644
--- a/runtime/oti/VMHelpers.hpp
+++ b/runtime/oti/VMHelpers.hpp
@@ -2252,7 +2252,6 @@ class VM_VMHelpers
}
}
}
-
#endif /* JAVA_SPEC_VERSION >= 19 */
static VMINLINE void
diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h
index 7a6050166e9..e319b67ef3b 100644
--- a/runtime/oti/j9nonbuilder.h
+++ b/runtime/oti/j9nonbuilder.h
@@ -4960,6 +4960,8 @@ typedef struct J9InternalVMFunctions {
void (*freeTLS)(struct J9VMThread *currentThread, j9object_t threadObj);
UDATA (*walkContinuationStackFrames)(struct J9VMThread *currentThread, struct J9VMContinuation *continuation, J9StackWalkState *walkState);
UDATA (*walkAllStackFrames)(struct J9VMThread *currentThread, J9StackWalkState *walkState);
+ BOOLEAN (*acquireVThreadInspector)(struct J9VMThread *currentThread, jobject thread, BOOLEAN spin);
+ void (*releaseVThreadInspector)(struct J9VMThread *currentThread, jobject thread);
#endif /* JAVA_SPEC_VERSION >= 19 */
UDATA (*checkArgsConsumed)(struct J9JavaVM * vm, struct J9PortLibrary* portLibrary, struct J9VMInitArgs* j9vm_args);
} J9InternalVMFunctions;
@@ -5834,11 +5836,6 @@ typedef struct J9JavaVM {
struct J9HashTable* ensureHashedClasses;
#if JAVA_SPEC_VERSION >= 19
U_64 nextTID;
- j9object_t *liveVirtualThreadList;
- omrthread_monitor_t liveVirtualThreadListMutex;
- volatile BOOLEAN inspectingLiveVirtualThreadList;
- UDATA virtualThreadLinkNextOffset;
- UDATA virtualThreadLinkPreviousOffset;
UDATA virtualThreadInspectorCountOffset;
UDATA isSuspendedByJVMTIOffset;
UDATA tlsOffset;
diff --git a/runtime/oti/vm_api.h b/runtime/oti/vm_api.h
index 866c77e55c5..c92a1dece44 100644
--- a/runtime/oti/vm_api.h
+++ b/runtime/oti/vm_api.h
@@ -4426,6 +4426,26 @@ walkContinuationStackFrames(J9VMThread *currentThread, J9VMContinuation *continu
*/
UDATA
walkAllStackFrames(J9VMThread *currentThread, J9StackWalkState *walkState);
+
+/**
+ * @brief Acquire inspector access on VirtualThread, block until access is acquired.
+ *
+ * @param currentThread
+ * @param thread target VirtualThread to acquire the inspector on
+ * @param spin call should spin until sucessfully acquired access
+ * @return TRUE on success, FALSE on failure
+ */
+BOOLEAN
+acquireVThreadInspector(J9VMThread *currentThread, jobject thread, BOOLEAN spin);
+
+/**
+ * @brief Release the inspector acquired from VirtualThread.
+ *
+ * @param currentThread
+ * @param thread target VirtualThread to release the inspector on
+ */
+void
+releaseVThreadInspector(J9VMThread *currentThread, jobject thread);
#endif /* JAVA_SPEC_VERSION >= 19 */
/* ---------------- hookableAsync.c ---------------- */
diff --git a/runtime/oti/vmconstantpool.xml b/runtime/oti/vmconstantpool.xml
index 17d7fea6b52..4be79d40312 100644
--- a/runtime/oti/vmconstantpool.xml
+++ b/runtime/oti/vmconstantpool.xml
@@ -416,6 +416,7 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-excepti
+
diff --git a/runtime/vm/ContinuationHelpers.cpp b/runtime/vm/ContinuationHelpers.cpp
index d1f861679b1..77db9b03546 100644
--- a/runtime/vm/ContinuationHelpers.cpp
+++ b/runtime/vm/ContinuationHelpers.cpp
@@ -380,4 +380,64 @@ walkAllStackFrames(J9VMThread *currentThread, J9StackWalkState *walkState)
(void*)walkState);
return rc;
}
+
+BOOLEAN
+acquireVThreadInspector(J9VMThread *currentThread, jobject thread, BOOLEAN spin)
+{
+ J9JavaVM *vm = currentThread->javaVM;
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ MM_ObjectAccessBarrierAPI objectAccessBarrier = MM_ObjectAccessBarrierAPI(currentThread);
+ j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+ I_64 vthreadInspectorCount;
+retry:
+ vthreadInspectorCount = J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset);
+ if (vthreadInspectorCount < 0) {
+ /* Thread is in transition, wait. */
+ vmFuncs->internalExitVMToJNI(currentThread);
+ VM_AtomicSupport::yieldCPU();
+ /* After wait, the thread may suspend here. */
+ vmFuncs->internalEnterVMFromJNI(currentThread);
+ threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+ if (spin) {
+ goto retry;
+ } else {
+ return FALSE;
+ }
+ } else if (!objectAccessBarrier.inlineMixedObjectCompareAndSwapU64(
+ currentThread,
+ threadObj,
+ vm->virtualThreadInspectorCountOffset,
+ (U_64)vthreadInspectorCount,
+ ((U_64)(vthreadInspectorCount + 1)))
+ ) {
+ /* Field updated by another thread, try again. */
+ if (spin) {
+ goto retry;
+ } else {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void
+releaseVThreadInspector(J9VMThread *currentThread, jobject thread)
+{
+ J9JavaVM *vm = currentThread->javaVM;
+ MM_ObjectAccessBarrierAPI objectAccessBarrier = MM_ObjectAccessBarrierAPI(currentThread);
+ j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+ I_64 vthreadInspectorCount = J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset);
+ /* vthreadInspectorCount must be greater than 0 before decrement. */
+ Assert_VM_true(vthreadInspectorCount > 0);
+ while (!objectAccessBarrier.inlineMixedObjectCompareAndSwapU64(
+ currentThread,
+ threadObj,
+ vm->virtualThreadInspectorCountOffset,
+ (U_64)vthreadInspectorCount,
+ ((U_64)(vthreadInspectorCount - 1)))
+ ) {
+ /* Field updated by another thread, try again. */
+ vthreadInspectorCount = J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset);
+ }
+}
} /* extern "C" */
diff --git a/runtime/vm/intfunc.c b/runtime/vm/intfunc.c
index 02cc8984e62..d11286c93e4 100644
--- a/runtime/vm/intfunc.c
+++ b/runtime/vm/intfunc.c
@@ -436,6 +436,8 @@ J9InternalVMFunctions J9InternalFunctions = {
freeTLS,
walkContinuationStackFrames,
walkAllStackFrames,
+ acquireVThreadInspector,
+ releaseVThreadInspector,
#endif /* JAVA_SPEC_VERSION >= 19 */
checkArgsConsumed,
};
diff --git a/runtime/vm/vmthinit.c b/runtime/vm/vmthinit.c
index 5c9d1158b86..5a643f4a9a4 100644
--- a/runtime/vm/vmthinit.c
+++ b/runtime/vm/vmthinit.c
@@ -92,7 +92,6 @@ UDATA initializeVMThreading(J9JavaVM *vm)
#if JAVA_SPEC_VERSION >= 19
/* Held when adding or removing a virtual thread from the list at virtual thread start or terminate. */
- omrthread_monitor_init_with_name(&vm->liveVirtualThreadListMutex, 0, "Live virtual thread list mutex") ||
omrthread_monitor_init_with_name(&vm->tlsFinalizersMutex, 0, "TLS finalizers mutex") ||
omrthread_monitor_init_with_name(&vm->tlsPoolMutex, 0, "TLS pool mutex") ||
#endif /* JAVA_SPEC_VERSION >= 19 */
@@ -197,10 +196,6 @@ void terminateVMThreading(J9JavaVM *vm)
#endif /* JAVA_SPEC_VERSION >= 16 */
#if JAVA_SPEC_VERSION >= 19
- if (NULL != vm->liveVirtualThreadListMutex) {
- omrthread_monitor_destroy(vm->liveVirtualThreadListMutex);
- vm->liveVirtualThreadListMutex = NULL;
- }
if (NULL != vm->tlsFinalizersMutex) {
omrthread_monitor_destroy(vm->tlsFinalizersMutex);
vm->tlsFinalizersMutex = NULL;