31
31
import java .util .Arrays ;
32
32
import java .util .concurrent .ConcurrentHashMap ;
33
33
import java .util .concurrent .Semaphore ;
34
+ import java .util .concurrent .atomic .AtomicReference ;
34
35
import java .util .logging .Level ;
35
36
36
37
import org .graalvm .options .OptionDescriptors ;
@@ -158,7 +159,7 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
158
159
* {@link com.oracle.graal.python.nodes.expression.InplaceArithmetic}) to call targets. Use this
159
160
* map to retrieve a singleton instance (per engine) such that proper AST sharing is possible.
160
161
*/
161
- private ConcurrentHashMap <Object , WeakReference <RootCallTarget >> arithmeticOperatorCallTargetCache ;
162
+ private final AtomicReference < ConcurrentHashMap <Object , WeakReference <RootCallTarget >>> arithmeticOpCallTargetCacheRef = new AtomicReference <>() ;
162
163
163
164
private final Shape emptyShape = Shape .newBuilder ().allowImplicitCastIntToDouble (false ).allowImplicitCastIntToLong (true ).shapeFlags (0 ).propertyAssumptions (true ).build ();
164
165
@ CompilationFinal (dimensions = 1 ) private final Shape [] builtinTypeInstanceShapes = new Shape [PythonBuiltinClassType .VALUES .length ];
@@ -696,6 +697,7 @@ public Shape getBuiltinTypeInstanceShape(PythonBuiltinClassType type) {
696
697
* target exists yet, it will be created lazily. This method is thread-safe and should be used
697
698
* for all contexts in this engine to enable AST sharing.
698
699
*/
700
+ @ TruffleBoundary
699
701
public RootCallTarget getOrCreateUnaryArithmeticCallTarget (UnaryArithmetic unaryOperator ) {
700
702
return getOrCreateArithmeticCallTarget (unaryOperator , unaryOperator ::createCallTarget );
701
703
}
@@ -705,6 +707,7 @@ public RootCallTarget getOrCreateUnaryArithmeticCallTarget(UnaryArithmetic unary
705
707
* target exists yet, it will be created lazily. This method is thread-safe and should be used
706
708
* for all contexts in this engine to enable AST sharing.
707
709
*/
710
+ @ TruffleBoundary
708
711
public RootCallTarget getOrCreateBinaryArithmeticCallTarget (BinaryArithmetic unaryOperator ) {
709
712
return getOrCreateArithmeticCallTarget (unaryOperator , unaryOperator ::createCallTarget );
710
713
}
@@ -714,6 +717,7 @@ public RootCallTarget getOrCreateBinaryArithmeticCallTarget(BinaryArithmetic una
714
717
* target exists yet, it will be created lazily. This method is thread-safe and should be used
715
718
* for all contexts in this engine to enable AST sharing.
716
719
*/
720
+ @ TruffleBoundary
717
721
public RootCallTarget getOrCreateTernaryArithmeticCallTarget (TernaryArithmetic unaryOperator ) {
718
722
return getOrCreateArithmeticCallTarget (unaryOperator , unaryOperator ::createCallTarget );
719
723
}
@@ -723,20 +727,26 @@ public RootCallTarget getOrCreateTernaryArithmeticCallTarget(TernaryArithmetic u
723
727
* target exists yet, it will be created lazily. This method is thread-safe and should be used
724
728
* for all contexts in this engine to enable AST sharing.
725
729
*/
730
+ @ TruffleBoundary
726
731
public RootCallTarget getOrCreateInplaceArithmeticCallTarget (InplaceArithmetic unaryOperator ) {
727
732
return getOrCreateArithmeticCallTarget (unaryOperator , unaryOperator ::createCallTarget );
728
733
}
729
734
730
735
private RootCallTarget getOrCreateArithmeticCallTarget (Object arithmeticOperator , Function <PythonLanguage , RootCallTarget > supplier ) {
731
- if (arithmeticOperatorCallTargetCache == null ) {
732
- synchronized (this ) {
733
- // need to do check a second time (now synchronized)
734
- if (arithmeticOperatorCallTargetCache == null ) {
735
- arithmeticOperatorCallTargetCache = new ConcurrentHashMap <>();
736
+ CompilerAsserts .neverPartOfCompilation ();
737
+ ConcurrentHashMap <Object , WeakReference <RootCallTarget >> arithmeticOpCallTargetCache = arithmeticOpCallTargetCacheRef .get ();
738
+ if (arithmeticOpCallTargetCache == null ) {
739
+ arithmeticOpCallTargetCache = arithmeticOpCallTargetCacheRef .updateAndGet ((v ) -> {
740
+ // IMPORTANT: only create a new instance if we still see 'null'; otherwise we would
741
+ // overwrite the update of a different thread
742
+ if (v == null ) {
743
+ return new ConcurrentHashMap <>();
736
744
}
737
- }
745
+ return v ;
746
+ });
738
747
}
739
- WeakReference <RootCallTarget > ctRef = arithmeticOperatorCallTargetCache .compute (arithmeticOperator , (k , v ) -> {
748
+
749
+ WeakReference <RootCallTarget > ctRef = arithmeticOpCallTargetCache .compute (arithmeticOperator , (k , v ) -> {
740
750
RootCallTarget cachedCallTarget = v != null ? v .get () : null ;
741
751
if (cachedCallTarget == null ) {
742
752
return new WeakReference <>(supplier .apply (this ));
@@ -752,7 +762,7 @@ private RootCallTarget getOrCreateArithmeticCallTarget(Object arithmeticOperator
752
762
// the map.
753
763
final RootCallTarget callTargetToCache = supplier .apply (this );
754
764
callTarget = callTargetToCache ;
755
- arithmeticOperatorCallTargetCache .computeIfAbsent (arithmeticOperator , (k ) -> new WeakReference <>(callTargetToCache ));
765
+ arithmeticOpCallTargetCache .computeIfAbsent (arithmeticOperator , (k ) -> new WeakReference <>(callTargetToCache ));
756
766
}
757
767
assert callTarget != null ;
758
768
return callTarget ;
0 commit comments