Skip to content

Commit

Permalink
Merge pull request #15541 from EricYangIBM/jvmtiTLS
Browse files Browse the repository at this point in the history
Add thread local storage to java thread objects
  • Loading branch information
gacholio committed Aug 27, 2022
2 parents 1719d2e + af3a4c1 commit c14fe74
Show file tree
Hide file tree
Showing 16 changed files with 457 additions and 84 deletions.
5 changes: 5 additions & 0 deletions runtime/jcl/common/jclcinit.c
Expand Up @@ -644,6 +644,11 @@ initializeRequiredClasses(J9VMThread *vmThread, char* dllName)
#endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */

#if JAVA_SPEC_VERSION >= 19
/* TLS hidden field points to an array holding jvmti thread local data. */
if (0 != vmFuncs->addHiddenInstanceField(vm, "java/lang/Thread", "tls", "J", &vm->tlsOffset)) {
return 1;
}

/* Points to the next VirtualThread in the liveVirtualThreadList. */
if (0 != vmFuncs->addHiddenInstanceField(vm, "java/lang/VirtualThread", "linkNext", "Ljava/lang/VirtualThread;", &vm->virtualThreadLinkNextOffset)) {
return 1;
Expand Down
10 changes: 10 additions & 0 deletions runtime/jcl/common/thread.cpp
Expand Up @@ -603,6 +603,10 @@ Java_java_lang_VirtualThread_notifyJvmtiMountEnd(JNIEnv *env, jobject thread, jb
omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);

if (firstMount) {
TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_STARTED(vm->hookInterface, currentThread);
}

/* 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);
Expand Down Expand Up @@ -647,6 +651,8 @@ Java_java_lang_VirtualThread_notifyJvmtiUnmountBegin(JNIEnv *env, jobject thread
J9OBJECT_OBJECT_STORE(currentThread, threadPrev, vm->virtualThreadLinkNextOffset, threadNext);
J9OBJECT_OBJECT_STORE(currentThread, threadNext, vm->virtualThreadLinkPreviousOffset, threadPrev);
}

TRIGGER_J9HOOK_VM_VIRTUAL_THREAD_END(vm->hookInterface, currentThread);
}

omrthread_monitor_exit(vm->liveVirtualThreadListMutex);
Expand All @@ -665,6 +671,10 @@ Java_java_lang_VirtualThread_notifyJvmtiUnmountEnd(JNIEnv *env, jobject thread,
omrthread_monitor_enter(vm->liveVirtualThreadListMutex);
j9object_t threadObj = J9_JNI_UNWRAP_REFERENCE(thread);

if (lastUnmount) {
vmFuncs->freeTLS(currentThread, threadObj);
}

/* 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);
Expand Down
3 changes: 3 additions & 0 deletions runtime/jvmti/j9jvmti.tdf
Expand Up @@ -635,3 +635,6 @@ TraceEntry=Trc_JVMTI_jvmtiHookVirtualThreadEnd_Entry Overhead=1 Level=5 Noenv Te
TraceExit=Trc_JVMTI_jvmtiHookVirtualThreadEnd_Exit Overhead=1 Level=5 Noenv Template="HookVirtualThreadEnd"
TraceEntry=Trc_JVMTI_jvmtiHookVirtualThreadStarted_Entry Overhead=1 Level=5 Noenv Template="HookVirtualThreadStarted"
TraceExit=Trc_JVMTI_jvmtiHookVirtualThreadStarted_Exit Overhead=1 Level=5 Noenv Template="HookVirtualThreadStarted"

TraceEntry=Trc_JVMTI_jvmtiHookVirtualThreadDestroy_Entry Overhead=1 Level=5 Noenv Template="HookVirtualThreadDestroy"
TraceExit=Trc_JVMTI_jvmtiHookVirtualThreadDestroy_Exit Overhead=1 Level=5 Noenv Template="HookVirtualThreadDestroy"
3 changes: 0 additions & 3 deletions runtime/jvmti/jvmtiEventManagement.c
Expand Up @@ -203,9 +203,6 @@ jvmtiSetEventNotificationMode(jvmtiEnv* env,
#if JAVA_SPEC_VERSION >= 11
case JVMTI_EVENT_SAMPLED_OBJECT_ALLOC:
#endif /* JAVA_SPEC_VERSION >= 11 */
#if JAVA_SPEC_VERSION >= 19
case JVMTI_EVENT_VIRTUAL_THREAD_START:
#endif /* JAVA_SPEC_VERSION >= 19 */
if (event_thread != NULL) {
JVMTI_ERROR(JVMTI_ERROR_ILLEGAL_ARGUMENT);
}
Expand Down
218 changes: 181 additions & 37 deletions runtime/jvmti/jvmtiHelpers.c
Expand Up @@ -316,7 +316,7 @@ disposeEnvironment(J9JVMTIEnv * j9env, UDATA freeData)
}

if (0 != j9env->tlsKey) {
omrthread_tls_free(j9env->tlsKey);
jvmtiTLSFree(vm, j9env->tlsKey);
j9env->tlsKey = 0;
}
}
Expand Down Expand Up @@ -352,7 +352,6 @@ allocateEnvironment(J9InvocationJavaVM * invocationJavaVM, jint version, void **
#if defined (J9VM_INTERP_NATIVE_SUPPORT)
J9HookInterface** jitHook = vm->internalVMFunctions->getJITHookInterface(vm);
#endif
J9VMThread * walkThread;

memset(j9env, 0, sizeof(J9JVMTIEnv));
j9env->functions = &jvmtiFunctionTable;
Expand Down Expand Up @@ -405,31 +404,16 @@ allocateEnvironment(J9InvocationJavaVM * invocationJavaVM, jint version, void **
if (j9env->breakpoints == NULL) {
goto fail;
}
if (omrthread_tls_alloc(&j9env->tlsKey)) {
if (jvmtiTLSAlloc(vm, &j9env->tlsKey)) {
goto fail;
}

/* Create thread data structs for all existing threads */

omrthread_monitor_enter(vm->vmThreadListMutex);
walkThread = vm->mainThread;
do {
if (createThreadData(j9env, walkThread) != JVMTI_ERROR_NONE) {
/* Struct creation failed, disallow the attaching of this environment */
omrthread_monitor_exit(vm->vmThreadListMutex);
goto fail;
}
} while ((walkThread = walkThread->linkNext) != vm->mainThread);

/* Hook events that every environment needs */
/* Hook the thread and virtual thread destroy events to clean up any allocated TLS structs. */

if (hookRequiredEvents(j9env) != 0) {
omrthread_monitor_exit(vm->vmThreadListMutex);
goto fail;
}

omrthread_monitor_exit(vm->vmThreadListMutex);

if (jvmtiData->environmentsHead == NULL) {
issueWriteBarrier();
jvmtiData->environmentsHead = jvmtiData->environmentsTail = j9env;
Expand Down Expand Up @@ -463,6 +447,20 @@ allocateEnvironment(J9InvocationJavaVM * invocationJavaVM, jint version, void **
UDATA
prepareForEvent(J9JVMTIEnv * j9env, J9VMThread * currentThread, J9VMThread * eventThread, UDATA eventNumber, jthread * threadRefPtr, UDATA * hadVMAccessPtr, UDATA wantVMAccess, UDATA jniRefSlots, UDATA * javaOffloadOldState)
{
J9JVMTIThreadData *threadData = NULL;
#if JAVA_SPEC_VERSION >= 19
j9object_t threadObj = currentThread->threadObject;
/* threadObj can be null early in J9VMThread initialization. */
if (NULL != threadObj) {
void *tlsArray = J9OBJECT_ADDRESS_LOAD(currentThread, threadObj, currentThread->javaVM->tlsOffset);
if (NULL != tlsArray) {
threadData = jvmtiTLSGet(currentThread, threadObj, j9env->tlsKey);
}
}
#else
threadData = jvmtiTLSGet(currentThread, currentThread->threadObject, j9env->tlsKey);
#endif /* JAVA_SPEC_VERSION >= 19 */

/* If this environment has been disposed, do not report any events */

if (j9env->flags & J9JVMTIENV_FLAG_DISPOSED) {
Expand All @@ -474,9 +472,6 @@ prepareForEvent(J9JVMTIEnv * j9env, J9VMThread * currentThread, J9VMThread * eve
if (currentThread->publicFlags & J9_PUBLIC_FLAGS_STOPPED) {
if ((eventNumber != JVMTI_EVENT_VM_DEATH)
&& (eventNumber != JVMTI_EVENT_THREAD_END)
#if JAVA_SPEC_VERSION >= 19
&& (eventNumber != JVMTI_EVENT_VIRTUAL_THREAD_END)
#endif /* JAVA_SPEC_VERSION >= 19 */
) {
return FALSE;
}
Expand All @@ -493,7 +488,9 @@ prepareForEvent(J9JVMTIEnv * j9env, J9VMThread * currentThread, J9VMThread * eve

/* See if the event is enabled either globally or for the current thread */

if (EVENT_IS_ENABLED(eventNumber, &(j9env->globalEventEnable)) || EVENT_IS_ENABLED(eventNumber, &(THREAD_DATA_FOR_VMTHREAD(j9env, currentThread)->threadEventEnable))) {
if (EVENT_IS_ENABLED(eventNumber, &(j9env->globalEventEnable))
|| ((NULL != threadData) && EVENT_IS_ENABLED(eventNumber, &(threadData->threadEventEnable)))
) {
j9object_t * refs;

++jniRefSlots; /* for saving the current exception */
Expand Down Expand Up @@ -1435,26 +1432,59 @@ queueCompileEvent(J9JVMTIData * jvmtiData, jmethodID methodID, void * startPC, U

#endif /* INTERP_NATIVE_SUPPORT */


#if JAVA_SPEC_VERSION >= 19
jvmtiError
createThreadData(J9JVMTIEnv * j9env, J9VMThread * vmThread)
allocateTLS(J9JavaVM *vm, j9object_t thread)
{
J9JVMTIThreadData * threadData;

omrthread_monitor_enter(j9env->threadDataPoolMutex);
threadData = pool_newElement(j9env->threadDataPool);
omrthread_monitor_exit(j9env->threadDataPoolMutex);
if (threadData == NULL) {
return JVMTI_ERROR_OUT_OF_MEMORY;
void *tlsArray = NULL;
jvmtiError rc = JVMTI_ERROR_NONE;
Assert_JVMTI_notNull(thread);

tlsArray = J9OBJECT_ADDRESS_LOAD_VM(vm, thread, vm->tlsOffset);
if (NULL == tlsArray) {
omrthread_monitor_enter(vm->tlsPoolMutex);
tlsArray = J9OBJECT_ADDRESS_LOAD_VM(vm, thread, vm->tlsOffset);
if (NULL == tlsArray) {
/* We are the first to access the TLS array for the given thread. */
tlsArray = pool_newElement(vm->tlsPool);
if (NULL == tlsArray) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
} else {
J9OBJECT_ADDRESS_STORE_VM(vm, thread, vm->tlsOffset, tlsArray);
}
}
omrthread_monitor_exit(vm->tlsPoolMutex);
}

memset(threadData, 0, sizeof(J9JVMTIThreadData));
threadData->vmThread = vmThread;
omrthread_tls_set(vmThread->osThread, j9env->tlsKey, threadData);
return JVMTI_ERROR_NONE;
return rc;
}
#endif /* JAVA_SPEC_VERSION >= 19 */

jvmtiError
createThreadData(J9JVMTIEnv *j9env, J9VMThread *vmThread, j9object_t thread)
{
J9JVMTIThreadData *threadData = NULL;
jvmtiError rc = JVMTI_ERROR_NONE;
Assert_JVMTI_notNull(thread);

threadData = jvmtiTLSGet(vmThread, thread, j9env->tlsKey);
if (NULL == threadData) {
omrthread_monitor_enter(j9env->threadDataPoolMutex);
threadData = jvmtiTLSGet(vmThread, thread, j9env->tlsKey);
if (NULL == threadData) {
/* We are the first to access the thread data for the given thread. */
threadData = pool_newElement(j9env->threadDataPool);
if (NULL == threadData) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
} else {
jvmtiTLSSet(vmThread, thread, j9env->tlsKey, threadData);
}
}
omrthread_monitor_exit(j9env->threadDataPoolMutex);
}

return rc;
}

jvmtiError
setEventNotificationMode(J9JVMTIEnv * j9env, J9VMThread * currentThread, jint mode, jint event_type, jthread event_thread, jint low, jint high)
Expand All @@ -1479,11 +1509,24 @@ setEventNotificationMode(J9JVMTIEnv * j9env, J9VMThread * currentThread, jint mo
if (event_thread == NULL) {
eventMap = &(j9env->globalEventEnable);
} else {
j9object_t threadObject = J9_JNI_UNWRAP_REFERENCE(event_thread);
rc = getVMThread(currentThread, event_thread, &targetThread, TRUE, TRUE);
if (rc != JVMTI_ERROR_NONE) {
goto done;
}
eventMap = &(THREAD_DATA_FOR_VMTHREAD(j9env, targetThread)->threadEventEnable);
#if JAVA_SPEC_VERSION >= 19
rc = allocateTLS(vm, threadObject);
if (JVMTI_ERROR_NONE != rc) {
releaseVMThread(currentThread, targetThread, event_thread);
goto done;
}
#endif /* JAVA_SPEC_VERSION >= 19 */
rc = createThreadData(j9env, targetThread, threadObject);
if (JVMTI_ERROR_NONE != rc) {
releaseVMThread(currentThread, targetThread, event_thread);
goto done;
}
eventMap = &(jvmtiTLSGet(targetThread, threadObject, j9env->tlsKey)->threadEventEnable);
}

/* Single step can alloc/free the bytecode table, so we need exclusive to prevent any threads from using the table */
Expand Down Expand Up @@ -1660,3 +1703,104 @@ ensureHeapWalkable(J9VMThread *currentThread)
}
}
}

#if JAVA_SPEC_VERSION >= 19
static void
tlsNullFinalizer(void *entry)
{
/* do nothing */
}
#endif /* JAVA_SPEC_VERSION >= 19 */

IDATA
jvmtiTLSAlloc(J9JavaVM *vm, UDATA *handle)
{
#if JAVA_SPEC_VERSION >= 19
return jvmtiTLSAllocWithFinalizer(vm, handle, tlsNullFinalizer);
#else /* JAVA_SPEC_VERSION >= 19 */
return omrthread_tls_alloc(handle);
#endif /* JAVA_SPEC_VERSION >= 19 */
}

#if JAVA_SPEC_VERSION >= 19
IDATA
jvmtiTLSAllocWithFinalizer(J9JavaVM *vm, UDATA *handle, j9_tls_finalizer_t finalizer)
{
IDATA i = 0;

Assert_JVMTI_notNull(finalizer);
*handle = 0;

omrthread_monitor_enter(vm->tlsFinalizersMutex);
for (i = 0; i < J9JVMTI_MAX_TLS_KEYS; i++) {
if (NULL == vm->tlsFinalizers[i]) {
*handle = i + 1;
vm->tlsFinalizers[i] = finalizer;
break;
}
}
omrthread_monitor_exit(vm->tlsFinalizersMutex);

return i < J9JVMTI_MAX_TLS_KEYS ? 0 : -1;
}
#endif /* JAVA_SPEC_VERSION >= 19 */

IDATA
jvmtiTLSFree(J9JavaVM *vm, UDATA key)
{
#if JAVA_SPEC_VERSION >= 19
pool_state state;
J9JVMTIThreadData **each = NULL;

omrthread_monitor_enter(vm->tlsPoolMutex);
each = pool_startDo(vm->tlsPool, &state);
while (NULL != each) {
each[key - 1] = NULL;
each = pool_nextDo(&state);
}
omrthread_monitor_exit(vm->tlsPoolMutex);

omrthread_monitor_enter(vm->tlsFinalizersMutex);
vm->tlsFinalizers[key - 1] = NULL;
omrthread_monitor_exit(vm->tlsFinalizersMutex);

return 0;
#else /* JAVA_SPEC_VERSION >= 19 */
return omrthread_tls_free(key);
#endif /* JAVA_SPEC_VERSION >= 19 */
}

IDATA
jvmtiTLSSet(J9VMThread *vmThread, j9object_t thread, UDATA key, J9JVMTIThreadData *value)
{
#if JAVA_SPEC_VERSION >= 19
J9JavaVM *vm = vmThread->javaVM;
J9JVMTIThreadData **data = NULL;
Assert_JVMTI_notNull(thread);

data = (J9JVMTIThreadData **)J9OBJECT_ADDRESS_LOAD_VM(vm, thread, vm->tlsOffset);
Assert_JVMTI_notNull(data);
data[key - 1] = value;

return 0;
#else /* JAVA_SPEC_VERSION >= 19 */
return omrthread_tls_set(vmThread->osThread, key, (void *)(value));
#endif /* JAVA_SPEC_VERSION >= 19 */
}

J9JVMTIThreadData *
jvmtiTLSGet(J9VMThread *vmThread, j9object_t thread, UDATA key)
{
#if JAVA_SPEC_VERSION >= 19
J9JavaVM *vm = vmThread->javaVM;
J9JVMTIThreadData **data = NULL;
Assert_JVMTI_notNull(thread);

data = (J9JVMTIThreadData **)J9OBJECT_ADDRESS_LOAD_VM(vm, thread, vm->tlsOffset);
Assert_JVMTI_notNull(data);

return data[key - 1];
#else /* JAVA_SPEC_VERSION >= 19 */
return (J9JVMTIThreadData *)omrthread_tls_get(vmThread->osThread, key);
#endif /* JAVA_SPEC_VERSION >= 19 */
}

0 comments on commit c14fe74

Please sign in to comment.