You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2019/12/13 08:14:16 UTC

[httpcomponents-core] branch master updated: Allow for timeout while acquiring lock in StrictConnPool. (#163)

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

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 6d6f1fa  Allow for timeout while acquiring lock in StrictConnPool. (#163)
6d6f1fa is described below

commit 6d6f1fa4a6e3a4d33c8ae51f9d284e4e900eb8a1
Author: cwildman <ch...@chriswildman.com>
AuthorDate: Fri Dec 13 00:14:07 2019 -0800

    Allow for timeout while acquiring lock in StrictConnPool. (#163)
    
    Allow for timeout while acquiring lock in StrictConnPool
---
 .../org/apache/hc/core5/pool/StrictConnPool.java   | 37 +++++++----
 .../apache/hc/core5/pool/TestStrictConnPool.java   | 71 ++++++++++++++++++++++
 2 files changed, 97 insertions(+), 11 deletions(-)

diff --git a/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java b/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
index 19c2e36..dfd5b36 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
@@ -171,21 +171,36 @@ public class StrictConnPool<T, C extends ModalCloseable> implements ManagedConnP
         Args.notNull(route, "Route");
         Args.notNull(requestTimeout, "Request timeout");
         Asserts.check(!this.isShutDown.get(), "Connection pool shut down");
+        final Deadline deadline = Deadline.calculate(requestTimeout);
         final BasicFuture<PoolEntry<T, C>> future = new BasicFuture<>(callback);
-        this.lock.lock();
+        final boolean acquiredLock;
+
         try {
-            final LeaseRequest<T, C> request = new LeaseRequest<>(route, state, requestTimeout, future);
-            final boolean completed = processPendingRequest(request);
-            if (!request.isDone() && !completed) {
-                this.leasingRequests.add(request);
-            }
-            if (request.isDone()) {
-                this.completedRequests.add(request);
+            acquiredLock = this.lock.tryLock(requestTimeout.getDuration(), requestTimeout.getTimeUnit());
+        } catch (final InterruptedException interruptedException) {
+            Thread.currentThread().interrupt();
+            future.cancel();
+            return future;
+        }
+
+        if (acquiredLock) {
+            try {
+                final LeaseRequest<T, C> request = new LeaseRequest<>(route, state, requestTimeout, future);
+                final boolean completed = processPendingRequest(request);
+                if (!request.isDone() && !completed) {
+                    this.leasingRequests.add(request);
+                }
+                if (request.isDone()) {
+                    this.completedRequests.add(request);
+                }
+            } finally {
+                this.lock.unlock();
             }
-        } finally {
-            this.lock.unlock();
+            fireCallbacks();
+        } else {
+            future.failed(DeadlineTimeoutException.from(deadline));
         }
-        fireCallbacks();
+
         return future;
     }
 
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java b/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java
index 610b091..7aa5c50 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/pool/TestStrictConnPool.java
@@ -27,11 +27,16 @@
 package org.apache.hc.core5.pool;
 
 import java.util.Collections;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.hc.core5.function.Callback;
 import org.apache.hc.core5.http.HttpConnection;
 import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.util.DeadlineTimeoutException;
 import org.apache.hc.core5.util.TimeValue;
 import org.apache.hc.core5.util.Timeout;
 import org.junit.Assert;
@@ -512,6 +517,72 @@ public class TestStrictConnPool {
         Assert.assertTrue(future3.isDone());
     }
 
+    private static class HoldInternalLockThread extends Thread {
+        private HoldInternalLockThread(final StrictConnPool<String, HttpConnection> pool, final CountDownLatch lockHeld) {
+            super(new Runnable() {
+                @Override
+                public void run() {
+                    pool.lease("somehost", null); // lease a connection so we have something to enumLeased()
+                    pool.enumLeased(new Callback<PoolEntry<String, HttpConnection>>() {
+                        @Override
+                        public void execute(final PoolEntry<String, HttpConnection> object) {
+                            try {
+                                lockHeld.countDown();
+                                Thread.sleep(Long.MAX_VALUE);
+                            } catch (final InterruptedException ignored) {
+                            }
+                        }
+                    });
+                }
+            });
+        }
+    }
+
+    @Test
+    public void testLeaseRequestLockTimeout() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1);
+        final CountDownLatch lockHeld = new CountDownLatch(1);
+        final Thread holdInternalLock = new HoldInternalLockThread(pool, lockHeld);
+
+        holdInternalLock.start(); // Start a thread to grab the internal conn pool lock
+        lockHeld.await(); // Wait until we know the internal lock is held
+
+        // Attempt to get a connection while lock is held
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null);
+
+        try {
+            future2.get();
+        } catch (final ExecutionException executionException) {
+            Assert.assertTrue(executionException.getCause() instanceof DeadlineTimeoutException);
+            holdInternalLock.interrupt(); // Cleanup
+            return;
+        }
+        Assert.fail("Expected deadline timeout.");
+    }
+
+    @Test
+    public void testLeaseRequestInterrupted() throws Exception {
+        final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1);
+        final CountDownLatch lockHeld = new CountDownLatch(1);
+        final Thread holdInternalLock = new HoldInternalLockThread(pool, lockHeld);
+
+        holdInternalLock.start(); // Start a thread to grab the internal conn pool lock
+        lockHeld.await(); // Wait until we know the internal lock is held
+
+        Thread.currentThread().interrupt();
+        // Attempt to get a connection while lock is held and thread is interrupted
+        final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null);
+
+        Assert.assertTrue(Thread.interrupted());
+        try {
+            future2.get();
+        } catch (final CancellationException cancellationException) {
+            holdInternalLock.interrupt(); // Cleanup
+            return;
+        }
+        Assert.fail("Expected interrupted exception.");
+    }
+
     @Test
     public void testLeaseRequestCanceled() throws Exception {
         final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1);