Author: markt
Date: Wed Jul  9 21:29:44 2014
New Revision: 1609308

URL: http://svn.apache.org/r1609308
Log:
POOL-263
Fix a threading issue that meant that concurrent calls to close() and 
returnObject() could result in some returned objects not being destroyed.

Modified:
    commons/proper/pool/trunk/src/changes/changes.xml
    
commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
    
commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
    
commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java

Modified: commons/proper/pool/trunk/src/changes/changes.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/changes/changes.xml?rev=1609308&r1=1609307&r2=1609308&view=diff
==============================================================================
--- commons/proper/pool/trunk/src/changes/changes.xml (original)
+++ commons/proper/pool/trunk/src/changes/changes.xml Wed Jul  9 21:29:44 2014
@@ -45,6 +45,10 @@ The <action> type attribute can be add,u
   <body>
   <release version="2.3" date="TBD" description=
 "TBD">
+    <action dev="markt" type="fix" issue="POOL-263">
+      Fix a threading issue that meant that concurrent calls to close() and
+      returnObject() could result in some returned objects not being destroyed.
+    </action>
     <action dev="psteitz" type="add" issue="POOL-262">
       Made fairness configurable for GenericObjectPool, GenericKeyedObjectPool.
     </action>

Modified: 
commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
URL: 
http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java?rev=1609308&r1=1609307&r2=1609308&view=diff
==============================================================================
--- 
commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
 (original)
+++ 
commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
 Wed Jul  9 21:29:44 2014
@@ -535,6 +535,12 @@ public class GenericKeyedObjectPool<K,T>
             } else {
                 idleObjects.addLast(p);
             }
+            if (isClosed()) {
+                // Pool closed while object was being added to idle objects.
+                // Make sure the returned object is destroyed rather than left
+                // in the idle object pool (which would effectively be a leak)
+                clear(key);
+            }
         }
 
         if (hasBorrowWaiters()) {
@@ -1430,9 +1436,8 @@ public class GenericKeyedObjectPool<K,T>
          */
         public ObjectDeque(boolean fairness) {
             idleObjects = new LinkedBlockingDeque<PooledObject<S>>(fairness);
-            
         }
-        
+
         /**
          * Obtain the idle objects for the current key.
          *

Modified: 
commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
URL: 
http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java?rev=1609308&r1=1609307&r2=1609308&view=diff
==============================================================================
--- 
commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
 (original)
+++ 
commons/proper/pool/trunk/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
 Wed Jul  9 21:29:44 2014
@@ -111,7 +111,7 @@ public class GenericObjectPool<T> extend
             throw new IllegalArgumentException("factory may not be null");
         }
         this.factory = factory;
-        
+
         idleObjects = new 
LinkedBlockingDeque<PooledObject<T>>(config.getFairness());
 
         setConfig(config);
@@ -609,6 +609,12 @@ public class GenericObjectPool<T> extend
             } else {
                 idleObjects.addLast(p);
             }
+            if (isClosed()) {
+                // Pool closed while object was being added to idle objects.
+                // Make sure the returned object is destroyed rather than left
+                // in the idle object pool (which would effectively be a leak)
+                clear();
+            }
         }
         updateStatsReturn(activeTime);
     }
@@ -904,6 +910,12 @@ public class GenericObjectPool<T> extend
                 idleObjects.addLast(p);
             }
         }
+        if (isClosed()) {
+            // Pool closed while object was being added to idle objects.
+            // Make sure the returned object is destroyed rather than left
+            // in the idle object pool (which would effectively be a leak)
+            clear();
+        }
     }
 
     /**

Modified: 
commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
URL: 
http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java?rev=1609308&r1=1609307&r2=1609308&view=diff
==============================================================================
--- 
commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
 (original)
+++ 
commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java
 Wed Jul  9 21:29:44 2014
@@ -754,6 +754,45 @@ public class TestGenericObjectPool exten
         }
     }
 
+    /**
+     * This is the test case for POOL-263. It is disabled since it will always
+     * pass without artificial delay being injected into GOP.returnObject() and
+     * a way to this this hasn't currently been found that doesn't involve
+     * polluting the GOP implementation. The artificial delay needs to be
+     * inserted just before the final call to isLifo() in the returnObject()
+     * method.
+     */
+    //@Test(timeout=60000)
+    public void testReturnObject() throws Exception {
+
+        pool.setMaxTotal(1);
+        pool.setMaxIdle(-1);
+        String active = pool.borrowObject();
+
+        assertEquals(1, pool.getNumActive());
+        assertEquals(0, pool.getNumIdle());
+
+        Thread t = new Thread() {
+
+            @Override
+            public void run() {
+                pool.close();
+            }
+
+        };
+        t.start();
+
+        pool.returnObject(active);
+
+        // Wait for the close() thread to complete
+        while (t.isAlive()) {
+            Thread.sleep(50);
+        }
+
+        assertEquals(0, pool.getNumIdle());
+    }
+
+
     @Test(timeout=60000)
     public void testSettersAndGetters() throws Exception {
         {
@@ -1396,7 +1435,7 @@ public class TestGenericObjectPool exten
             */
         }
     }
-    
+
     /**
      * Verifies that concurrent threads never "share" instances
      */
@@ -1516,7 +1555,7 @@ public class TestGenericObjectPool exten
                 boolean randomDelay, Object obj) {
             this(pool, iter, delay, delay, randomDelay, obj);
         }
-        
+
         public TestThread(ObjectPool<T> pool, int iter, int startDelay,
             int holdTime, boolean randomDelay, Object obj) {
         _pool = pool;
@@ -1827,7 +1866,7 @@ public class TestGenericObjectPool exten
             }
         }
     }
-    
+
     protected static class AtomicIntegerFactory
     extends BasePooledObjectFactory<AtomicInteger> {
 
@@ -1836,7 +1875,7 @@ public class TestGenericObjectPool exten
     private long createLatency = 0;
     private long destroyLatency = 0;
     private long validateLatency = 0;
-    
+
     @Override
     public AtomicInteger create() {
         try {
@@ -1873,7 +1912,7 @@ public class TestGenericObjectPool exten
         } catch (InterruptedException ex) {}
         return instance.getObject().intValue() == 1;
     }
-    
+
     @Override
     public void destroyObject(PooledObject<AtomicInteger> p) {
         try {
@@ -1881,7 +1920,7 @@ public class TestGenericObjectPool exten
         } catch (InterruptedException ex) {}
     }
 
-    
+
     /**
      * @param activateLatency the activateLatency to set
      */
@@ -1889,7 +1928,7 @@ public class TestGenericObjectPool exten
         this.activateLatency = activateLatency;
     }
 
-    
+
     /**
      * @param passivateLatency the passivateLatency to set
      */
@@ -1897,7 +1936,7 @@ public class TestGenericObjectPool exten
         this.passivateLatency = passivateLatency;
     }
 
-    
+
     /**
      * @param createLatency the createLatency to set
      */
@@ -1905,7 +1944,7 @@ public class TestGenericObjectPool exten
         this.createLatency = createLatency;
     }
 
-    
+
     /**
      * @param destroyLatency the destroyLatency to set
      */
@@ -1913,7 +1952,7 @@ public class TestGenericObjectPool exten
         this.destroyLatency = destroyLatency;
     }
 
-    
+
     /**
      * @param validateLatency the validateLatency to set
      */
@@ -1942,7 +1981,7 @@ public class TestGenericObjectPool exten
     })
     @Test(timeout=60000)
     public void testBorrowObjectFairness() throws Exception {
-        
+
         int numThreads = 40;
         int maxTotal = 40;
 
@@ -1951,9 +1990,9 @@ public class TestGenericObjectPool exten
         config.setMaxIdle(maxTotal);
         config.setFairness(true);
         config.setLifo(false);
-        
+
         pool = new GenericObjectPool(factory, config);
-        
+
         // Exhaust the pool
         String[] objects = new String[maxTotal];
         for (int i = 0; i < maxTotal; i++) {
@@ -1973,7 +2012,7 @@ public class TestGenericObjectPool exten
                 fail(e.toString());
             }
         }
-        
+
         // Return objects, other threads should get served in order
         for (int i = 0; i < maxTotal; i++) {
             pool.returnObject(objects[i]);
@@ -2310,7 +2349,7 @@ public class TestGenericObjectPool exten
         Assert.assertEquals(0, pool.getNumActive());
         Assert.assertEquals(1, pool.getNumIdle());
     }
-    
+
     // POOL-259
     @Test
     public void testClientWaitStats() throws Exception {
@@ -2327,8 +2366,8 @@ public class TestGenericObjectPool exten
         pool.borrowObject();
         // Second borrow does not have to wait on create, average should be 
about 50
         Assert.assertTrue(pool.getMaxBorrowWaitTimeMillis() >= 100);
-        Assert.assertTrue(pool.getMeanBorrowWaitTimeMillis() < 60);  
-        Assert.assertTrue(pool.getMeanBorrowWaitTimeMillis() > 40);  
+        Assert.assertTrue(pool.getMeanBorrowWaitTimeMillis() < 60);
+        Assert.assertTrue(pool.getMeanBorrowWaitTimeMillis() > 40);
     }
 
     private static final class DummyFactory


Reply via email to