Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,18 @@ public Comparator<MavenProject> getComparator() {
* @return the project's weight (higher means longer dependency chain)
*/
public long getProjectWeight(MavenProject project) {
return projectWeights.computeIfAbsent(project, this::calculateWeight);
// First check if weight is already calculated
Long existingWeight = projectWeights.get(project);
if (existingWeight != null) {
return existingWeight;
}

// Calculate weight without using computeIfAbsent to avoid recursive update issues
long weight = calculateWeight(project);
Comment thread
gnodet marked this conversation as resolved.

// Use putIfAbsent to handle concurrent access safely
Long previousWeight = projectWeights.putIfAbsent(project, weight);
return previousWeight != null ? previousWeight : weight;
}

private Comparator<MavenProject> createComparator() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub;
Expand Down Expand Up @@ -154,4 +159,53 @@ void testSameWeightOrdering() {
long weightC = comparator.getProjectWeight(ProjectDependencyGraphStub.C);
assertEquals(weightB, weightC, "Projects B and C should have the same weight");
}

@Test
void testConcurrentWeightCalculation() throws Exception {
// Test that concurrent weight calculation doesn't cause recursive update issues
// This test simulates the scenario that causes the IllegalStateException

int numThreads = 10;
int numIterations = 100;
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
CountDownLatch latch = new CountDownLatch(numThreads);
AtomicReference<Exception> exception = new AtomicReference<>();

for (int i = 0; i < numThreads; i++) {
executor.submit(() -> {
try {
for (int j = 0; j < numIterations; j++) {
// Simulate concurrent access to weight calculation
// This can trigger the recursive update issue
List<MavenProject> projects = Arrays.asList(
ProjectDependencyGraphStub.A,
ProjectDependencyGraphStub.B,
ProjectDependencyGraphStub.C,
ProjectDependencyGraphStub.X,
ProjectDependencyGraphStub.Y,
ProjectDependencyGraphStub.Z);

// Sort projects concurrently - this triggers weight calculation
projects.sort(comparator.getComparator());

// Also directly access weights to increase contention
for (MavenProject project : projects) {
comparator.getProjectWeight(project);
}
}
} catch (Exception e) {
exception.set(e);
} finally {
latch.countDown();
}
});
}

latch.await(30, TimeUnit.SECONDS);
executor.shutdown();

if (exception.get() != null) {
throw exception.get();
}
}
}
Loading