This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git

commit 1c86ff471bf25a1b69fd47ce7d05aaace89f1c86
Author: Guillaume Nodet <[email protected]>
AuthorDate: Sun Jun 7 06:47:49 2026 +0000

    Fix IpcServer Lock.unlock() — complete futures outside monitor
    
    F-20: Collect futures to complete during the synchronized block but
    complete them after releasing the Lock monitor, preventing I/O
    operations (socket writes in thenRun callbacks) from being
    serialized under the lock.
---
 .../org/eclipse/aether/named/ipc/IpcServer.java    | 35 ++++++++++++----------
 1 file changed, 20 insertions(+), 15 deletions(-)

diff --git 
a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
 
b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
index 9f58fb7e2..b7ae0a03f 100644
--- 
a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
+++ 
b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
@@ -442,24 +442,29 @@ public class IpcServer {
             return future;
         }
 
-        public synchronized void unlock(Context context) {
-            if (holders.remove(context)) {
-                while (waiters != null
-                        && !waiters.isEmpty()
-                        && (holders.isEmpty() || holders.get(0).shared && 
waiters.get(0).context.shared)) {
-                    Waiter waiter = waiters.remove(0);
-                    holders.add(waiter.context);
-                    waiter.future.complete(null);
-                }
-            } else if (waiters != null) {
-                for (Iterator<Waiter> it = waiters.iterator(); it.hasNext(); ) 
{
-                    Waiter waiter = it.next();
-                    if (waiter.context == context) {
-                        it.remove();
-                        waiter.future.cancel(false);
+        public void unlock(Context context) {
+            List<CompletableFuture<Void>> toComplete;
+            synchronized (this) {
+                toComplete = new ArrayList<>();
+                if (holders.remove(context)) {
+                    while (waiters != null
+                            && !waiters.isEmpty()
+                            && (holders.isEmpty() || holders.get(0).shared && 
waiters.get(0).context.shared)) {
+                        Waiter waiter = waiters.remove(0);
+                        holders.add(waiter.context);
+                        toComplete.add(waiter.future);
+                    }
+                } else if (waiters != null) {
+                    for (Iterator<Waiter> it = waiters.iterator(); 
it.hasNext(); ) {
+                        Waiter waiter = it.next();
+                        if (waiter.context == context) {
+                            it.remove();
+                            waiter.future.cancel(false);
+                        }
                     }
                 }
             }
+            toComplete.forEach(f -> f.complete(null));
         }
 
         public synchronized boolean isEmpty() {

Reply via email to