Author: psteitz
Date: Sat Dec 29 18:23:33 2012
New Revision: 1426799

URL: http://svn.apache.org/viewvc?rev=1426799&view=rev
Log:
Fixed threadsafety problem in GOP, GKOP invalidateObject. JIRA: POOL-231.

Modified:
    
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/TestGenericKeyedObjectPool.java
    
commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java

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=1426799&r1=1426798&r2=1426799&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
 Sat Dec 29 18:23:33 2012
@@ -551,7 +551,11 @@ public class GenericKeyedObjectPool<K,T>
             throw new IllegalStateException(
                     "Object not currently part of this pool");
         }
-        destroy(key, p, true);
+        synchronized (p) {
+            if (p.getState() != PooledObjectState.INVALID) { 
+                destroy(key, p, true);
+            }
+        }
     }
 
 

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=1426799&r1=1426798&r2=1426799&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
 Sat Dec 29 18:23:33 2012
@@ -581,10 +581,14 @@ public class GenericObjectPool<T> extend
                 return;
             } else {
                 throw new IllegalStateException(
-                        "Returned object not currently part of this pool");
+                        "Invalidated object not currently part of this pool");
             }
         }   
-        destroy(p);
+        synchronized (p) {
+            if (p.getState() != PooledObjectState.INVALID) { 
+                destroy(p);
+            }
+        }
     }
 
     /**

Modified: 
commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
URL: 
http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java?rev=1426799&r1=1426798&r2=1426799&view=diff
==============================================================================
--- 
commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
 (original)
+++ 
commons/proper/pool/trunk/src/test/java/org/apache/commons/pool2/impl/TestGenericKeyedObjectPool.java
 Sat Dec 29 18:23:33 2012
@@ -1433,6 +1433,88 @@ public class TestGenericKeyedObjectPool 
         // Check thread was interrupted
         assertTrue(wtt._thrown instanceof InterruptedException);
     }
+    
+    /**
+     * POOL-231 - verify that concurrent invalidates of the same object do not
+     * corrupt pool destroyCount.
+     */
+    @Test
+    public void testConcurrentInvalidate() throws Exception {
+        // Get allObjects and idleObjects loaded with some instances
+        final int nObjects = 1000;
+        final String key = "one";
+        pool.setMaxTotal(nObjects);
+        pool.setMaxTotalPerKey(nObjects);
+        pool.setMaxIdlePerKey(nObjects);
+        final String [] obj = new String[nObjects];
+        for (int i = 0; i < nObjects; i++) {
+            obj[i] = pool.borrowObject(key);
+        }
+        for (int i = 0; i < nObjects; i++) {
+            if (i % 2 == 0) {
+                pool.returnObject(key, obj[i]);
+            }
+        }
+        final int nThreads = 20;
+        final int nIterations = 60;
+        final InvalidateThread[] threads = new InvalidateThread[nThreads];
+        // Randomly generated list of distinct invalidation targets
+        final ArrayList<Integer> targets = new ArrayList<Integer>();
+        final Random random = new Random();
+        for (int j = 0; j < nIterations; j++) {
+            // Get a random invalidation target
+            Integer targ = new Integer(random.nextInt(nObjects));
+            while (targets.contains(targ)) {
+                targ = new Integer(random.nextInt(nObjects));
+            }
+            targets.add(targ);
+            // Launch nThreads threads all trying to invalidate the target
+            for (int i = 0; i < nThreads; i++) {
+                threads[i] = new InvalidateThread(pool,key, obj[targ]);
+            }
+            for (int i = 0; i < nThreads; i++) {
+                new Thread(threads[i]).start();
+            }
+            boolean done = false;
+            while (!done) {
+                done = true;
+                for (int i = 0; i < nThreads; i++) {
+                    done = done && threads[i].complete();
+                }
+                Thread.sleep(100);
+            }
+        }
+        Assert.assertEquals(nIterations, pool.getDestroyedCount());
+    }
+    
+    /**
+     * Attempts to invalidate an object, swallowing IllegalStateException.
+     */
+    static class InvalidateThread implements Runnable {
+        private final String obj;
+        private final KeyedObjectPool<String, String> pool;
+        private final String key;
+        private boolean done = false;
+        public InvalidateThread(KeyedObjectPool<String, String> pool, String 
key, String obj) {
+            this.obj = obj;
+            this.pool = pool;
+            this.key = key;
+        }
+        public void run() {
+            try {
+                pool.invalidateObject(key, obj);
+            } catch (IllegalStateException ex) {
+                // Ignore
+            } catch (Exception ex) {
+                Assert.fail("Unexpected exception " + ex.toString());
+            } finally {
+                done = true;
+            }
+        }
+        public boolean complete() {
+            return done;
+        }
+    }
 
     /*
      * Very simple test thread that just tries to borrow an object from

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=1426799&r1=1426798&r2=1426799&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
 Sat Dec 29 18:23:33 2012
@@ -1100,6 +1100,84 @@ public class TestGenericObjectPool exten
         assertEquals("Total count different than expected.", 0, 
pool.getNumActive());
         pool.close();
     }
+    
+    /**
+     * POOL-231 - verify that concurrent invalidates of the same object do not
+     * corrupt pool destroyCount.
+     */
+    @Test
+    public void testConcurrentInvalidate() throws Exception {
+        // Get allObjects and idleObjects loaded with some instances
+        final int nObjects = 1000;
+        pool.setMaxTotal(nObjects);
+        pool.setMaxIdle(nObjects);
+        final Object[] obj = new Object[nObjects];
+        for (int i = 0; i < nObjects; i++) {
+            obj[i] = pool.borrowObject();
+        }
+        for (int i = 0; i < nObjects; i++) {
+            if (i % 2 == 0) {
+                pool.returnObject(obj[i]);
+            }
+        }
+        final int nThreads = 20;
+        final int nIterations = 60;
+        final InvalidateThread[] threads = new InvalidateThread[nThreads];
+        // Randomly generated list of distinct invalidation targets
+        final ArrayList<Integer> targets = new ArrayList<Integer>();
+        final Random random = new Random();
+        for (int j = 0; j < nIterations; j++) {
+            // Get a random invalidation target
+            Integer targ = new Integer(random.nextInt(nObjects));
+            while (targets.contains(targ)) {
+                targ = new Integer(random.nextInt(nObjects));
+            }
+            targets.add(targ);
+            // Launch nThreads threads all trying to invalidate the target
+            for (int i = 0; i < nThreads; i++) {
+                threads[i] = new InvalidateThread(pool, obj[targ]);
+            }
+            for (int i = 0; i < nThreads; i++) {
+                new Thread(threads[i]).start();
+            }
+            boolean done = false;
+            while (!done) {
+                done = true;
+                for (int i = 0; i < nThreads; i++) {
+                    done = done && threads[i].complete();
+                }
+                Thread.sleep(100);
+            }
+        }
+        Assert.assertEquals(nIterations, pool.getDestroyedCount());
+    }
+    
+    /**
+     * Attempts to invalidate an object, swallowing IllegalStateException.
+     */
+    static class InvalidateThread implements Runnable {
+        private final Object obj;
+        private final ObjectPool<Object> pool;
+        private boolean done = false;
+        public InvalidateThread(ObjectPool<Object> pool, Object obj) {
+            this.obj = obj;
+            this.pool = pool;
+        }
+        public void run() {
+            try {
+                pool.invalidateObject(obj);
+            } catch (IllegalStateException ex) {
+                // Ignore
+            } catch (Exception ex) {
+                Assert.fail("Unexpected exception " + ex.toString());
+            } finally {
+                done = true;
+            }
+        }
+        public boolean complete() {
+            return done;
+        }
+    }
 
     @Test(timeout=60000)
     public void testMinIdle() throws Exception {


Reply via email to