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 <pste...@apache.org>
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);
         }
     }
-    
+
 }
 
 

Reply via email to