Re-working of fix for POOL-303 git-svn-id: https://svn.apache.org/repos/asf/commons/proper/pool/trunk@1735269 13f79535-47bb-0310-9956-ffa450edef68
Project: http://git-wip-us.apache.org/repos/asf/commons-pool/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-pool/commit/a4c544a2 Tree: http://git-wip-us.apache.org/repos/asf/commons-pool/tree/a4c544a2 Diff: http://git-wip-us.apache.org/repos/asf/commons-pool/diff/a4c544a2 Branch: refs/heads/master Commit: a4c544a24242701673073d32d2ddbf037fac0099 Parents: 170a509 Author: Mark Thomas <ma...@apache.org> Authored: Wed Mar 16 17:20:41 2016 +0000 Committer: Mark Thomas <ma...@apache.org> Committed: Wed Mar 16 17:20:41 2016 +0000 ---------------------------------------------------------------------- .../pool2/impl/GenericKeyedObjectPool.java | 53 ++++++++++++++++---- .../commons/pool2/impl/GenericObjectPool.java | 51 ++++++++++++++++--- .../pool2/impl/TestGenericObjectPool.java | 6 ++- 3 files changed, 93 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-pool/blob/a4c544a2/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java ---------------------------------------------------------------------- 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 dcfe448..d477cfa 100644 --- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java +++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java @@ -1017,26 +1017,58 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> } } - final long newCreateCount = objectDeque.getCreateCount().incrementAndGet(); + // Flag that indicates if create should: + // - TRUE: call the factory to create an object + // - FALSE: return null + // - null: loop and re-test the condition that determines whether to + // call the factory + Boolean create = null; + while (create == null) { + synchronized (objectDeque.makeObjectCountLock) { + final long newCreateCount = objectDeque.getCreateCount().incrementAndGet(); + // Check against the per key limit + if (newCreateCount > maxTotalPerKeySave) { + // The key is currently at capacity or in the process of + // making enough new objects to take it to capacity. + numTotal.decrementAndGet(); + objectDeque.getCreateCount().decrementAndGet(); + if (objectDeque.makeObjectCount == 0) { + // There are no makeObject() calls in progress for this + // key so the key is at capacity. Do not attempt to + // create a new object. Return and wait for an object to + // be returned. + create = Boolean.FALSE; + } else { + // There are makeObject() calls in progress that might + // bring the pool to capacity. Those calls might also + // fail so wait until they complete and then re-test if + // the pool is at capacity or not. + objectDeque.makeObjectCountLock.wait(); + } + } else { + // The pool is not at capacity. Create a new object. + objectDeque.makeObjectCount++; + create = Boolean.TRUE; + } + } + } - // Check against the per key limit - if (newCreateCount > maxTotalPerKeySave) { - numTotal.decrementAndGet(); - objectDeque.getCreateCount().decrementAndGet(); + if (!create.booleanValue()) { return null; } - PooledObject<T> p = null; try { p = factory.makeObject(key); } catch (final Exception e) { numTotal.decrementAndGet(); objectDeque.getCreateCount().decrementAndGet(); - // POOL-303. There may be threads waiting on an object return that - // isn't going to happen. Unblock them. - objectDeque.idleObjects.interuptTakeWaiters(); throw e; + } finally { + synchronized (objectDeque.makeObjectCountLock) { + objectDeque.makeObjectCount--; + objectDeque.makeObjectCountLock.notifyAll(); + } } createdCount.incrementAndGet(); @@ -1431,6 +1463,9 @@ public class GenericKeyedObjectPool<K,T> extends BaseGenericObjectPool<T> */ private final AtomicInteger createCount = new AtomicInteger(0); + private long makeObjectCount = 0; + private final Object makeObjectCountLock = new Object(); + /* * The map is keyed on pooled instances, wrapped to ensure that * they work properly as keys. http://git-wip-us.apache.org/repos/asf/commons-pool/blob/a4c544a2/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java index 0e0c9bd..487eec2 100644 --- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java +++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java @@ -842,24 +842,59 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> */ private PooledObject<T> create() throws Exception { int localMaxTotal = getMaxTotal(); + // This simplifies the code later in this method if (localMaxTotal < 0) { localMaxTotal = Integer.MAX_VALUE; } - final long newCreateCount = createCount.incrementAndGet(); - if (newCreateCount > localMaxTotal) { - createCount.decrementAndGet(); + + // Flag that indicates if create should: + // - TRUE: call the factory to create an object + // - FALSE: return null + // - null: loop and re-test the condition that determines whether to + // call the factory + Boolean create = null; + while (create == null) { + synchronized (makeObjectCountLock) { + final long newCreateCount = createCount.incrementAndGet(); + if (newCreateCount > localMaxTotal) { + // The pool is currently at capacity or in the process of + // making enough new objects to take it to capacity. + createCount.decrementAndGet(); + if (makeObjectCount == 0) { + // There are no makeObject() calls in progress so the + // pool is at capacity. Do not attempt to create a new + // object. Return and wait for an object to be returned + create = Boolean.FALSE; + } else { + // There are makeObject() calls in progress that might + // bring the pool to capacity. Those calls might also + // fail so wait until they complete and then re-test if + // the pool is at capacity or not. + makeObjectCountLock.wait(); + } + } else { + // The pool is not at capacity. Create a new object. + makeObjectCount++; + create = Boolean.TRUE; + } + } + } + + if (!create.booleanValue()) { return null; } final PooledObject<T> p; try { p = factory.makeObject(); - } catch (final Exception e) { + } catch (Exception e) { createCount.decrementAndGet(); - // POOL-303. There may be threads waiting on an object return that - // isn't going to happen. Unblock them. - idleObjects.interuptTakeWaiters(); throw e; + } finally { + synchronized (makeObjectCountLock) { + makeObjectCount--; + makeObjectCountLock.notifyAll(); + } } final AbandonedConfig ac = this.abandonedConfig; @@ -1129,6 +1164,8 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> * {@link #_maxActive} objects created at any one time. */ private final AtomicLong createCount = new AtomicLong(0); + private long makeObjectCount = 0; + private final Object makeObjectCountLock = new Object(); private final LinkedBlockingDeque<PooledObject<T>> idleObjects; // JMX specific attributes http://git-wip-us.apache.org/repos/asf/commons-pool/blob/a4c544a2/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java index f951691..5890146 100644 --- a/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java +++ b/src/test/java/org/apache/commons/pool2/impl/TestGenericObjectPool.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.lang.management.ManagementFactory; +import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -2590,6 +2591,9 @@ public class TestGenericObjectPool extends TestBaseObjectPool { } Assert.assertFalse(thread1.isAlive()); Assert.assertFalse(thread2.isAlive()); + + Assert.assertTrue(thread1._thrown instanceof UnsupportedCharsetException); + Assert.assertTrue(thread2._thrown instanceof UnsupportedCharsetException); } private static class CreateFailFactory extends BasePooledObjectFactory<String> { @@ -2599,7 +2603,7 @@ public class TestGenericObjectPool extends TestBaseObjectPool { @Override public String create() throws Exception { semaphore.acquire(); - throw new Exception(); + throw new UnsupportedCharsetException("wibble"); } @Override