You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by mb...@apache.org on 2007/02/19 20:52:44 UTC

svn commit: r509320 - in /jakarta/commons/proper/httpclient/trunk: release_notes.txt src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java

Author: mbecke
Date: Mon Feb 19 11:52:43 2007
New Revision: 509320

URL: http://svn.apache.org/viewvc?view=rev&rev=509320
Log:
[HTTPCLIENT-633]: MultithreadedConnectionManager's handling of thread interrupts.

Contributed by: Michael Becke
Reviewed by: Roland Weber and Oleg Kalnichevski


Modified:
    jakarta/commons/proper/httpclient/trunk/release_notes.txt
    jakarta/commons/proper/httpclient/trunk/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java
    jakarta/commons/proper/httpclient/trunk/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java

Modified: jakarta/commons/proper/httpclient/trunk/release_notes.txt
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/httpclient/trunk/release_notes.txt?view=diff&rev=509320&r1=509319&r2=509320
==============================================================================
--- jakarta/commons/proper/httpclient/trunk/release_notes.txt (original)
+++ jakarta/commons/proper/httpclient/trunk/release_notes.txt Mon Feb 19 11:52:43 2007
@@ -1,4 +1,8 @@
 Changes since Release 3.1 Beta 1:
+* [HTTPCLIENT-633] - Changed MultiThreadedHttpConnectionManager's handling of thread 
+           interrupts. IllegalThreadStateException is now thrown when waiting threads
+           are interrupted from outside of the connection manager.
+           Contributed by Michael Becke <mbecke at apache.org>
 
 * [HTTPCLIENT-628] - IOException in AutoCloseInputStream.available()
            Contributed by Roland Weber <rolandw at apache.org>

Modified: jakarta/commons/proper/httpclient/trunk/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/httpclient/trunk/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java?view=diff&rev=509320&r1=509319&r2=509320
==============================================================================
--- jakarta/commons/proper/httpclient/trunk/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java (original)
+++ jakarta/commons/proper/httpclient/trunk/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java Mon Feb 19 11:52:43 2007
@@ -505,6 +505,8 @@
                             waitingThread = new WaitingThread();
                             waitingThread.hostConnectionPool = hostPool;
                             waitingThread.thread = Thread.currentThread();
+                        } else {
+                            waitingThread.interruptedByConnectionPool = false;
                         }
                                     
                         if (useTimeout) {
@@ -514,14 +516,24 @@
                         hostPool.waitingThreads.addLast(waitingThread);
                         connectionPool.waitingThreads.addLast(waitingThread);
                         connectionPool.wait(timeToWait);
-                        
-                        // we have not been interrupted so we need to remove ourselves from the 
-                        // wait queue
-                        hostPool.waitingThreads.remove(waitingThread);
-                        connectionPool.waitingThreads.remove(waitingThread);
                     } catch (InterruptedException e) {
-                        // do nothing
+                        if (!waitingThread.interruptedByConnectionPool) {
+                            LOG.debug("Interrupted while waiting for connection", e);
+                            throw new IllegalThreadStateException(
+                                "Interrupted while waiting in MultiThreadedHttpConnectionManager");
+                        }
+                        // Else, do nothing, we were interrupted by the connection pool
+                        // and should now have a connection waiting for us, continue
+                        // in the loop and let's get it.
                     } finally {
+                        if (!waitingThread.interruptedByConnectionPool) {
+                            // Either we timed out, experienced a "spurious wakeup", or were
+                            // interrupted by an external thread.  Regardless we need to 
+                            // cleanup for ourselves in the wait queue.
+                            hostPool.waitingThreads.remove(waitingThread);
+                            connectionPool.waitingThreads.remove(waitingThread);
+                        }
+                        
                         if (useTimeout) {
                             endWait = System.currentTimeMillis();
                             timeToWait -= (endWait - startWait);
@@ -725,6 +737,7 @@
             while (iter.hasNext()) {
                 WaitingThread waiter = (WaitingThread) iter.next();
                 iter.remove();
+                waiter.interruptedByConnectionPool = true;
                 waiter.thread.interrupt();
             }
             
@@ -923,7 +936,7 @@
                 if (LOG.isDebugEnabled()) {
                     LOG.debug("Notifying thread waiting on host pool, hostConfig=" 
                         + hostPool.hostConfiguration);
-                }                
+                }
                 waitingThread = (WaitingThread) hostPool.waitingThreads.removeFirst();
                 waitingThreads.remove(waitingThread);
             } else if (waitingThreads.size() > 0) {
@@ -937,6 +950,7 @@
             }
                 
             if (waitingThread != null) {
+                waitingThread.interruptedByConnectionPool = true;
                 waitingThread.thread.interrupt();
             }
         }
@@ -1033,6 +1047,11 @@
         
         /** The connection pool the thread is waiting for */
         public HostConnectionPool hostConnectionPool;
+        
+        /** Flag to indicate if the thread was interrupted by the ConnectionPool. Set
+         * to true inside {@link ConnectionPool#notifyWaitingThread(HostConnectionPool)} 
+         * before the thread is interrupted. */
+        public boolean interruptedByConnectionPool = false;
     }
 
     /**

Modified: jakarta/commons/proper/httpclient/trunk/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/httpclient/trunk/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java?view=diff&rev=509320&r1=509319&r2=509320
==============================================================================
--- jakarta/commons/proper/httpclient/trunk/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java (original)
+++ jakarta/commons/proper/httpclient/trunk/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java Mon Feb 19 11:52:43 2007
@@ -655,6 +655,59 @@
         assertEquals("connectionsInPool(host)", manager.getConnectionsInPool(client.getHostConfiguration()), 0);
     }
     
+    /**
+     * Tests that thread waiting in the MultiThreadedHttpConnectionManager can be 
+     * interrupted.
+     */
+    public void testWaitingThreadInterrupted() {
+
+        this.server.setHttpService(new EchoService());
+
+        MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
+        connectionManager.getParams().setIntParameter(
+            HttpConnectionManagerParams.MAX_TOTAL_CONNECTIONS, 1);
+
+        HostConfiguration host1 = new HostConfiguration();
+        host1.setHost("host1", -1, "http");
+
+        // hold on to the only connection
+        HttpConnection connection = connectionManager.getConnection(host1);
+
+        // wait for a connection on another thread
+        GetConnectionThread getConn = new GetConnectionThread(host1, connectionManager, 1000);
+        getConn.start();
+        
+        // give the thread a chance to block
+        synchronized (this) {
+            try {
+                this.wait(500);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        
+        // interrupt the thread, this should cancel waiting with a RuntimeException
+        getConn.interrupt();
+        
+        try {
+            getConn.join();
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+        
+        // make sure the correct exception was thrown
+        assertTrue(getConn.exception != null);
+        assertEquals(getConn.exception.getClass(), IllegalThreadStateException.class);
+        
+        // make sure the connection manager is still working
+        connection.releaseConnection();
+        try {
+            connectionManager.getConnectionWithTimeout(host1, 10);
+        } catch (ConnectionPoolTimeoutException e) {
+            fail("Connection not available");
+        }
+    }
+    
     public void testReclaimUnusedConnection() {
 
         this.server.setHttpService(new EchoService());



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org