Skip to content

Commit a9b4735

Browse files
committed
Use AtomicReference to lazy init concurrent CT cache
1 parent f6d3305 commit a9b4735

File tree

1 file changed

+19
-9
lines changed

1 file changed

+19
-9
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.Arrays;
3232
import java.util.concurrent.ConcurrentHashMap;
3333
import java.util.concurrent.Semaphore;
34+
import java.util.concurrent.atomic.AtomicReference;
3435
import java.util.logging.Level;
3536

3637
import org.graalvm.options.OptionDescriptors;
@@ -158,7 +159,7 @@ public final class PythonLanguage extends TruffleLanguage<PythonContext> {
158159
* {@link com.oracle.graal.python.nodes.expression.InplaceArithmetic}) to call targets. Use this
159160
* map to retrieve a singleton instance (per engine) such that proper AST sharing is possible.
160161
*/
161-
private ConcurrentHashMap<Object, WeakReference<RootCallTarget>> arithmeticOperatorCallTargetCache;
162+
private final AtomicReference<ConcurrentHashMap<Object, WeakReference<RootCallTarget>>> arithmeticOpCallTargetCacheRef = new AtomicReference<>();
162163

163164
private final Shape emptyShape = Shape.newBuilder().allowImplicitCastIntToDouble(false).allowImplicitCastIntToLong(true).shapeFlags(0).propertyAssumptions(true).build();
164165
@CompilationFinal(dimensions = 1) private final Shape[] builtinTypeInstanceShapes = new Shape[PythonBuiltinClassType.VALUES.length];
@@ -696,6 +697,7 @@ public Shape getBuiltinTypeInstanceShape(PythonBuiltinClassType type) {
696697
* target exists yet, it will be created lazily. This method is thread-safe and should be used
697698
* for all contexts in this engine to enable AST sharing.
698699
*/
700+
@TruffleBoundary
699701
public RootCallTarget getOrCreateUnaryArithmeticCallTarget(UnaryArithmetic unaryOperator) {
700702
return getOrCreateArithmeticCallTarget(unaryOperator, unaryOperator::createCallTarget);
701703
}
@@ -705,6 +707,7 @@ public RootCallTarget getOrCreateUnaryArithmeticCallTarget(UnaryArithmetic unary
705707
* target exists yet, it will be created lazily. This method is thread-safe and should be used
706708
* for all contexts in this engine to enable AST sharing.
707709
*/
710+
@TruffleBoundary
708711
public RootCallTarget getOrCreateBinaryArithmeticCallTarget(BinaryArithmetic unaryOperator) {
709712
return getOrCreateArithmeticCallTarget(unaryOperator, unaryOperator::createCallTarget);
710713
}
@@ -714,6 +717,7 @@ public RootCallTarget getOrCreateBinaryArithmeticCallTarget(BinaryArithmetic una
714717
* target exists yet, it will be created lazily. This method is thread-safe and should be used
715718
* for all contexts in this engine to enable AST sharing.
716719
*/
720+
@TruffleBoundary
717721
public RootCallTarget getOrCreateTernaryArithmeticCallTarget(TernaryArithmetic unaryOperator) {
718722
return getOrCreateArithmeticCallTarget(unaryOperator, unaryOperator::createCallTarget);
719723
}
@@ -723,20 +727,26 @@ public RootCallTarget getOrCreateTernaryArithmeticCallTarget(TernaryArithmetic u
723727
* target exists yet, it will be created lazily. This method is thread-safe and should be used
724728
* for all contexts in this engine to enable AST sharing.
725729
*/
730+
@TruffleBoundary
726731
public RootCallTarget getOrCreateInplaceArithmeticCallTarget(InplaceArithmetic unaryOperator) {
727732
return getOrCreateArithmeticCallTarget(unaryOperator, unaryOperator::createCallTarget);
728733
}
729734

730735
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<>();
736744
}
737-
}
745+
return v;
746+
});
738747
}
739-
WeakReference<RootCallTarget> ctRef = arithmeticOperatorCallTargetCache.compute(arithmeticOperator, (k, v) -> {
748+
749+
WeakReference<RootCallTarget> ctRef = arithmeticOpCallTargetCache.compute(arithmeticOperator, (k, v) -> {
740750
RootCallTarget cachedCallTarget = v != null ? v.get() : null;
741751
if (cachedCallTarget == null) {
742752
return new WeakReference<>(supplier.apply(this));
@@ -752,7 +762,7 @@ private RootCallTarget getOrCreateArithmeticCallTarget(Object arithmeticOperator
752762
// the map.
753763
final RootCallTarget callTargetToCache = supplier.apply(this);
754764
callTarget = callTargetToCache;
755-
arithmeticOperatorCallTargetCache.computeIfAbsent(arithmeticOperator, (k) -> new WeakReference<>(callTargetToCache));
765+
arithmeticOpCallTargetCache.computeIfAbsent(arithmeticOperator, (k) -> new WeakReference<>(callTargetToCache));
756766
}
757767
assert callTarget != null;
758768
return callTarget;

0 commit comments

Comments
 (0)