Copilot commented on code in PR #12630:
URL: https://github.com/apache/maven/pull/12630#discussion_r3683061868


##########
impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectRealmCacheTest.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.project;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.eclipse.aether.graph.DependencyFilter;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+class DefaultProjectRealmCacheTest {
+
+    @Test
+    void testConcurrentPutWithSameKey() throws Exception {
+        DefaultProjectRealmCache cache = new DefaultProjectRealmCache();
+        ClassRealm realm = mock(ClassRealm.class);
+        DependencyFilter filter = mock(DependencyFilter.class);
+        ProjectRealmCache.Key key = cache.createKey(List.of(realm));
+
+        int threadCount = 10;
+        CyclicBarrier barrier = new CyclicBarrier(threadCount);
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        List<Future<Boolean>> futures = new ArrayList<>();
+
+        for (int i = 0; i < threadCount; i++) {
+            futures.add(executor.submit(() -> {
+                barrier.await();

Review Comment:
   The test references `DependencyFilter` but does not import it in this file. 
Add the appropriate `DependencyFilter` import (matching the type used by 
`DefaultProjectRealmCache.put(...)`) so the test compiles.



##########
impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectRealmCacheTest.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.project;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.eclipse.aether.graph.DependencyFilter;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+class DefaultProjectRealmCacheTest {
+
+    @Test
+    void testConcurrentPutWithSameKey() throws Exception {
+        DefaultProjectRealmCache cache = new DefaultProjectRealmCache();
+        ClassRealm realm = mock(ClassRealm.class);
+        DependencyFilter filter = mock(DependencyFilter.class);
+        ProjectRealmCache.Key key = cache.createKey(List.of(realm));
+
+        int threadCount = 10;
+        CyclicBarrier barrier = new CyclicBarrier(threadCount);
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        List<Future<Boolean>> futures = new ArrayList<>();
+
+        for (int i = 0; i < threadCount; i++) {
+            futures.add(executor.submit(() -> {
+                barrier.await();
+                try {
+                    cache.put(key, mock(ClassRealm.class), 
mock(DependencyFilter.class));
+                    return true;
+                } catch (IllegalStateException e) {
+                    return false;
+                }
+            }));
+        }
+
+        int successCount = 0;

Review Comment:
   The loop variable `futre` looks like a typo and reduces readability. Rename 
it to `future` (or similar) to make the intent clear.



##########
impl/maven-core/src/test/java/org/apache/maven/project/DefaultProjectRealmCacheTest.java:
##########
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.project;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.eclipse.aether.graph.DependencyFilter;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+class DefaultProjectRealmCacheTest {
+
+    @Test
+    void testConcurrentPutWithSameKey() throws Exception {
+        DefaultProjectRealmCache cache = new DefaultProjectRealmCache();
+        ClassRealm realm = mock(ClassRealm.class);
+        DependencyFilter filter = mock(DependencyFilter.class);
+        ProjectRealmCache.Key key = cache.createKey(List.of(realm));
+
+        int threadCount = 10;
+        CyclicBarrier barrier = new CyclicBarrier(threadCount);
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        List<Future<Boolean>> futures = new ArrayList<>();
+
+        for (int i = 0; i < threadCount; i++) {
+            futures.add(executor.submit(() -> {
+                barrier.await();
+                try {
+                    cache.put(key, mock(ClassRealm.class), 
mock(DependencyFilter.class));
+                    return true;
+                } catch (IllegalStateException e) {
+                    return false;
+                }
+            }));
+        }
+
+        int successCount = 0;
+        for (Future<Boolean> f : futures) {
+            if (f.get()) {
+                successCount++;
+            }

Review Comment:
   This concurrency test can hang indefinitely if a task fails before reaching 
`barrier.await()` or if a thread stalls, because both `barrier.await()` and 
`Future.get()` are unbounded waits. Add timeouts (e.g., timed `await` / timed 
`get`) and ensure the executor is shut down in a `finally` block (optionally 
with `awaitTermination` / `shutdownNow`) to keep CI runs from deadlocking.



##########
impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectRealmCache.java:
##########
@@ -94,13 +94,13 @@ public CacheRecord get(Key key) {
     public CacheRecord put(Key key, ClassRealm projectRealm, DependencyFilter 
extensionArtifactFilter) {
         Objects.requireNonNull(projectRealm, "projectRealm cannot be null");
 
-        if (cache.containsKey(key)) {
-            throw new IllegalStateException("Duplicate project realm for 
extensions " + key);
-        }
-
         CacheRecord record = new CacheRecord(projectRealm, 
extensionArtifactFilter);
 
-        cache.put(key, record);
+        CacheRecord existing = cache.putIfAbsent(key, record);
+
+        if (existing != null) {
+            throw new IllegalStateException("Duplicate project realm for 
extensions " + key);
+        }

Review Comment:
   `CacheRecord record` is allocated even when the key is already present (the 
duplicate path). If duplicates are expected under contention and you want to 
avoid unnecessary allocations, consider using `cache.compute(...)` to only 
construct a new `CacheRecord` when the current value is null (and throw 
otherwise).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to