Skip to content

Commit 608e25c

Browse files
maksim-grebeniuk-sonarsourcesonartech
authored andcommitted
SONARPY-3140 Rework synchronization with concurrency package tools (#351)
GitOrigin-RevId: 7d721aacef118af22c4e3f2b44504aed198231c1
1 parent ab2dbb4 commit 608e25c

9 files changed

Lines changed: 122 additions & 67 deletions

File tree

python-commons/src/main/java/org/sonar/plugins/python/IssuesRepository.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.ArrayDeque;
2121
import java.util.List;
2222
import java.util.Optional;
23+
import java.util.concurrent.locks.Lock;
2324
import javax.annotation.CheckForNull;
2425
import org.slf4j.Logger;
2526
import org.slf4j.LoggerFactory;
@@ -41,14 +42,14 @@ public class IssuesRepository {
4142
private final PythonChecks checks;
4243
private final PythonIndexer indexer;
4344
private final boolean isInSonarLint;
44-
private final Object monitor;
45+
private final Lock lock;
4546

46-
public IssuesRepository(SensorContext context, PythonChecks checks, PythonIndexer indexer, boolean isInSonarLint, Object monitor) {
47+
public IssuesRepository(SensorContext context, PythonChecks checks, PythonIndexer indexer, boolean isInSonarLint, Lock lock) {
4748
this.context = context;
4849
this.checks = checks;
4950
this.indexer = indexer;
5051
this.isInSonarLint = isInSonarLint;
51-
this.monitor = monitor;
52+
this.lock = lock;
5253
}
5354

5455
public void save(PythonInputFile inputFile, List<PythonCheck.PreciseIssue> issues) {
@@ -94,8 +95,11 @@ private void save(PythonInputFile inputFile, PythonCheck.PreciseIssue preciseIss
9495
}
9596

9697
private void save(NewIssue newIssue) {
97-
synchronized (monitor) {
98+
try {
99+
lock.lock();
98100
newIssue.save();
101+
} finally {
102+
lock.unlock();
99103
}
100104
}
101105

python-commons/src/main/java/org/sonar/plugins/python/MeasuresRepository.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.sonar.plugins.python;
1818

1919
import java.util.Set;
20+
import java.util.concurrent.locks.Lock;
2021
import org.sonar.api.batch.sensor.SensorContext;
2122
import org.sonar.api.batch.sensor.measure.NewMeasure;
2223
import org.sonar.api.issue.NoSonarFilter;
@@ -33,17 +34,26 @@ public class MeasuresRepository {
3334
private final NoSonarFilter noSonarFilter;
3435
private final FileLinesContextFactory fileLinesContextFactory;
3536
private final boolean isInSonarLint;
36-
private final Object monitor;
37+
private final Lock lock;
3738

38-
public MeasuresRepository(SensorContext context, NoSonarFilter noSonarFilter, FileLinesContextFactory fileLinesContextFactory, boolean isInSonarLint, Object monitor) {
39+
public MeasuresRepository(SensorContext context, NoSonarFilter noSonarFilter, FileLinesContextFactory fileLinesContextFactory, boolean isInSonarLint, Lock lock) {
3940
this.context = context;
4041
this.noSonarFilter = noSonarFilter;
4142
this.fileLinesContextFactory = fileLinesContextFactory;
4243
this.isInSonarLint = isInSonarLint;
43-
this.monitor = monitor;
44+
this.lock = lock;
4445
}
4546

46-
public synchronized void save(PythonInputFile inputFile, PythonVisitorContext visitorContext) {
47+
public void save(PythonInputFile inputFile, PythonVisitorContext visitorContext) {
48+
try {
49+
lock.lock();
50+
saveInternal(inputFile, visitorContext);
51+
} finally {
52+
lock.unlock();
53+
}
54+
}
55+
56+
private void saveInternal(PythonInputFile inputFile, PythonVisitorContext visitorContext) {
4757
FileMetrics fileMetrics = new FileMetrics(visitorContext, isNotebook(inputFile));
4858
FileLinesVisitor fileLinesVisitor = fileMetrics.fileLinesVisitor();
4959

@@ -73,8 +83,11 @@ public synchronized void save(PythonInputFile inputFile, PythonVisitorContext vi
7383
}
7484

7585
private void processNoSonarInFile(PythonInputFile inputFile, FileLinesVisitor fileLinesVisitor) {
76-
synchronized (monitor) {
86+
try {
87+
lock.lock();
7788
noSonarFilter.noSonarInFile(inputFile.wrappedFile(), fileLinesVisitor.getLinesWithNoSonar());
89+
} finally {
90+
lock.unlock();
7891
}
7992
}
8093

@@ -91,14 +104,20 @@ private void saveMetricOnFile(PythonInputFile inputFile, Metric<Integer> metric,
91104
}
92105

93106
private void save(FileLinesContext fileLinesContext) {
94-
synchronized (monitor) {
107+
try {
108+
lock.lock();
95109
fileLinesContext.save();
110+
} finally {
111+
lock.unlock();
96112
}
97113
}
98114

99115
private void save(NewMeasure<Integer> measure) {
100-
synchronized (monitor) {
116+
try {
117+
lock.lock();
101118
measure.save();
119+
} finally {
120+
lock.unlock();
102121
}
103122
}
104123
}

python-commons/src/main/java/org/sonar/plugins/python/NewSymbolsCollector.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.ArrayList;
2020
import java.util.Comparator;
2121
import java.util.List;
22+
import java.util.concurrent.locks.Lock;
2223
import org.sonar.api.batch.sensor.symbol.NewSymbol;
2324
import org.sonar.api.batch.sensor.symbol.NewSymbolTable;
2425
import org.sonar.plugins.python.api.symbols.Symbol;
@@ -33,10 +34,10 @@
3334
import org.sonar.plugins.python.api.tree.Tree;
3435

3536
public class NewSymbolsCollector {
36-
private final Object monitor;
37+
private final Lock lock;
3738

38-
public NewSymbolsCollector(Object monitor) {
39-
this.monitor = monitor;
39+
public NewSymbolsCollector(Lock lock) {
40+
this.lock = lock;
4041
}
4142

4243
public void collect(NewSymbolTable newSymbolTable, FileInput fileInput) {
@@ -46,8 +47,11 @@ public void collect(NewSymbolTable newSymbolTable, FileInput fileInput) {
4647
}
4748

4849
private void save(NewSymbolTable newSymbolTable) {
49-
synchronized (monitor) {
50+
try {
51+
lock.lock();
5052
newSymbolTable.save();
53+
} finally {
54+
lock.unlock();
5155
}
5256
}
5357

python-commons/src/main/java/org/sonar/plugins/python/PythonHighlighter.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.Collections;
2020
import java.util.HashSet;
2121
import java.util.Set;
22+
import java.util.concurrent.locks.Lock;
23+
import java.util.concurrent.locks.ReentrantLock;
2224
import javax.annotation.Nullable;
2325
import org.sonar.api.batch.sensor.SensorContext;
2426
import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
@@ -85,14 +87,14 @@
8587
*/
8688
public class PythonHighlighter {
8789

88-
private final Object monitor;
90+
private final Lock lock;
8991

90-
public PythonHighlighter(Object monitor) {
91-
this.monitor = monitor;
92+
public PythonHighlighter(Lock lock) {
93+
this.lock = lock;
9294
}
9395

9496
public PythonHighlighter() {
95-
this(new Object());
97+
this(new ReentrantLock());
9698
}
9799

98100
public void highlight(SensorContext sensorContext, PythonVisitorContext visitorContext, PythonInputFile inputFile) {
@@ -102,8 +104,11 @@ public void highlight(SensorContext sensorContext, PythonVisitorContext visitorC
102104
}
103105

104106
private void save(NewHighlighting newHighlighting) {
105-
synchronized (monitor) {
107+
try {
108+
lock.lock();
106109
newHighlighting.save();
110+
} finally {
111+
lock.unlock();
107112
}
108113
}
109114

python-commons/src/main/java/org/sonar/plugins/python/PythonScanner.java

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
import java.util.Collection;
2525
import java.util.Collections;
2626
import java.util.HashSet;
27+
import java.util.List;
2728
import java.util.Map;
2829
import java.util.Set;
2930
import java.util.concurrent.ConcurrentHashMap;
3031
import java.util.concurrent.atomic.AtomicBoolean;
3132
import java.util.concurrent.atomic.AtomicInteger;
33+
import java.util.concurrent.locks.Lock;
34+
import java.util.concurrent.locks.ReentrantLock;
3235
import java.util.function.Supplier;
3336
import java.util.regex.Pattern;
3437
import org.slf4j.Logger;
@@ -44,6 +47,7 @@
4447
import org.sonar.plugins.python.api.PythonInputFileContext;
4548
import org.sonar.plugins.python.api.PythonSubscriptionCheck;
4649
import org.sonar.plugins.python.api.PythonVisitorContext;
50+
import org.sonar.plugins.python.api.internal.EndOfAnalysis;
4751
import org.sonar.plugins.python.api.tree.FileInput;
4852
import org.sonar.plugins.python.cpd.PythonCpdAnalyzer;
4953
import org.sonar.plugins.python.indexer.PythonIndexer;
@@ -68,18 +72,18 @@ public class PythonScanner extends Scanner {
6872
private final AtomicInteger recognitionErrorCount;
6973
private final AtomicBoolean foundDatabricks;
7074
private final PythonFileConsumer architectureCallback;
71-
private final Map<String, Object> repositoryLocks;
75+
private final Map<String, Lock> repositoryLocks;
7276
private final NewSymbolsCollector newSymbolsCollector;
7377
private final PythonHighlighter pythonHighlighter;
7478
private final IssuesRepository issuesRepository;
7579
private final MeasuresRepository measuresRepository;
80+
private final Lock lock;
7681

7782
public PythonScanner(
7883
SensorContext context, PythonChecks checks, FileLinesContextFactory fileLinesContextFactory, NoSonarFilter noSonarFilter,
7984
Supplier<PythonParser> parserSupplier, PythonIndexer indexer, PythonFileConsumer architectureCallback) {
8085
super(context);
8186
this.checks = checks;
82-
this.cpdAnalyzer = new PythonCpdAnalyzer(context, this);
8387
this.parserSupplier = parserSupplier;
8488
this.indexer = indexer;
8589
this.indexer.buildOnce(context);
@@ -88,10 +92,12 @@ public PythonScanner(
8892
this.recognitionErrorCount = new AtomicInteger(0);
8993
this.foundDatabricks = new AtomicBoolean(false);
9094
this.repositoryLocks = new ConcurrentHashMap<>();
91-
this.newSymbolsCollector = new NewSymbolsCollector(this);
92-
this.pythonHighlighter = new PythonHighlighter(this);
93-
this.issuesRepository = new IssuesRepository(context, checks, indexer, isInSonarLint(context), this);
94-
this.measuresRepository = new MeasuresRepository(context, noSonarFilter, fileLinesContextFactory, isInSonarLint(context), this);
95+
this.lock = new ReentrantLock();
96+
this.cpdAnalyzer = new PythonCpdAnalyzer(context, lock);
97+
this.newSymbolsCollector = new NewSymbolsCollector(lock);
98+
this.pythonHighlighter = new PythonHighlighter(lock);
99+
this.issuesRepository = new IssuesRepository(context, checks, indexer, isInSonarLint(context), lock);
100+
this.measuresRepository = new MeasuresRepository(context, noSonarFilter, fileLinesContextFactory, isInSonarLint(context), lock);
95101
}
96102

97103
@Override
@@ -113,9 +119,8 @@ protected void scanFile(PythonInputFile inputFile) throws IOException {
113119
executeChecks(visitorContext, checks.sonarPythonChecks(), fileType, inputFile);
114120
executeOtherChecks(inputFile, visitorContext, fileType);
115121

116-
synchronized (repositoryLocks.computeIfAbsent(ARCHITECTURE_CALLBACK_LOCK_KEY, k -> new Object())) {
117-
architectureCallback.scanFile(visitorContext);
118-
}
122+
123+
runLockedByRepository(ARCHITECTURE_CALLBACK_LOCK_KEY, () -> architectureCallback.scanFile(visitorContext));
119124

120125
issuesRepository.save(inputFile, visitorContext.getIssues());
121126

@@ -187,13 +192,8 @@ private void executeChecks(PythonVisitorContext visitorContext, Collection<Pytho
187192
}
188193

189194
private void executeOtherChecks(PythonInputFile inputFile, PythonVisitorContext visitorContext, InputFile.Type fileType) {
190-
checks.noSonarPythonChecks().forEach((repositoryKey, repositoryChecks) -> {
191-
var lock = repositoryLocks.computeIfAbsent(repositoryKey, k -> new Object());
192-
synchronized (lock) {
193-
executeChecks(visitorContext, repositoryChecks, fileType, inputFile);
194-
}
195-
}
196-
);
195+
checks.noSonarPythonChecks()
196+
.forEach((repositoryKey, repositoryChecks) -> runLockedByRepository(repositoryKey, () -> executeChecks(visitorContext, repositoryChecks, fileType, inputFile)));
197197
}
198198

199199
private void searchForDataBricks(PythonVisitorContext visitorContext) {
@@ -229,17 +229,15 @@ public boolean scanFileWithoutParsing(PythonInputFile inputFile) {
229229

230230
result = scanFileWithoutParsingSonarPython(inputFile, fileType, inputFileContext, result);
231231

232+
var atomicResult = new AtomicBoolean(result);
232233
var otherChecks = checks.noSonarPythonChecks();
233-
for (var entry : otherChecks.entrySet()) {
234-
var repositoryKey = entry.getKey();
235-
var repositoryChecks = entry.getValue();
236-
var lock = repositoryLocks.computeIfAbsent(repositoryKey, k -> new Object());
237-
synchronized (lock) {
238-
for (var check : repositoryChecks) {
239-
result = scanFileWithoutParsingNotSonarPython(inputFile, check, fileType, result, inputFileContext);
240-
}
234+
otherChecks.forEach((repositoryKey, repositoryChecks) -> runLockedByRepository(repositoryKey, () -> {
235+
for (var check : repositoryChecks) {
236+
var scanResult = scanFileWithoutParsingNotSonarPython(inputFile, check, fileType, atomicResult.get(), inputFileContext);
237+
atomicResult.set(scanResult);
241238
}
242-
}
239+
}));
240+
result = atomicResult.get();
243241

244242
result &= architectureCallback.scanWithoutParsing(inputFileContext);
245243
if (!result) {
@@ -293,14 +291,11 @@ private boolean scanFileWithoutParsingSonarPython(PythonInputFile inputFile, Inp
293291
public void endOfAnalysis() {
294292
indexer.postAnalysis(context);
295293
checks.sonarPythonEndOfAnalyses().forEach(c -> c.endOfAnalysis(indexer.cacheContext()));
296-
checks.noSonarPythonEndOfAnalyses().forEach(
297-
(repositoryKey, endOfAnalyses) -> {
298-
var lock = repositoryLocks.computeIfAbsent(repositoryKey, k -> new Object());
299-
synchronized (lock) {
300-
endOfAnalyses.forEach(c -> c.endOfAnalysis(indexer.cacheContext()));
301-
}
302-
}
303-
);
294+
checks.noSonarPythonEndOfAnalyses().forEach(this::endOfAnalysisForRepository);
295+
}
296+
297+
private void endOfAnalysisForRepository(String repositoryKey, List<EndOfAnalysis> endOfAnalyses) {
298+
runLockedByRepository(repositoryKey, () -> endOfAnalyses.forEach(c -> c.endOfAnalysis(indexer.cacheContext())));
304299
}
305300

306301
boolean isCheckNotApplicable(PythonCheck pythonCheck, InputFile.Type fileType) {
@@ -355,4 +350,14 @@ protected int getNumberOfThreads(SensorContext context) {
355350
return context.config().getInt(THREADS_PROPERTY_NAME)
356351
.orElse(1);
357352
}
353+
354+
private void runLockedByRepository(String repositoryKey, Runnable runnable) {
355+
var repositoryLock = repositoryLocks.computeIfAbsent(repositoryKey, k -> new ReentrantLock());
356+
try {
357+
repositoryLock.lock();
358+
runnable.run();
359+
} finally {
360+
repositoryLock.unlock();
361+
}
362+
}
358363
}

python-commons/src/main/java/org/sonar/plugins/python/cpd/PythonCpdAnalyzer.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.IOException;
2222
import java.util.ArrayList;
2323
import java.util.List;
24+
import java.util.concurrent.locks.Lock;
2425
import org.sonar.api.batch.fs.InputFile;
2526
import org.sonar.api.batch.sensor.SensorContext;
2627
import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
@@ -43,11 +44,11 @@ public class PythonCpdAnalyzer {
4344
private static final Logger LOG = LoggerFactory.getLogger(PythonCpdAnalyzer.class);
4445

4546
private final SensorContext context;
46-
private final Object monitor;
47+
private final Lock lock;
4748

48-
public PythonCpdAnalyzer(SensorContext context, Object monitor) {
49+
public PythonCpdAnalyzer(SensorContext context, Lock lock) {
4950
this.context = context;
50-
this.monitor = monitor;
51+
this.lock = lock;
5152
}
5253

5354
public void pushCpdTokens(InputFile inputFile, PythonVisitorContext visitorContext) {
@@ -116,22 +117,31 @@ private void saveTokensToCache(PythonVisitorContext visitorContext, List<Token>
116117
}
117118

118119
private void save(NewCpdTokens cpdTokens) {
119-
synchronized (monitor) {
120+
try {
121+
lock.lock();
120122
cpdTokens.save();
123+
} finally {
124+
lock.unlock();
121125
}
122126
}
123127

124128
private void copyFromPrevious(CacheContext cacheContext, String dataKey, String tableKey) {
125-
synchronized (monitor) {
129+
try {
130+
lock.lock();
126131
cacheContext.getWriteCache().copyFromPrevious(dataKey);
127132
cacheContext.getWriteCache().copyFromPrevious(tableKey);
133+
} finally {
134+
lock.unlock();
128135
}
129136
}
130137

131138
private void writeToCache(CacheContext cacheContext, String fileKey, CpdSerializer.SerializationResult result) {
132-
synchronized (monitor) {
139+
try {
140+
lock.lock();
133141
cacheContext.getWriteCache().write(stringTableCacheKey(fileKey), result.stringTable);
134142
cacheContext.getWriteCache().write(dataCacheKey(fileKey), result.data);
143+
} finally {
144+
lock.unlock();
135145
}
136146
}
137147

0 commit comments

Comments
 (0)