Skip to content

Commit e57519e

Browse files
thalliumbabsingh
authored andcommitted
Implement Thread.findScopedValueBindings()
Walk the stack frames to look for the runWith() method and get its first argument, which should be an instance of java.lang.ScopedValue$Snapshot. Closes: eclipse-openj9#16677 Closes: eclipse-openj9#17402 Co-authored-by: Gengchen Tuo gengchen.tuo@ibm.com Signed-off-by: Babneet Singh sbabneet@ca.ibm.com
1 parent 6df98f6 commit e57519e

File tree

2 files changed

+126
-10
lines changed

2 files changed

+126
-10
lines changed

runtime/jcl/common/thread.cpp

+122-10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ extern "C" {
4444
#define TIMED_WAITING 4
4545
#define TERMINATED 5
4646

47+
#if JAVA_SPEC_VERSION >= 20
48+
typedef struct J9ScopedValueBindingsWalkClasses {
49+
J9Class *threadClass;
50+
J9Class *virtualThreadClass;
51+
J9Class *scopedValueCarrierClass;
52+
J9Class *scopedValueSnapshotClass;
53+
} J9ScopedValueBindingsWalkClasses;
54+
#endif /* JAVA_SPEC_VERSION >= 20 */
55+
4756
jint
4857
getJclThreadState(UDATA vmstate, jboolean started)
4958
{
@@ -603,28 +612,131 @@ Java_java_lang_Thread_setExtentLocalCache(JNIEnv *env, jclass unusedClass, jobje
603612
#endif /* JAVA_SPEC_VERSION >= 19 */
604613

605614
#if JAVA_SPEC_VERSION >= 20
615+
/**
616+
* Frame walk iterator to find the most recent scoped value bindings on the stack.
617+
*
618+
* @param currentThread the current thread
619+
* @param walkState the stack walk state
620+
* @return J9_STACKWALK_KEEP_ITERATING or J9_STACKWALK_STOP_ITERATING
621+
*/
622+
static UDATA
623+
findScopedValueBindingsWalkFunction(J9VMThread *currentThread, J9StackWalkState *walkState)
624+
{
625+
UDATA rc = J9_STACKWALK_KEEP_ITERATING;
626+
627+
if (NULL != walkState->userData1) {
628+
J9Method *method = walkState->method;
629+
J9Class *methodClass = J9_CLASS_FROM_METHOD(method);
630+
J9ROMMethod *romMethod = J9_ROM_METHOD_FROM_RAM_METHOD(method);
631+
J9UTF8 *methodName = J9ROMMETHOD_NAME(romMethod);
632+
J9ScopedValueBindingsWalkClasses *classes = (J9ScopedValueBindingsWalkClasses *)walkState->userData3;
633+
634+
if (((classes->threadClass == methodClass)
635+
|| (classes->virtualThreadClass == methodClass)
636+
|| (classes->scopedValueCarrierClass == methodClass))
637+
&& J9UTF8_LITERAL_EQUALS(J9UTF8_DATA(methodName), J9UTF8_LENGTH(methodName), "runWith")
638+
) {
639+
UDATA count = (UDATA)walkState->userData2;
640+
rc = J9_STACKWALK_STOP_ITERATING;
641+
/* runWith method should only have one instance of java.lang.ScopedValue$Snapshot. */
642+
Assert_JCL_true((NULL != walkState->userData1) && (1 == count));
643+
} else if (0 == walkState->inlineDepth) {
644+
/* Reset userData1 and userData2 after iterating all O-slots of a method. */
645+
walkState->userData1 = NULL;
646+
walkState->userData2 = (void *)0;
647+
}
648+
}
649+
650+
return rc;
651+
}
652+
653+
/**
654+
* O-slot iterator to find the most recent scoped value bindings in the object slots.
655+
*
656+
* @param currentThread the current thread
657+
* @param walkState the stack walk state
658+
* @param slot pointer to slot containing an object pointer
659+
* @param stackLocation pointer to the slot in the stack containing possibly compressed object pointer
660+
*/
661+
static void
662+
findScopedValueBindingsOSlotWalkFunction(J9VMThread *currentThread, J9StackWalkState *walkState, j9object_t *slot, const void *stackLocation)
663+
{
664+
j9object_t slotObject = *slot;
665+
J9Class *clazz = J9OBJECT_CLAZZ(currentThread, slotObject);
666+
J9ScopedValueBindingsWalkClasses *classes = (J9ScopedValueBindingsWalkClasses *)walkState->userData3;
667+
J9Class *snapshotClass = (J9Class *)classes->scopedValueSnapshotClass;
668+
669+
if (0 != isSameOrSuperClassOf(snapshotClass, clazz)) {
670+
UDATA count = (UDATA)walkState->userData2;
671+
walkState->userData1 = (void *)slotObject;
672+
walkState->userData2 = (void *)(count + 1);
673+
}
674+
}
675+
606676
/**
607677
* static native Object findScopedValueBindings();
608678
*
609-
* Find the most recent scoped value bindings in the stack.
610-
* TODO: Complete the function description.
679+
* Find the most recent scoped value bindings on the stack.
611680
*
612681
* @param env instance of JNIEnv
613682
* @param unusedClass
614-
* @return jobject
683+
* @return jobject the found scoped value bindings, or null if there are no
684+
* scoped value bindings on the stack
615685
*/
616686
jobject JNICALL
617687
Java_java_lang_Thread_findScopedValueBindings(JNIEnv *env, jclass unusedClass)
618688
{
619-
/* Currently, this method is unused since there is no API or test that
620-
* invokes this method in OpenJ9. If the assertion ever triggers, the
621-
* draft implementation below should be completed and merged.
689+
J9VMThread *currentThread = (J9VMThread *)env;
690+
J9JavaVM *vm = currentThread->javaVM;
691+
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
692+
J9StackWalkState walkState = {0};
693+
J9ScopedValueBindingsWalkClasses classes = {0};
694+
jobject bindings = NULL;
695+
696+
/* The goal is to find the first argument of the runWith method, which is an
697+
* instance of java.lang.ScopedValue$Snapshot. The first argument cannot be
698+
* reliably retrieved for compiled methods. There is supposed to be only one
699+
* argument, which is an instance of java.lang.ScopedValue$Snapshot.
622700
*
623-
* Issue: https://github.com/eclipse-openj9/openj9/issues/16677.
624-
* Implementation: https://github.com/eclipse-openj9/openj9/pull/17402.
701+
* The O-slot iterator is invoked before the frame iterator. So, all O-slots
702+
* are looked at until the runWith method is reached. For the runWith method,
703+
* there is an assertion which checks that only a single instance of
704+
* java.lang.ScopedValue$Snapshot is found. If the assertion triggers, it
705+
* will indicate that the Java implementation of the runWith method has been
706+
* modified.
625707
*/
626-
Assert_JCL_unimplemented();
627-
return NULL;
708+
walkState.walkThread = currentThread;
709+
walkState.skipCount = 0;
710+
walkState.userData1 = NULL;
711+
/* Counter to keep track of the java.lang.ScopedValue$Snapshot instances
712+
* encountered for a method.
713+
*/
714+
walkState.userData2 = (void *)0;
715+
walkState.frameWalkFunction = findScopedValueBindingsWalkFunction;
716+
walkState.objectSlotWalkFunction = findScopedValueBindingsOSlotWalkFunction;
717+
walkState.flags = J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_MAINTAIN_REGISTER_MAP
718+
| J9_STACKWALK_ITERATE_O_SLOTS | J9_STACKWALK_VISIBLE_ONLY;
719+
720+
vmFuncs->internalEnterVMFromJNI(currentThread);
721+
722+
/* Preload classes once, before walking the stack. Better for performance in
723+
* comparison to loading them multiple times within the stackwalk callbacks.
724+
*/
725+
classes.threadClass = J9VMJAVALANGTHREAD(vm);
726+
classes.virtualThreadClass = J9VMJAVALANGVIRTUALTHREAD(vm);
727+
classes.scopedValueCarrierClass = J9VMJAVALANGSCOPEDVALUECARRIER(vm);
728+
classes.scopedValueSnapshotClass = J9VMJAVALANGSCOPEDVALUESNAPSHOT(vm);
729+
730+
walkState.userData3 = (void *)&classes;
731+
732+
vm->walkStackFrames(currentThread, &walkState);
733+
734+
if (NULL != walkState.userData1) {
735+
bindings = VM_VMHelpers::createLocalRef(env, (j9object_t)walkState.userData1);
736+
}
737+
vmFuncs->internalExitVMToJNI(currentThread);
738+
739+
return bindings;
628740
}
629741

630742
/**

runtime/oti/vmconstantpool.xml

+4
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-ex
165165
<classref name="jdk/internal/foreign/AbstractMemorySegmentImpl" flags="opt_openjdkFfi" versions="17-"/>
166166
<classref name="jdk/internal/foreign/NativeMemorySegmentImpl" flags="opt_openjdkFfi" versions="17-"/>
167167

168+
<!-- Class references used in Thread.findScopedValueBindings(). -->
169+
<classref name="java/lang/ScopedValue$Carrier" versions="20-"/>
170+
<classref name="java/lang/ScopedValue$Snapshot" versions="20-"/>
171+
168172
<!--
169173
NOTE: the resolution code in jclcinit.c only looks at the J9ROMClassRef->runtimeFlags to determine
170174
whether or not to attempt the class load. The flags sepcified on the fields are ignored.

0 commit comments

Comments
 (0)