26
26
27
27
package java .lang ;
28
28
29
+ import java .lang .ref .Reference ;
29
30
import java .util .NoSuchElementException ;
30
31
import java .util .Objects ;
31
- import java .lang .ref .Reference ;
32
- import java .util .concurrent .StructuredTaskScope ;
33
32
import java .util .concurrent .StructureViolationException ;
33
+ import java .util .concurrent .StructuredTaskScope ;
34
+ import java .util .function .IntSupplier ;
34
35
import java .util .function .Supplier ;
35
36
import jdk .internal .access .JavaUtilConcurrentTLRAccess ;
36
37
import jdk .internal .access .SharedSecrets ;
38
+ import jdk .internal .vm .ScopedValueContainer ;
37
39
import jdk .internal .vm .annotation .ForceInline ;
38
40
import jdk .internal .vm .annotation .Hidden ;
39
- import jdk .internal .vm .ScopedValueContainer ;
41
+ import jdk .internal .vm .annotation . Stable ;
40
42
41
43
/**
42
44
* A value that may be safely and efficiently shared to methods without using method
@@ -244,6 +246,9 @@ public final class ScopedValue<T> {
244
246
@ Override
245
247
public int hashCode () { return hash ; }
246
248
249
+ @ Stable
250
+ static IntSupplier hashGenerator ;
251
+
247
252
/**
248
253
* An immutable map from {@code ScopedValue} to values.
249
254
*
@@ -526,7 +531,8 @@ public static <T> Carrier where(ScopedValue<T> key, T value) {
526
531
}
527
532
528
533
private ScopedValue () {
529
- this .hash = generateKey ();
534
+ IntSupplier nextHash = hashGenerator ;
535
+ this .hash = nextHash != null ? nextHash .getAsInt () : generateKey ();
530
536
}
531
537
532
538
/**
@@ -552,11 +558,11 @@ public T get() {
552
558
// This code should perhaps be in class Cache. We do it
553
559
// here because the generated code is small and fast and
554
560
// we really want it to be inlined in the caller.
555
- int n = (hash & Cache .SLOT_MASK ) * 2 ;
561
+ int n = (hash & Cache .Constants . SLOT_MASK ) * 2 ;
556
562
if (objects [n ] == this ) {
557
563
return (T )objects [n + 1 ];
558
564
}
559
- n = ((hash >>> Cache .INDEX_BITS ) & Cache .SLOT_MASK ) * 2 ;
565
+ n = ((hash >>> Cache .INDEX_BITS ) & Cache .Constants . SLOT_MASK ) * 2 ;
560
566
if (objects [n ] == this ) {
561
567
return (T )objects [n + 1 ];
562
568
}
@@ -580,11 +586,11 @@ private T slowGet() {
580
586
public boolean isBound () {
581
587
Object [] objects = scopedValueCache ();
582
588
if (objects != null ) {
583
- int n = (hash & Cache .SLOT_MASK ) * 2 ;
589
+ int n = (hash & Cache .Constants . SLOT_MASK ) * 2 ;
584
590
if (objects [n ] == this ) {
585
591
return true ;
586
592
}
587
- n = ((hash >>> Cache .INDEX_BITS ) & Cache .SLOT_MASK ) * 2 ;
593
+ n = ((hash >>> Cache .INDEX_BITS ) & Cache .Constants . SLOT_MASK ) * 2 ;
588
594
if (objects [n ] == this ) {
589
595
return true ;
590
596
}
@@ -688,17 +694,17 @@ private static Snapshot scopedValueBindings() {
688
694
689
695
private static int nextKey = 0xf0f0_f0f0 ;
690
696
691
- // A Marsaglia xor-shift generator used to generate hashes. This one has full period, so
692
- // it generates 2**32 - 1 hashes before it repeats. We're going to use the lowest n bits
693
- // and the next n bits as cache indexes, so we make sure that those indexes map
694
- // to different slots in the cache.
697
+ // A Marsaglia xor-shift generator used to generate hashes. This one has
698
+ // full period, so it generates 2**32 - 1 hashes before it repeats. We're
699
+ // going to use the lowest n bits and the next n bits as cache indexes, so
700
+ // we make sure that those indexes map to different slots in the cache.
695
701
private static synchronized int generateKey () {
696
702
int x = nextKey ;
697
703
do {
698
704
x ^= x >>> 12 ;
699
705
x ^= x << 9 ;
700
706
x ^= x >>> 23 ;
701
- } while (Cache .primarySlot (x ) == Cache .secondarySlot (x ));
707
+ } while ((( Cache .primaryIndex (x ) ^ Cache .secondaryIndex (x )) & 1 ) == 0 );
702
708
return (nextKey = x );
703
709
}
704
710
@@ -709,7 +715,7 @@ private static synchronized int generateKey() {
709
715
* @return the bitmask
710
716
*/
711
717
int bitmask () {
712
- return (1 << Cache .primaryIndex (this )) | (1 << (Cache .secondaryIndex (this ) + Cache .TABLE_SIZE ));
718
+ return (1 << Cache .primaryIndex (hash )) | (1 << (Cache .secondaryIndex (hash ) + Cache .TABLE_SIZE ));
713
719
}
714
720
715
721
// Return true iff bitmask, considered as a set of bits, contains all
@@ -727,57 +733,100 @@ private static final class Cache {
727
733
static final int TABLE_MASK = TABLE_SIZE - 1 ;
728
734
static final int PRIMARY_MASK = (1 << TABLE_SIZE ) - 1 ;
729
735
730
- // The number of elements in the cache array, and a bit mask used to
731
- // select elements from it.
732
- private static final int CACHE_TABLE_SIZE , SLOT_MASK ;
733
- // The largest cache we allow. Must be a power of 2 and greater than
734
- // or equal to 2.
735
- private static final int MAX_CACHE_SIZE = 16 ;
736
-
737
- static {
738
- final String propertyName = "java.lang.ScopedValue.cacheSize" ;
739
- var sizeString = System .getProperty (propertyName , "16" );
740
- var cacheSize = Integer .valueOf (sizeString );
741
- if (cacheSize < 2 || cacheSize > MAX_CACHE_SIZE ) {
742
- cacheSize = MAX_CACHE_SIZE ;
743
- System .err .println (propertyName + " is out of range: is " + sizeString );
744
- }
745
- if ((cacheSize & (cacheSize - 1 )) != 0 ) { // a power of 2
746
- cacheSize = MAX_CACHE_SIZE ;
747
- System .err .println (propertyName + " must be an integer power of 2: is " + sizeString );
736
+
737
+ // This class serves to defer initialization of some values until they
738
+ // are needed. In particular, we must not invoke System.getProperty
739
+ // early in the JDK boot process, because that leads to a circular class
740
+ // initialization dependency.
741
+ //
742
+ // In more detail:
743
+ //
744
+ // The size of the cache depends on System.getProperty. Generating the
745
+ // hash of an instance of ScopedValue depends on ThreadLocalRandom.
746
+ //
747
+ // Invoking either of these early in the JDK boot process will cause
748
+ // startup to fail with an unrecoverable circular dependency.
749
+ //
750
+ // To break these cycles we allow scoped values to be created (but not
751
+ // used) without invoking either System.getProperty or
752
+ // ThreadLocalRandom. To do this we defer querying System.getProperty
753
+ // until the first reference to CACHE_TABLE_SIZE, and we define a local
754
+ // hash generator which is used until CACHE_TABLE_SIZE is initialized.
755
+
756
+ private static class Constants {
757
+ // The number of elements in the cache array, and a bit mask used to
758
+ // select elements from it.
759
+ private static final int CACHE_TABLE_SIZE , SLOT_MASK ;
760
+ // The largest cache we allow. Must be a power of 2 and greater than
761
+ // or equal to 2.
762
+ private static final int MAX_CACHE_SIZE = 16 ;
763
+
764
+ private static final JavaUtilConcurrentTLRAccess THREAD_LOCAL_RANDOM_ACCESS
765
+ = SharedSecrets .getJavaUtilConcurrentTLRAccess ();
766
+
767
+ static {
768
+ final String propertyName = "java.lang.ScopedValue.cacheSize" ;
769
+ var sizeString = System .getProperty (propertyName , "16" );
770
+ var cacheSize = Integer .valueOf (sizeString );
771
+ if (cacheSize < 2 || cacheSize > MAX_CACHE_SIZE ) {
772
+ cacheSize = MAX_CACHE_SIZE ;
773
+ System .err .println (propertyName + " is out of range: is " + sizeString );
774
+ }
775
+ if ((cacheSize & (cacheSize - 1 )) != 0 ) { // a power of 2
776
+ cacheSize = MAX_CACHE_SIZE ;
777
+ System .err .println (propertyName + " must be an integer power of 2: is " + sizeString );
778
+ }
779
+ CACHE_TABLE_SIZE = cacheSize ;
780
+ SLOT_MASK = cacheSize - 1 ;
781
+
782
+ // hashGenerator is set here (in class Constants rather than
783
+ // in global scope) in order not to initialize
784
+ // j.u.c.ThreadLocalRandom early in the JDK boot process.
785
+ // After this static initialization, new instances of
786
+ // ScopedValue will be initialized by a thread-local random
787
+ // generator.
788
+ hashGenerator = new IntSupplier () {
789
+ @ Override
790
+ public int getAsInt () {
791
+ int x ;
792
+ do {
793
+ x = THREAD_LOCAL_RANDOM_ACCESS
794
+ .nextSecondaryThreadLocalRandomSeed ();
795
+ } while (Cache .primarySlot (x ) == Cache .secondarySlot (x ));
796
+ return x ;
797
+ }
798
+ };
748
799
}
749
- CACHE_TABLE_SIZE = cacheSize ;
750
- SLOT_MASK = cacheSize - 1 ;
751
800
}
752
801
753
- static int primaryIndex (ScopedValue <?> key ) {
754
- return key . hash & TABLE_MASK ;
802
+ static int primaryIndex (int hash ) {
803
+ return hash & Cache . TABLE_MASK ;
755
804
}
756
805
757
- static int secondaryIndex (ScopedValue <?> key ) {
758
- return (key . hash >> INDEX_BITS ) & TABLE_MASK ;
806
+ static int secondaryIndex (int hash ) {
807
+ return (hash >> INDEX_BITS ) & Cache . TABLE_MASK ;
759
808
}
760
809
761
810
private static int primarySlot (ScopedValue <?> key ) {
762
- return key .hashCode () & SLOT_MASK ;
811
+ return key .hashCode () & Constants . SLOT_MASK ;
763
812
}
764
813
765
814
private static int secondarySlot (ScopedValue <?> key ) {
766
- return (key .hash >> INDEX_BITS ) & SLOT_MASK ;
815
+ return (key .hash >> INDEX_BITS ) & Constants . SLOT_MASK ;
767
816
}
768
817
769
818
static int primarySlot (int hash ) {
770
- return hash & SLOT_MASK ;
819
+ return hash & Constants . SLOT_MASK ;
771
820
}
772
821
773
822
static int secondarySlot (int hash ) {
774
- return (hash >> INDEX_BITS ) & SLOT_MASK ;
823
+ return (hash >> INDEX_BITS ) & Constants . SLOT_MASK ;
775
824
}
776
825
777
826
static void put (ScopedValue <?> key , Object value ) {
778
827
Object [] theCache = scopedValueCache ();
779
828
if (theCache == null ) {
780
- theCache = new Object [CACHE_TABLE_SIZE * 2 ];
829
+ theCache = new Object [Constants . CACHE_TABLE_SIZE * 2 ];
781
830
setScopedValueCache (theCache );
782
831
}
783
832
// Update the cache to replace one entry with the value we just looked up.
@@ -813,26 +862,23 @@ private static void setKey(Object[] objs, int n, Object key) {
813
862
objs [n * 2 ] = key ;
814
863
}
815
864
816
- private static final JavaUtilConcurrentTLRAccess THREAD_LOCAL_RANDOM_ACCESS
817
- = SharedSecrets .getJavaUtilConcurrentTLRAccess ();
818
-
819
865
// Return either true or false, at pseudo-random, with a bias towards true.
820
866
// This chooses either the primary or secondary cache slot, but the
821
867
// primary slot is approximately twice as likely to be chosen as the
822
868
// secondary one.
823
869
private static boolean chooseVictim () {
824
- int r = THREAD_LOCAL_RANDOM_ACCESS .nextSecondaryThreadLocalRandomSeed ();
870
+ int r = Constants . THREAD_LOCAL_RANDOM_ACCESS .nextSecondaryThreadLocalRandomSeed ();
825
871
return (r & 15 ) >= 5 ;
826
872
}
827
873
828
874
// Null a set of cache entries, indicated by the 1-bits given
829
875
static void invalidate (int toClearBits ) {
830
- toClearBits = (toClearBits >>> TABLE_SIZE ) | ( toClearBits & PRIMARY_MASK ) ;
876
+ toClearBits = (( toClearBits >>> Cache . TABLE_SIZE ) | toClearBits ) & PRIMARY_MASK ;
831
877
Object [] objects ;
832
878
if ((objects = scopedValueCache ()) != null ) {
833
879
for (int bits = toClearBits ; bits != 0 ; ) {
834
880
int index = Integer .numberOfTrailingZeros (bits );
835
- setKeyAndObjectAt (objects , index & SLOT_MASK , null , null );
881
+ setKeyAndObjectAt (objects , index & Constants . SLOT_MASK , null , null );
836
882
bits &= ~1 << index ;
837
883
}
838
884
}
0 commit comments