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); + } + } }