3131import java .util .Map ;
3232import java .util .Objects ;
3333import java .util .concurrent .ThreadFactory ;
34- import java .util .function .BooleanSupplier ;
3534
3635import org .graalvm .compiler .api .directives .GraalDirectives ;
3736import org .graalvm .compiler .replacements .ReplacementsUtil ;
5958import com .oracle .svm .core .jdk .JDK17OrLater ;
6059import com .oracle .svm .core .jdk .JDK19OrEarlier ;
6160import com .oracle .svm .core .jdk .JDK19OrLater ;
61+ import com .oracle .svm .core .jdk .JDK20OrLater ;
6262import com .oracle .svm .core .jdk .LoomJDK ;
6363import com .oracle .svm .core .jdk .NotLoomJDK ;
6464import com .oracle .svm .core .monitor .MonitorSupport ;
6565import com .oracle .svm .core .util .VMError ;
66- import com .oracle .svm .util .ReflectionUtil ;
6766
6867@ TargetClass (Thread .class )
6968@ SuppressWarnings ({"unused" })
@@ -80,6 +79,10 @@ public final class Target_java_lang_Thread {
8079 @ Alias //
8180 @ TargetElement (onlyWith = JDK19OrLater .class ) //
8281 static int NO_INHERIT_THREAD_LOCALS ;
82+
83+ @ Alias //
84+ @ TargetElement (onlyWith = JDK20OrLater .class ) //
85+ static Object NEW_THREAD_BINDINGS ;
8386 // Checkstyle: resume
8487
8588 /** This field is initialized when the thread actually starts executing. */
@@ -190,10 +193,13 @@ public final class Target_java_lang_Thread {
190193 @ RecomputeFieldValue (kind = RecomputeFieldValue .Kind .Reset ) //
191194 Object lockHelper ;
192195
193- @ Inject @ TargetElement (onlyWith = { HasScopedValueCache .class , HasExtentLocalCache . class , LoomJDK . class } ) //
196+ @ Inject @ TargetElement (onlyWith = JDK19OrLater .class ) //
194197 @ RecomputeFieldValue (kind = RecomputeFieldValue .Kind .Reset ) //
195198 Object [] scopedValueCache ;
196199
200+ @ Alias @ TargetElement (onlyWith = JDK20OrLater .class ) //
201+ Object scopedValueBindings ;
202+
197203 @ Alias
198204 @ Platforms (InternalPlatform .NATIVE_ONLY .class )
199205 native void setPriority (int newPriority );
@@ -370,6 +376,10 @@ private Target_java_lang_Thread(
370376 boolean allowThreadLocals = (characteristics & NO_THREAD_LOCALS ) == 0 ;
371377 boolean inheritThreadLocals = (characteristics & NO_INHERIT_THREAD_LOCALS ) == 0 ;
372378 JavaThreads .initializeNewThread (this , g , target , nameLocal , stackSize , acc , allowThreadLocals , inheritThreadLocals );
379+
380+ if (JavaVersionUtil .JAVA_SPEC >= 20 ) {
381+ this .scopedValueBindings = NEW_THREAD_BINDINGS ;
382+ }
373383 }
374384
375385 @ Substitute
@@ -394,6 +404,10 @@ private Target_java_lang_Thread(String name, int characteristics, boolean bound)
394404 boolean allowThreadLocals = (characteristics & NO_THREAD_LOCALS ) == 0 ;
395405 boolean inheritThreadLocals = (characteristics & NO_INHERIT_THREAD_LOCALS ) == 0 ;
396406 JavaThreads .initNewThreadLocalsAndLoader (this , allowThreadLocals , inheritThreadLocals , Thread .currentThread ());
407+
408+ if (JavaVersionUtil .JAVA_SPEC >= 20 ) {
409+ this .scopedValueBindings = NEW_THREAD_BINDINGS ;
410+ }
397411 }
398412
399413 @ SuppressWarnings ("hiding" )
@@ -665,29 +679,44 @@ static Thread startVirtualThreadWithoutLoom(Runnable task) {
665679 }
666680
667681 @ Substitute
668- @ TargetElement (onlyWith = HasExtentLocalCache .class )
682+ @ TargetElement (onlyWith = { JDK19OrLater .class , JDK19OrEarlier . class } )
669683 static Object [] extentLocalCache () {
670684 return JavaThreads .toTarget (currentCarrierThread ()).scopedValueCache ;
671685 }
672686
673687 @ Substitute
674- @ TargetElement (onlyWith = HasExtentLocalCache .class )
688+ @ TargetElement (onlyWith = { JDK19OrLater .class , JDK19OrEarlier . class } )
675689 static void setExtentLocalCache (Object [] cache ) {
676690 JavaThreads .toTarget (currentCarrierThread ()).scopedValueCache = cache ;
677691 }
678692
679693 @ Substitute
680- @ TargetElement (onlyWith = HasScopedValueCache .class )
694+ @ TargetElement (onlyWith = JDK20OrLater .class )
681695 static Object [] scopedValueCache () {
682696 return JavaThreads .toTarget (currentCarrierThread ()).scopedValueCache ;
683697 }
684698
685699 @ Substitute
686- @ TargetElement (onlyWith = HasScopedValueCache .class )
700+ @ TargetElement (onlyWith = JDK20OrLater .class )
687701 static void setScopedValueCache (Object [] cache ) {
688702 JavaThreads .toTarget (currentCarrierThread ()).scopedValueCache = cache ;
689703 }
690704
705+ @ Substitute
706+ @ TargetElement (onlyWith = JDK20OrLater .class )
707+ static Object findScopedValueBindings () {
708+ /*
709+ * We don't have the means to extract the bindings object parameter from runWith frames on
710+ * the stack like HotSpot does. However, at this time, we need to support only two cases:
711+ * current bindings in a virtual thread, and current bindings in the carrier thread.
712+ */
713+ Object bindings = JavaThreads .toTarget (Thread .currentThread ()).scopedValueBindings ;
714+ if (bindings != null ) {
715+ return bindings ;
716+ }
717+ return JavaThreads .toTarget (currentCarrierThread ()).scopedValueBindings ;
718+ }
719+
691720 @ Substitute
692721 static void blockedOn (Target_sun_nio_ch_Interruptible b ) {
693722 JavaThreads .blockedOn (b );
@@ -725,22 +754,6 @@ boolean isTerminated() {
725754 @ Alias
726755 @ TargetElement (onlyWith = JDK19OrLater .class )
727756 native long threadId ();
728-
729- private static class HasExtentLocalCache implements BooleanSupplier {
730- @ Override
731- public boolean getAsBoolean () {
732- boolean result = ReflectionUtil .lookupMethod (true , Thread .class , "extentLocalCache" ) != null ;
733- assert !result || JavaVersionUtil .JAVA_SPEC == 19 : "extentLocalCache should not exist as of JDK 20" ;
734- return result ;
735- }
736- }
737-
738- public static class HasScopedValueCache implements BooleanSupplier {
739- @ Override
740- public boolean getAsBoolean () {
741- return ReflectionUtil .lookupMethod (true , Thread .class , "scopedValueCache" ) != null ;
742- }
743- }
744757}
745758
746759@ TargetClass (value = Thread .class , innerClass = "Builder" , onlyWith = JDK19OrLater .class )
0 commit comments