From 54c65d0b601e5403f3e40ecb3353a785070af7da Mon Sep 17 00:00:00 2001 From: Babneet Singh Date: Mon, 8 Aug 2022 20:25:08 -0400 Subject: [PATCH] Block Virtual Threads for JVMTI Inspection - Return the corresponding J9VMThread for a virtual thread from getVMThread. - Prevent the virtual thread from (un)mounting using the inspectorCount approach. - The (un)mountBegin/(un)mountEnd natives lock the mount/unmount section and prevent inspectors from inspecting during that time. - For a virtual thread, even the underlying J9VMThread is prevented from terminating using the inspectorCount approach. - Moved code from notifyJvmtiUnmountEnd to notifyJvmtiUnmountBegin for blocking before unmount. Also, VirtualThread.carrierThread is set to NULL in unmount. So, removing the VirtualThread from the linked list before the last unmount in notifyJvmtiUnmountBegin seems correct. - Block mount after yield if a virtual thread is being inspected. - In getVMThread: - targetThread will be NULL for a yielded virtual thread. - JVMTI_ERROR_NONE will be returned for a yielded virtual thread. - virtualThreadInspectorCount will be incremented for the yielded virtual thread. - If a virtual thread is in the process of mounting/unmounting, wait until it is done before inspecting. Related: #15183. This impacts all JVMTI functions that rely on getVMThread and releaseVMThread. Original PR: #15690 Co-authored-by: Babneet Singh Signed-off-by: Eric Yang --- runtime/jcl/common/jclcinit.c | 9 +- runtime/jcl/common/thread.cpp | 106 ++++++++++++++---- runtime/jvmti/jvmtiExtensionMechanism.c | 12 +- runtime/jvmti/jvmtiForceEarlyReturn.c | 4 +- runtime/jvmti/jvmtiHelpers.c | 99 ++++++++++++---- runtime/jvmti/jvmtiLocalVariable.c | 6 +- runtime/jvmti/jvmtiStackFrame.c | 20 ++-- runtime/jvmti/jvmtiThread.c | 47 ++++---- runtime/jvmti/jvmtiThreadGroup.c | 2 +- runtime/jvmti/jvmtiTimers.c | 2 +- runtime/jvmti/jvmti_internal.h | 29 ++--- runtime/jvmti/suspendhelper.cpp | 8 +- runtime/oti/j9nonbuilder.h | 1 + runtime/oti/jvmtiInternal.h | 6 + runtime/oti/vmconstantpool.xml | 4 + test/functional/Java19andUp/playlist.xml | 4 +- .../test/jep425/VirtualThreadTests.java | 31 +++++ 17 files changed, 278 insertions(+), 112 deletions(-) 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 c0ff8d84df2..6d5f190c1f0 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -5679,6 +5679,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 c1a4a1ab998..e69bb376124 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 + @@ -239,6 +240,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); + } + } }