diff --git a/runtime/jcl/common/jclcinit.c b/runtime/jcl/common/jclcinit.c
index c2dffc16694..4502b15ce45 100644
--- a/runtime/jcl/common/jclcinit.c
+++ b/runtime/jcl/common/jclcinit.c
@@ -644,13 +644,20 @@ initializeRequiredClasses(J9VMThread *vmThread, char* dllName)
#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */
#if JAVA_SPEC_VERSION >= 19
- /* These fields point to the next and previous VirtualThreads in the liveVirtualThreadList. */
+ /* 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;
+ }
#endif /* JAVA_SPEC_VERSION >= 19 */
vmThread->privateFlags |= J9_PRIVATE_FLAGS_REPORT_ERROR_LOADING_CLASS;
diff --git a/runtime/jcl/common/thread.cpp b/runtime/jcl/common/thread.cpp
index 82caf73fd8f..a6e068e2c0e 100644
--- a/runtime/jcl/common/thread.cpp
+++ b/runtime/jcl/common/thread.cpp
@@ -527,17 +527,32 @@ Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass clazz)
void JNICALL
Java_java_lang_VirtualThread_notifyJvmtiMountBegin(JNIEnv *env, jobject thread, jboolean firstMount)
{
- if (firstMount) {
- J9VMThread *currentThread = (J9VMThread *)env;
- J9JavaVM *vm = currentThread->javaVM;
- J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
- J9MemoryManagerFunctions *mmFuncs = vm->memoryManagerFunctions;
+ J9VMThread *currentThread = (J9VMThread *)env;
+ J9JavaVM *vm = currentThread->javaVM;
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ vmFuncs->internalEnterVMFromJNI(currentThread);
+ omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
+ j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+
+ while (J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset) > 0) {
+ /* Thread is being inspected, wait. */
+ VM_VMHelpers::pushObjectInSpecialFrame(currentThread, threadObj);
+ vmFuncs->internalExitVMToJNI(currentThread);
+ omrthread_monitor_wait(vm->liveVirtualThreadListMutex);
vmFuncs->internalEnterVMFromJNI(currentThread);
+ threadObj = VM_VMHelpers::popObjectInSpecialFrame(currentThread);
+ }
+
+ /* 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);
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
+ if (firstMount) {
if (NULL == vm->liveVirtualThreadList) {
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.
@@ -562,7 +577,6 @@ Java_java_lang_VirtualThread_notifyJvmtiMountBegin(JNIEnv *env, jobject thread,
if (NULL != vm->liveVirtualThreadList) {
j9object_t root = *(vm->liveVirtualThreadList);
- j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
j9object_t rootPrev = J9OBJECT_OBJECT_LOAD(currentThread, root, vm->virtualThreadLinkPreviousOffset);
/* Add thread to the end of the list. */
@@ -571,38 +585,61 @@ Java_java_lang_VirtualThread_notifyJvmtiMountBegin(JNIEnv *env, jobject thread,
J9OBJECT_OBJECT_STORE(currentThread, rootPrev, vm->virtualThreadLinkNextOffset, threadObj);
J9OBJECT_OBJECT_STORE(currentThread, root, vm->virtualThreadLinkPreviousOffset, threadObj);
}
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
-
- vmFuncs->internalExitVMToJNI(currentThread);
}
+
+ omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ vmFuncs->internalExitVMToJNI(currentThread);
}
/* private native void notifyJvmtiMountEnd(boolean firstMount); */
void JNICALL
Java_java_lang_VirtualThread_notifyJvmtiMountEnd(JNIEnv *env, jobject thread, jboolean firstMount)
{
+ J9VMThread *currentThread = (J9VMThread *)env;
+ J9JavaVM *vm = currentThread->javaVM;
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+
+ vmFuncs->internalEnterVMFromJNI(currentThread);
+ omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
+ j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+
+ /* Allow thread to be inspected again. */
+ Assert_JCL_true(-1 == J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset));
+ J9OBJECT_I64_STORE(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, 0);
+ omrthread_monitor_notify_all(vm->liveVirtualThreadListMutex);
+
+ omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ vmFuncs->internalExitVMToJNI(currentThread);
}
/* private native void notifyJvmtiUnmountBegin(boolean lastUnmount); */
void JNICALL
Java_java_lang_VirtualThread_notifyJvmtiUnmountBegin(JNIEnv *env, jobject thread, jboolean lastUnmount)
{
-}
+ J9VMThread *currentThread = (J9VMThread *)env;
+ J9JavaVM *vm = currentThread->javaVM;
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
-/* private native void notifyJvmtiUnmountEnd(boolean lastUnmount); */
-void JNICALL
-Java_java_lang_VirtualThread_notifyJvmtiUnmountEnd(JNIEnv *env, jobject thread, jboolean lastUnmount)
-{
- if (lastUnmount) {
- J9VMThread *currentThread = (J9VMThread *)env;
- J9JavaVM *vm = currentThread->javaVM;
- J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ vmFuncs->internalEnterVMFromJNI(currentThread);
+ omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
+ j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+ while (J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset) > 0) {
+ /* Thread is being inspected, wait. */
+ VM_VMHelpers::pushObjectInSpecialFrame(currentThread, threadObj);
+ vmFuncs->internalExitVMToJNI(currentThread);
+ omrthread_monitor_wait(vm->liveVirtualThreadListMutex);
vmFuncs->internalEnterVMFromJNI(currentThread);
+ threadObj = VM_VMHelpers::popObjectInSpecialFrame(currentThread);
+ }
- omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
+ /* 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);
+
+ if (lastUnmount) {
if (NULL != vm->liveVirtualThreadList) {
- j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
j9object_t threadPrev = J9OBJECT_OBJECT_LOAD(currentThread, threadObj, vm->virtualThreadLinkPreviousOffset);
j9object_t threadNext = J9OBJECT_OBJECT_LOAD(currentThread, threadObj, vm->virtualThreadLinkNextOffset);
@@ -610,10 +647,31 @@ Java_java_lang_VirtualThread_notifyJvmtiUnmountEnd(JNIEnv *env, jobject thread,
J9OBJECT_OBJECT_STORE(currentThread, threadPrev, vm->virtualThreadLinkNextOffset, threadNext);
J9OBJECT_OBJECT_STORE(currentThread, threadNext, vm->virtualThreadLinkPreviousOffset, threadPrev);
}
- omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
-
- vmFuncs->internalExitVMToJNI(currentThread);
}
+
+ omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ vmFuncs->internalExitVMToJNI(currentThread);
+}
+
+/* private native void notifyJvmtiUnmountEnd(boolean lastUnmount); */
+void JNICALL
+Java_java_lang_VirtualThread_notifyJvmtiUnmountEnd(JNIEnv *env, jobject thread, jboolean lastUnmount)
+{
+ J9VMThread *currentThread = (J9VMThread *)env;
+ J9JavaVM *vm = currentThread->javaVM;
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+
+ vmFuncs->internalEnterVMFromJNI(currentThread);
+ omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
+ j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);
+
+ /* Allow thread to be inspected again. */
+ Assert_JCL_true(-1 == J9OBJECT_I64_LOAD(currentThread, threadObj, vm->virtualThreadInspectorCountOffset));
+ J9OBJECT_I64_STORE(currentThread, threadObj, vm->virtualThreadInspectorCountOffset, 0);
+ omrthread_monitor_notify_all(vm->liveVirtualThreadListMutex);
+
+ omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ vmFuncs->internalExitVMToJNI(currentThread);
}
/* private static native void registerNatives(); */
diff --git a/runtime/jvmti/jvmtiExtensionMechanism.c b/runtime/jvmti/jvmtiExtensionMechanism.c
index 956ff3e7fb4..9a939cb3bbd 100644
--- a/runtime/jvmti/jvmtiExtensionMechanism.c
+++ b/runtime/jvmti/jvmtiExtensionMechanism.c
@@ -1458,7 +1458,7 @@ jvmtiGetOSThreadID(jvmtiEnv* jvmti_env, ...)
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -1468,7 +1468,7 @@ jvmtiGetOSThreadID(jvmtiEnv* jvmti_env, ...)
rc = getVMThread(currentThread, thread, &targetThread, TRUE, TRUE);
if (rc == JVMTI_ERROR_NONE) {
rv_threadid = (jlong) omrthread_get_osId(targetThread->osThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -1508,7 +1508,7 @@ jvmtiGetStackTraceExtended(jvmtiEnv* env, ...)
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -1525,7 +1525,7 @@ jvmtiGetStackTraceExtended(jvmtiEnv* env, ...)
rc = jvmtiInternalGetStackTraceExtended(env, type, currentThread, targetThread, start_depth, (UDATA) max_frame_count, frame_buffer, &rv_count);
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -3444,7 +3444,7 @@ jvmtiGetJ9vmThread(jvmtiEnv *env, ...)
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -3455,7 +3455,7 @@ jvmtiGetJ9vmThread(jvmtiEnv *env, ...)
rc = getVMThread(currentThread, thread, &targetThread, TRUE, TRUE);
if (rc == JVMTI_ERROR_NONE) {
rv_vmThread = targetThread;
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
diff --git a/runtime/jvmti/jvmtiForceEarlyReturn.c b/runtime/jvmti/jvmtiForceEarlyReturn.c
index bb706efc8c7..e7b1dad929a 100644
--- a/runtime/jvmti/jvmtiForceEarlyReturn.c
+++ b/runtime/jvmti/jvmtiForceEarlyReturn.c
@@ -138,7 +138,7 @@ jvmtiForceEarlyReturn(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -238,7 +238,7 @@ jvmtiForceEarlyReturn(jvmtiEnv* env,
}
resume:
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
diff --git a/runtime/jvmti/jvmtiHelpers.c b/runtime/jvmti/jvmtiHelpers.c
index 25442d1b268..2a0b260bf7f 100644
--- a/runtime/jvmti/jvmtiHelpers.c
+++ b/runtime/jvmti/jvmtiHelpers.c
@@ -95,40 +95,82 @@ static UDATA watchedClassEqual (void *lhsEntry, void *rhsEntry, void *userData);
jvmtiError
-getVMThread(J9VMThread * currentThread, jthread thread, J9VMThread ** vmThreadPtr, UDATA allowNull, UDATA mustBeAlive)
+getVMThread(J9VMThread *currentThread, jthread thread, J9VMThread **vmThreadPtr, UDATA allowNull, UDATA mustBeAlive)
{
- J9JavaVM * vm = currentThread->javaVM;
- j9object_t threadObject;
- J9VMThread * targetThread = NULL;
+ J9JavaVM *vm = currentThread->javaVM;
+ j9object_t threadObject = NULL;
+ J9VMThread *targetThread = NULL;
+ BOOLEAN isThreadAlive = FALSE;
+#if JAVA_SPEC_VERSION >= 19
+ BOOLEAN isVirtualThread = FALSE;
+#endif /* JAVA_SPEC_VERSION >= 19 */
- if (thread == NULL) {
+ if (NULL == thread) {
if (allowNull) {
*vmThreadPtr = currentThread;
return JVMTI_ERROR_NONE;
}
return JVMTI_ERROR_INVALID_THREAD;
} else {
- threadObject = *((j9object_t*) thread);
+ threadObject = J9_JNI_UNWRAP_REFERENCE(thread);
if (currentThread->threadObject == threadObject) {
*vmThreadPtr = currentThread;
return JVMTI_ERROR_NONE;
}
}
- /* Make sure the vmThread stays alive while it is being used */
-
+ /* Make sure the vmThread stays alive while it is being used. */
omrthread_monitor_enter(vm->vmThreadListMutex);
- if (!J9VMJAVALANGTHREAD_STARTED(currentThread, threadObject) || ((targetThread = J9VMJAVALANGTHREAD_THREADREF(currentThread, threadObject)) == NULL)) {
+#if JAVA_SPEC_VERSION >= 19
+ isVirtualThread = IS_VIRTUAL_THREAD(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);
+ }
+
+ jint vthreadState = J9VMJAVALANGVIRTUALTHREAD_STATE(currentThread, threadObject);
+ j9object_t carrierThread = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CARRIERTHREAD(currentThread, threadObject);
+ if (NULL != carrierThread) {
+ targetThread = J9VMJAVALANGTHREAD_THREADREF(currentThread, carrierThread);
+ }
+ isThreadAlive = (JVMTI_VTHREAD_STATE_NEW != vthreadState) && (JVMTI_VTHREAD_STATE_TERMINATED != vthreadState);
+ } else
+#endif /* JAVA_SPEC_VERSION >= 19 */
+ {
+ targetThread = J9VMJAVALANGTHREAD_THREADREF(currentThread, threadObject);
+ isThreadAlive = J9VMJAVALANGTHREAD_STARTED(currentThread, threadObject) && (NULL != targetThread);
+ }
+
+ if (!isThreadAlive) {
if (mustBeAlive) {
+#if JAVA_SPEC_VERSION >= 19
+ if (isVirtualThread) {
+ omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ }
+#endif /* JAVA_SPEC_VERSION >= 19 */
omrthread_monitor_exit(vm->vmThreadListMutex);
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
}
*vmThreadPtr = targetThread;
- if (targetThread != NULL) {
- ++(targetThread->inspectorCount);
+ 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);
return JVMTI_ERROR_NONE;
}
@@ -136,15 +178,33 @@ getVMThread(J9VMThread * currentThread, jthread thread, J9VMThread ** vmThreadPt
void
-releaseVMThread(J9VMThread * currentThread, J9VMThread * targetThread)
+releaseVMThread(J9VMThread *currentThread, J9VMThread *targetThread, jthread thread)
{
- if ((currentThread != targetThread) && (targetThread != NULL)) {
- J9JavaVM * vm = targetThread->javaVM;
-
- /* Release this thread (allow it to die) now that we are no longer inspecting it */
+#if JAVA_SPEC_VERSION >= 19
+ if (NULL != thread) {
+ j9object_t threadObject = J9_JNI_UNWRAP_REFERENCE(thread);
+ if ((currentThread->threadObject != threadObject) && IS_VIRTUAL_THREAD(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 (0 == vthreadInspectorCount) {
+ omrthread_monitor_notify_all(vm->liveVirtualThreadListMutex);
+ }
+ omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
+ }
+ }
+#endif /* JAVA_SPEC_VERSION >= 19 */
+ if ((NULL != targetThread) && (currentThread != targetThread)) {
+ J9JavaVM *vm = targetThread->javaVM;
+ /* Release the J9VMThread (allow it to die) now that we are no longer inspecting it. */
omrthread_monitor_enter(vm->vmThreadListMutex);
- if (--(targetThread->inspectorCount) == 0) {
+ if (0 == --(targetThread->inspectorCount)) {
omrthread_monitor_notify_all(vm->vmThreadListMutex);
}
omrthread_monitor_exit(vm->vmThreadListMutex);
@@ -1468,10 +1528,9 @@ setEventNotificationMode(J9JVMTIEnv * j9env, J9VMThread * currentThread, jint mo
}
}
- if (targetThread != NULL) {
- releaseVMThread(currentThread, targetThread);
+ if (NULL != event_thread) {
+ releaseVMThread(currentThread, targetThread, event_thread);
}
-
done:
return rc;
}
diff --git a/runtime/jvmti/jvmtiLocalVariable.c b/runtime/jvmti/jvmtiLocalVariable.c
index 1b3afc2eb4c..b287d331c00 100644
--- a/runtime/jvmti/jvmtiLocalVariable.c
+++ b/runtime/jvmti/jvmtiLocalVariable.c
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 1991, 2018 IBM Corp. and others
+ * Copyright (c) 1991, 2022 IBM Corp. and others
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -309,7 +309,7 @@ jvmtiGetOrSetLocal(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
rc = getVMThread(currentThread, thread, &targetThread, TRUE, TRUE);
@@ -425,7 +425,7 @@ jvmtiGetOrSetLocal(jvmtiEnv* env,
*((jobject *) value_ptr) = vm->internalVMFunctions->j9jni_createLocalRef((JNIEnv *) currentThread, obj);
}
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
}
diff --git a/runtime/jvmti/jvmtiStackFrame.c b/runtime/jvmti/jvmtiStackFrame.c
index c52fb5c323f..15810177222 100644
--- a/runtime/jvmti/jvmtiStackFrame.c
+++ b/runtime/jvmti/jvmtiStackFrame.c
@@ -45,7 +45,7 @@ jvmtiGetStackTrace(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -62,7 +62,7 @@ jvmtiGetStackTrace(jvmtiEnv* env,
rc = jvmtiInternalGetStackTrace(env, currentThread, targetThread, start_depth, (UDATA) max_frame_count, frame_buffer, &rv_count);
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -272,7 +272,7 @@ jvmtiGetFrameCount(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -293,7 +293,7 @@ jvmtiGetFrameCount(jvmtiEnv* env,
rv_count = (jint) walkState.framesWalked;
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -326,7 +326,7 @@ jvmtiPopFrame(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -361,7 +361,7 @@ jvmtiPopFrame(jvmtiEnv* env,
}
}
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -388,7 +388,7 @@ jvmtiGetFrameLocation(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -424,7 +424,7 @@ jvmtiGetFrameLocation(jvmtiEnv* env,
}
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -453,7 +453,7 @@ jvmtiNotifyFramePop(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -491,7 +491,7 @@ jvmtiNotifyFramePop(jvmtiEnv* env,
}
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
diff --git a/runtime/jvmti/jvmtiThread.c b/runtime/jvmti/jvmtiThread.c
index f924facc546..45b60bf2eaa 100644
--- a/runtime/jvmti/jvmtiThread.c
+++ b/runtime/jvmti/jvmtiThread.c
@@ -51,11 +51,10 @@ jvmtiGetThreadState(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
j9object_t threadObject = NULL;
j9object_t threadObjectLock = NULL;
- jboolean threadStartedFlag;
-
+ jboolean threadStartedFlag = JNI_FALSE;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
ENSURE_PHASE_LIVE(env);
@@ -111,7 +110,7 @@ jvmtiGetThreadState(jvmtiEnv* env,
rv_thread_state = getThreadState(currentThread, targetThread->threadObject);
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
}
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -361,7 +360,7 @@ jvmtiStopThread(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -385,7 +384,7 @@ jvmtiStopThread(jvmtiEnv* env,
setHaltFlag(targetThread, J9_PUBLIC_FLAGS_STOP);
}
omrthread_monitor_exit(targetThread->publicFlagsMutex);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -407,7 +406,7 @@ jvmtiInterruptThread(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -422,7 +421,7 @@ jvmtiInterruptThread(jvmtiEnv* env,
vm->sidecarInterruptFunction(targetThread);
}
#endif
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -451,7 +450,7 @@ jvmtiGetThreadInfo(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread = NULL;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -540,7 +539,7 @@ jvmtiGetThreadInfo(jvmtiEnv* env,
rv_context_class_loader = contextClassLoader;
}
done:
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
}
@@ -572,7 +571,7 @@ jvmtiGetOwnedMonitorInfo(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -605,7 +604,7 @@ jvmtiGetOwnedMonitorInfo(jvmtiEnv* env,
rv_owned_monitor_count = count;
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -638,7 +637,7 @@ jvmtiGetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -726,7 +725,7 @@ jvmtiGetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
}
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -755,7 +754,7 @@ jvmtiGetCurrentContendedMonitor(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -780,7 +779,7 @@ jvmtiGetCurrentContendedMonitor(jvmtiEnv* env,
}
vm->internalVMFunctions->resumeThreadForInspection(currentThread, targetThread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -878,7 +877,7 @@ jvmtiSetThreadLocalStorage(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -887,7 +886,7 @@ jvmtiSetThreadLocalStorage(jvmtiEnv* env,
rc = getVMThread(currentThread, thread, &targetThread, TRUE, TRUE);
if (rc == JVMTI_ERROR_NONE) {
THREAD_DATA_FOR_VMTHREAD((J9JVMTIEnv *) env, targetThread)->tls = (void *) data;
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
@@ -912,7 +911,7 @@ jvmtiGetThreadLocalStorage(jvmtiEnv* env,
rc = getCurrentVMThread(vm, ¤tThread);
if (rc == JVMTI_ERROR_NONE) {
- J9VMThread * targetThread;
+ J9VMThread *targetThread = NULL;
vm->internalVMFunctions->internalEnterVMFromJNI(currentThread);
@@ -923,7 +922,7 @@ jvmtiGetThreadLocalStorage(jvmtiEnv* env,
rc = getVMThread(currentThread, thread, &targetThread, TRUE, TRUE);
if (rc == JVMTI_ERROR_NONE) {
rv_data = THREAD_DATA_FOR_VMTHREAD((J9JVMTIEnv *) env, targetThread)->tls;
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
done:
vm->internalVMFunctions->internalExitVMToJNI(currentThread);
@@ -939,10 +938,8 @@ jvmtiGetThreadLocalStorage(jvmtiEnv* env,
static jvmtiError
resumeThread(J9VMThread * currentThread, jthread thread)
{
- J9VMThread * targetThread;
- jvmtiError rc;
-
- rc = getVMThread(currentThread, thread, &targetThread, FALSE, TRUE);
+ J9VMThread *targetThread = NULL;
+ jvmtiError rc = getVMThread(currentThread, thread, &targetThread, FALSE, TRUE);
if (rc == JVMTI_ERROR_NONE) {
if (targetThread->publicFlags & J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND) {
clearHaltFlag(targetThread, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND);
@@ -950,7 +947,7 @@ resumeThread(J9VMThread * currentThread, jthread thread)
} else {
rc = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
}
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
return rc;
diff --git a/runtime/jvmti/jvmtiThreadGroup.c b/runtime/jvmti/jvmtiThreadGroup.c
index 06da771d856..b795562e0d7 100644
--- a/runtime/jvmti/jvmtiThreadGroup.c
+++ b/runtime/jvmti/jvmtiThreadGroup.c
@@ -304,7 +304,7 @@ getThreadGroupChildrenImpl(J9JavaVM *vm, J9VMThread *currentThread, jobject grou
if (JVMTI_ERROR_NONE == getVMThread(currentThread, (jthread)&thread, &targetThread, FALSE, TRUE)) {
threads[numLiveThreads++] = (jthread)vmFuncs->j9jni_createLocalRef((JNIEnv *)currentThread, thread);
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, (jthread)&thread);
}
}
diff --git a/runtime/jvmti/jvmtiTimers.c b/runtime/jvmti/jvmtiTimers.c
index da1fa5d8763..fa1da57db5d 100644
--- a/runtime/jvmti/jvmtiTimers.c
+++ b/runtime/jvmti/jvmtiTimers.c
@@ -156,7 +156,7 @@ jvmtiGetThreadCpuTime(jvmtiEnv* env,
} else {
rv_nanos = (jlong)omrthread_get_cpu_time(targetThread->osThread);
}
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
}
done:
diff --git a/runtime/jvmti/jvmti_internal.h b/runtime/jvmti/jvmti_internal.h
index b7fac4cae5b..5b1253c394e 100644
--- a/runtime/jvmti/jvmti_internal.h
+++ b/runtime/jvmti/jvmti_internal.h
@@ -1124,14 +1124,14 @@ getThreadState(J9VMThread *currentThread, j9object_t threadObject);
/**
-* @brief
-* @param currentThread
-* @param thread
-* @param vmThreadPtr
-* @param allowNull
-* @param mustBeAlive
-* @return jvmtiError
-*/
+ * @brief
+ * @param currentThread
+ * @param thread
+ * @param vmThreadPtr
+ * @param allowNull
+ * @param mustBeAlive
+ * @return jvmtiError
+ */
jvmtiError
getVMThread(J9VMThread * currentThread, jthread thread, J9VMThread ** vmThreadPtr, UDATA allowNull, UDATA mustBeAlive);
@@ -1203,13 +1203,14 @@ queueCompileEvent(J9JVMTIData * jvmtiData, jmethodID methodID, void * startPC, U
/**
-* @brief
-* @param currentThread
-* @param targetThread
-* @return void
-*/
+ * @brief
+ * @param currentThread
+ * @param targetThread
+ * @param thread
+ * @return void
+ */
void
-releaseVMThread(J9VMThread * currentThread, J9VMThread * targetThread);
+releaseVMThread(J9VMThread *currentThread, J9VMThread *targetThread, jthread thread);
/**
diff --git a/runtime/jvmti/suspendhelper.cpp b/runtime/jvmti/suspendhelper.cpp
index ad83e1680ed..58e5cd72a18 100644
--- a/runtime/jvmti/suspendhelper.cpp
+++ b/runtime/jvmti/suspendhelper.cpp
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018, 2020 IBM Corp. and others
+ * Copyright (c) 2018, 2022 IBM Corp. and others
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
@@ -29,8 +29,8 @@ extern "C" {
jvmtiError
suspendThread(J9VMThread *currentThread, jthread thread, UDATA allowNull, UDATA *currentThreadSuspended)
{
- J9VMThread * targetThread;
- jvmtiError rc;
+ J9VMThread *targetThread = NULL;
+ jvmtiError rc = JVMTI_ERROR_NONE;
*currentThreadSuspended = FALSE;
rc = getVMThread(currentThread, thread, &targetThread, allowNull, TRUE);
@@ -59,7 +59,7 @@ suspendThread(J9VMThread *currentThread, jthread thread, UDATA allowNull, UDATA
Trc_JVMTI_threadSuspended(targetThread);
}
}
- releaseVMThread(currentThread, targetThread);
+ releaseVMThread(currentThread, targetThread, thread);
}
return rc;
diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h
index 08619123980..f6a4a560612 100644
--- a/runtime/oti/j9nonbuilder.h
+++ b/runtime/oti/j9nonbuilder.h
@@ -5765,6 +5765,7 @@ typedef struct J9JavaVM {
omrthread_monitor_t liveVirtualThreadListMutex;
UDATA virtualThreadLinkNextOffset;
UDATA virtualThreadLinkPreviousOffset;
+ UDATA virtualThreadInspectorCountOffset;
#endif /* JAVA_SPEC_VERSION >= 19 */
} J9JavaVM;
diff --git a/runtime/oti/jvmtiInternal.h b/runtime/oti/jvmtiInternal.h
index 05b8babc412..a5837d74d40 100644
--- a/runtime/oti/jvmtiInternal.h
+++ b/runtime/oti/jvmtiInternal.h
@@ -566,6 +566,12 @@ typedef struct jvmtiGcp_translation {
#define IBMJVMTI_EXTENDED_CALLSTACK 1
#define IBMJVMTI_UNEXTENDED_CALLSTACK 0
+#if JAVA_SPEC_VERSION >= 19
+/* These macros corresponds to the states in j.l.VirtualThread. */
+#define JVMTI_VTHREAD_STATE_NEW 0
+#define JVMTI_VTHREAD_STATE_TERMINATED 99
+#endif /* JAVA_SPEC_VERSION >= 19 */
+
/* The brace mismatches in the macros below are due to the usage pattern:
*
* JVMTI_ENVIRONMENTS_DO(jvmtiData, j9env) {
diff --git a/runtime/oti/vmconstantpool.xml b/runtime/oti/vmconstantpool.xml
index d4dcfacfd6c..9d7ba8732e2 100644
--- a/runtime/oti/vmconstantpool.xml
+++ b/runtime/oti/vmconstantpool.xml
@@ -51,6 +51,7 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-excepti
+
@@ -252,6 +253,9 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-excepti
+
+
+
diff --git a/test/functional/Java19andUp/playlist.xml b/test/functional/Java19andUp/playlist.xml
index 8a01fbcf34a..8627026c0ac 100644
--- a/test/functional/Java19andUp/playlist.xml
+++ b/test/functional/Java19andUp/playlist.xml
@@ -27,8 +27,10 @@
--enable-preview -Xint -Xgcpolicy:nogc
$(ADD_JVM_LIB_DIR_TO_LIBPATH) $(JAVA_COMMAND) $(JVM_OPTIONS) \
+ --add-opens java.base/java.lang=ALL-UNNAMED \
-cp $(Q)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)GeneralTest.jar$(Q) \
- org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng_190.xml$(Q) -testnames Jep425Tests_testVirtualThread \
+ org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng_190.xml$(Q) \
+ -testnames Jep425Tests_testVirtualThread \
-groups $(TEST_GROUP) \
-excludegroups $(DEFAULT_EXCLUDE); \
$(TEST_STATUS)
diff --git a/test/functional/Java19andUp/src/org/openj9/test/jep425/VirtualThreadTests.java b/test/functional/Java19andUp/src/org/openj9/test/jep425/VirtualThreadTests.java
index 789aba0a520..7cc2674b72a 100644
--- a/test/functional/Java19andUp/src/org/openj9/test/jep425/VirtualThreadTests.java
+++ b/test/functional/Java19andUp/src/org/openj9/test/jep425/VirtualThreadTests.java
@@ -27,6 +27,7 @@
import static org.testng.Assert.fail;
import java.lang.Thread;
+import java.lang.reflect.*;
import java.time.Duration;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -139,4 +140,34 @@ public void test_jniFromVirtualthread() {
Assert.fail("Unexpected exception occured : " + e.getMessage() , e);
}
}
+
+ private static int readVirtualThreadStates(Class vthreadCls, String fieldName) throws Exception {
+ Field vthreadState = vthreadCls.getDeclaredField(fieldName);
+ vthreadState.setAccessible(true);
+ return vthreadState.getInt(null);
+ }
+
+ @Test
+ public void test_verifyJVMTIMacros() {
+ final int JVMTI_VTHREAD_STATE_NEW = 0;
+ final int JVMTI_VTHREAD_STATE_TERMINATED = 99;
+
+ int value = 0;
+
+ try {
+ Class> vthreadCls = Class.forName("java.lang.VirtualThread");
+
+ value = readVirtualThreadStates(vthreadCls, "NEW");
+ if (JVMTI_VTHREAD_STATE_NEW != value) {
+ Assert.fail("JVMTI_VTHREAD_STATE_NEW (" + JVMTI_VTHREAD_STATE_NEW + ") does not match VirtualThread.NEW (" + value + ")");
+ }
+
+ value = readVirtualThreadStates(vthreadCls, "TERMINATED");
+ if (JVMTI_VTHREAD_STATE_TERMINATED != value) {
+ Assert.fail("JVMTI_VTHREAD_STATE_TERMINATED (" + JVMTI_VTHREAD_STATE_TERMINATED + ") does not match VirtualThread.TERMINATED (" + value + ")");
+ }
+ } catch (Exception e) {
+ Assert.fail("Unexpected exception occured : " + e.getMessage() , e);
+ }
+ }
}