Skip to content

Commit 63074d3

Browse files
maksim-grebeniuk-sonarsourcesonartech
authored andcommitted
SONARPY-2809 Make LazyType resolution thread safe (#213)
GitOrigin-RevId: 54a5d3b4c2f95c64ce859d621c715902ee54b735
1 parent 16c48d7 commit 63074d3

3 files changed

Lines changed: 19 additions & 16 deletions

File tree

python-frontend/src/main/java/org/sonar/python/semantic/v2/LazyTypesContext.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.HashMap;
2020
import java.util.Map;
21+
import java.util.concurrent.ConcurrentHashMap;
2122
import org.sonar.python.types.v2.LazyType;
2223
import org.sonar.python.types.v2.LazyTypeWrapper;
2324
import org.sonar.plugins.python.api.types.v2.PythonType;
@@ -28,7 +29,7 @@ public class LazyTypesContext {
2829
private final TypeTable typeTable;
2930

3031
public LazyTypesContext(ProjectLevelTypeTable typeTable) {
31-
this.lazyTypes = new HashMap<>();
32+
this.lazyTypes = new ConcurrentHashMap<>();
3233
this.typeTable = typeTable;
3334
}
3435

@@ -37,12 +38,7 @@ public TypeWrapper getOrCreateLazyTypeWrapper(String importPath) {
3738
}
3839

3940
public LazyType getOrCreateLazyType(String importPath) {
40-
if (lazyTypes.containsKey(importPath)) {
41-
return lazyTypes.get(importPath);
42-
}
43-
var lazyType = new LazyType(importPath, this);
44-
lazyTypes.put(importPath, lazyType);
45-
return lazyType;
41+
return lazyTypes.computeIfAbsent(importPath, ip -> new LazyType(ip, this));
4642
}
4743

4844
public PythonType resolveLazyType(LazyType lazyType) {

python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/FunctionDescriptorToPythonTypeConverter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.sonar.python.semantic.v2.converter;
1818

1919
import java.util.List;
20+
import java.util.Objects;
2021
import java.util.Optional;
2122
import org.sonar.python.index.Descriptor;
2223
import org.sonar.python.index.FunctionDescriptor;
@@ -49,6 +50,7 @@ public PythonType convert(ConversionContext ctx, FunctionDescriptor from) {
4950

5051
var decorators = from.decorators()
5152
.stream()
53+
.filter(Objects::nonNull) // SONARPY-2813 needs to be done to fix the null decorators
5254
.map(ctx.lazyTypesContext()::getOrCreateLazyType)
5355
.map(TypeWrapper::of)
5456
.toList();

python-frontend/src/main/java/org/sonar/python/types/v2/LazyType.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
*/
1717
package org.sonar.python.types.v2;
1818

19-
import java.util.ArrayDeque;
19+
import java.util.ArrayList;
2020
import java.util.Optional;
21-
import java.util.Queue;
21+
import java.util.concurrent.BlockingQueue;
22+
import java.util.concurrent.LinkedBlockingQueue;
2223
import java.util.function.Consumer;
2324
import org.sonar.plugins.python.api.LocationInFile;
2425
import org.sonar.plugins.python.api.types.v2.PythonType;
@@ -29,14 +30,14 @@
2930
public class LazyType implements PythonType, ResolvableType {
3031

3132
String importPath;
32-
private final Queue<Consumer<PythonType>> consumers;
33+
private final BlockingQueue<Consumer<PythonType>> consumers;
3334
private final LazyTypesContext lazyTypesContext;
3435
private static final String INTERACTION_MESSAGE = "Lazy types should not be interacted with.";
3536

3637
public LazyType(String importPath, LazyTypesContext lazyTypesContext) {
3738
this.importPath = importPath;
3839
this.lazyTypesContext = lazyTypesContext;
39-
consumers = new ArrayDeque<>();
40+
consumers = new LinkedBlockingQueue<>();
4041
}
4142

4243
public String importPath() {
@@ -49,18 +50,22 @@ public LazyType addConsumer(Consumer<PythonType> consumer) {
4950
}
5051

5152
public LazyType resolve(PythonType type) {
52-
consumers.forEach(c -> c.accept(type));
53-
consumers.clear();
53+
notifyConsumers(type);
5454
return this;
5555
}
5656

57-
public PythonType resolve() {
57+
public synchronized PythonType resolve() {
5858
PythonType resolvedType = lazyTypesContext.resolveLazyType(this);
59-
consumers.forEach(c -> c.accept(resolvedType));
60-
consumers.clear();
59+
notifyConsumers(resolvedType);
6160
return resolvedType;
6261
}
6362

63+
private void notifyConsumers(PythonType type) {
64+
var toNotify = new ArrayList<Consumer<PythonType>>();
65+
consumers.drainTo(toNotify);
66+
toNotify.forEach(c -> c.accept(type));
67+
}
68+
6469
@Override
6570
public PythonType unwrappedType() {
6671
throw new IllegalStateException(INTERACTION_MESSAGE);

0 commit comments

Comments
 (0)