This is an automated email from the ASF dual-hosted git repository.
psteitz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-pool.git
The following commit(s) were added to refs/heads/master by this push:
new 7d0b459 Complete fix for POOL-326
new 81ddcd7 Merge branch 'master' of
https://git-wip-us.apache.org/repos/asf/commons-pool
7d0b459 is described below
commit 7d0b45940238918033094684343551f90524e600
Author: psteitz <[email protected]>
AuthorDate: Fri Oct 4 17:57:01 2019 -0700
Complete fix for POOL-326
---
.../commons/pool2/impl/GenericKeyedObjectPool.java | 12 +++--
.../pool2/impl/TestGenericKeyedObjectPool.java | 63 +++++++++++++++++++++-
2 files changed, 70 insertions(+), 5 deletions(-)
diff --git
a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
index 6d8ec69..4cc69ed 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
@@ -1086,8 +1086,13 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
final ObjectDeque<T> objectDeque = register(key);
try {
- final boolean isIdle =
objectDeque.getIdleObjects().remove(toDestroy);
-
+ // Check idle state directly
+ boolean isIdle =
toDestroy.getState().equals(PooledObjectState.IDLE);
+ // If idle, not under eviction test, or always is true, remove
instance,
+ // updating isIdle if instance is found in idle objects
+ if (isIdle || always) {
+ isIdle = objectDeque.getIdleObjects().remove(toDestroy);
+ }
if (isIdle || always) {
objectDeque.getAllObjects().remove(new
IdentityWrapper<>(toDestroy.getObject()));
toDestroy.invalidate();
@@ -1170,8 +1175,7 @@ public class GenericKeyedObjectPool<K, T> extends
BaseGenericObjectPool<T>
lock.unlock();
lock = keyLock.writeLock();
lock.lock();
- if (objectDeque.getCreateCount().get() == 0 &&
objectDeque.getNumInterested().get() == 0
- && objectDeque.allObjects.isEmpty()) {
+ if (objectDeque.getCreateCount().get() == 0 &&
objectDeque.getNumInterested().get() == 0) {
// NOTE: Keys must always be removed from both poolMap and
// poolKeyList at the same time while protected by
// keyLock.writeLock()
diff --git
a/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
b/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
index f148bc0..d9247a7 100644
---
a/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
+++
b/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
@@ -2312,6 +2312,67 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
assertEquals(1, gkoPool.getNumActive());
}
+ // POOL-326
+ @Test
+ public void testEvictorClearOldestRace() throws Exception {
+ gkoPool.setMinEvictableIdleTimeMillis(100);
+ gkoPool.setNumTestsPerEvictionRun(1);
+
+ // Introduce latency between when evictor starts looking at an
instance and when
+ // it decides to destroy it
+ gkoPool.setEvictionPolicy(new SlowEvictionPolicy<String>(1000));
+
+ // Borrow an instance
+ String val = gkoPool.borrowObject("foo");
+
+ // Add another idle one
+ gkoPool.addObject("foo");
+
+ // Sleep long enough so idle one is eligible for eviction
+ Thread.sleep(1000);
+
+ // Start evictor and race with clearOldest
+ gkoPool.setTimeBetweenEvictionRunsMillis(10);
+
+ // Wait for evictor to start
+ Thread.sleep(100);
+ gkoPool.clearOldest();
+
+ // Wait for slow evictor to complete
+ Thread.sleep(1500);
+
+ // See if we get NPE on return (POOL-326)
+ gkoPool.returnObject("foo", val);
+ }
+
+
+ /*
+ * DefaultEvictionPolicy modified to add latency
+ */
+ private static class SlowEvictionPolicy<T> extends
DefaultEvictionPolicy<T> {
+ private final long delay;
+
+ /**
+ * Create SlowEvictionPolicy with the given delay in ms
+ *
+ * @param delay number of ms of latency to inject in evict
+ */
+ public SlowEvictionPolicy(long delay) {
+ super();
+ this.delay = delay;
+ }
+
+ @Override
+ public boolean evict(final EvictionConfig config, final
PooledObject<T> underTest,
+ final int idleCount) {
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ return super.evict(config, underTest, idleCount);
+ }
+ }
private static class DummyFactory
extends BaseKeyedPooledObjectFactory<Object,Object> {
@@ -2378,7 +2439,7 @@ public class TestGenericKeyedObjectPool extends
TestKeyedObjectPool {
return new DefaultPooledObject<>(value);
}
}
-
+
}