2424import java .util .Collection ;
2525import java .util .Collections ;
2626import java .util .HashSet ;
27+ import java .util .List ;
2728import java .util .Map ;
2829import java .util .Set ;
2930import java .util .concurrent .ConcurrentHashMap ;
3031import java .util .concurrent .atomic .AtomicBoolean ;
3132import java .util .concurrent .atomic .AtomicInteger ;
33+ import java .util .concurrent .locks .Lock ;
34+ import java .util .concurrent .locks .ReentrantLock ;
3235import java .util .function .Supplier ;
3336import java .util .regex .Pattern ;
3437import org .slf4j .Logger ;
4447import org .sonar .plugins .python .api .PythonInputFileContext ;
4548import org .sonar .plugins .python .api .PythonSubscriptionCheck ;
4649import org .sonar .plugins .python .api .PythonVisitorContext ;
50+ import org .sonar .plugins .python .api .internal .EndOfAnalysis ;
4751import org .sonar .plugins .python .api .tree .FileInput ;
4852import org .sonar .plugins .python .cpd .PythonCpdAnalyzer ;
4953import 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}
0 commit comments