? gc-connection.patch ? lib/junit.jar ? lib/servlet.jar Index: src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java,v retrieving revision 1.17.2.4 diff -u -r1.17.2.4 MultiThreadedHttpConnectionManager.java --- src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java 1 Sep 2003 18:05:51 -0000 1.17.2.4 +++ src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java 31 Oct 2003 23:27:07 -0000 @@ -126,6 +126,12 @@ private ReferenceQueue referenceQueue; /** + * Keep hold of this thread, if only so that it is possible to query it to + * see that it gets cleaned up. + */ + private ReferenceQueueThread referenceQueueCleanupThread; + + /** * No-args constructor */ public MultiThreadedHttpConnectionManager() { @@ -135,7 +141,17 @@ this.referenceQueue = new ReferenceQueue(); - new ReferenceQueueThread().start(); + referenceQueueCleanupThread = new ReferenceQueueThread(); + referenceQueueCleanupThread.start(); + } + + /** + * Get the thread used for monitoring cleanup of gc'd connections. + * + * @return The {@link Thread} used for cleaning up references to connections. + */ + Thread getReferenceQueueCleanupThread() { + return referenceQueueCleanupThread; } /** @@ -399,6 +415,16 @@ } /** + * Call this function if you are completely finished using the + * MultiThreadedHttpConnectionManager to enable the cleanup of miscellaneous + * data that otherwise might not be garbage collected. + */ + public void shutdown() { + referenceQueueCleanupThread.exitThread(); + referenceQueueCleanupThread = null; + } + + /** * Gets the host configuration for a connection. * @param conn the connection to get the configuration of * @return a new HostConfiguration @@ -688,14 +714,32 @@ */ private class ReferenceQueueThread extends Thread { + private boolean stayActive = true; + /** * Create an instance and make this a daemon thread. */ public ReferenceQueueThread() { + // name the thread for debugging convenience. + super("MultiThreadedHttpConnectionManager cleanup"); setDaemon(true); } /** + * Exit the daemon monitoring thread so that it can be garbage + * collected. + */ + public void exitThread() { + stayActive = false; + this.interrupt(); + try { + this.join(); + } catch (InterruptedException e) { + LOG.warn("Unexpected interrupt waiting for thread to die"); + } + } + + /** * Handles cleaning up for the given reference. Decrements any connection counts * and notifies waiting threads, if appropriate. * @@ -727,7 +771,7 @@ * Start execution. */ public void run() { - while (true) { + while (stayActive) { try { Reference ref = referenceQueue.remove(); if (ref != null) { Index: src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java,v retrieving revision 1.8 diff -u -r1.8 TestHttpConnectionManager.java --- src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java 28 Apr 2003 23:19:58 -0000 1.8 +++ src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java 31 Oct 2003 23:27:07 -0000 @@ -63,6 +63,7 @@ package org.apache.commons.httpclient; import java.io.IOException; +import java.lang.ref.WeakReference; import junit.framework.Test; import junit.framework.TestSuite; @@ -525,7 +526,40 @@ //Expected result } } - + + public void testDroppedThread() { + MultiThreadedHttpConnectionManager mthcm = new MultiThreadedHttpConnectionManager(); + WeakReference wr = new WeakReference(mthcm.getReferenceQueueCleanupThread()); + try { + GetMethod method = new GetMethod("http://cvs.apache.org/viewcvs/jakarta-commons/httpclient/"); + HttpClient httpClient = new HttpClient(mthcm); + httpClient.executeMethod(method); + String result = method.getResponseBodyAsString(); + method.releaseConnection(); + + httpClient = null; + method = null; + } catch (IOException e) { + fail("Unexpected exception:" + e); + } + + mthcm.shutdown(); + mthcm = null; + + // this sleep appears to be necessary in order to give the JVM + // time to clean up the miscellaneous pointers to the thread. Otherwise + // it doesn't get garbage collected. + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + fail("shouldn't be interrupted."); + } + + System.gc(); + Object thread = wr.get(); + assertNull("Thread should be null", thread); + } + static class GetConnectionThread extends Thread { private HostConfiguration hostConfiguration;