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