This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-pool.git
commit a6930dbc5d88e1001364b5638c74268ebd9bdc55 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Mon Feb 15 18:47:04 2021 -0500 Add and use java.time.Duration APIs timeouts instead of using ints for seconds. --- .../org/apache/commons/pool2/PooledObject.java | 29 +++++- .../commons/pool2/impl/BaseGenericObjectPool.java | 25 +++-- .../commons/pool2/impl/GenericKeyedObjectPool.java | 5 +- .../commons/pool2/impl/GenericObjectPool.java | 102 +++++++++++++++------ .../pool2/impl/TestBaseGenericObjectPool.java | 18 ++-- .../pool2/impl/TestDefaultPooledObject.java | 25 +++-- 6 files changed, 142 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/apache/commons/pool2/PooledObject.java b/src/main/java/org/apache/commons/pool2/PooledObject.java index 96f726d..2e46997 100644 --- a/src/main/java/org/apache/commons/pool2/PooledObject.java +++ b/src/main/java/org/apache/commons/pool2/PooledObject.java @@ -17,6 +17,7 @@ package org.apache.commons.pool2; import java.io.PrintWriter; +import java.time.Duration; import java.util.Deque; /** @@ -75,7 +76,19 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> { boolean equals(Object obj); /** - * Gets the time in milliseconds that this object last spent in the + * Gets the amount of time this object last spent in the + * active state (it may still be active in which case subsequent calls will + * return an increased value). + * + * @return The duration last spent in the active state + * @since 2.12.0 + */ + default Duration getActiveTime() { + return Duration.ofMillis(getActiveTimeMillis()); + } + + /** + * Gets the amount of time in milliseconds this object last spent in the * active state (it may still be active in which case subsequent calls will * return an increased value). * @@ -102,7 +115,19 @@ public interface PooledObject<T> extends Comparable<PooledObject<T>> { long getCreateTime(); /** - * Gets the time in milliseconds that this object last spend in the + * Gets the amount of time that this object last spend in the + * idle state (it may still be idle in which case subsequent calls will + * return an increased value). + * + * @return The amount of time in last spent in the idle state. + * @since 2.10.0 + */ + default Duration getIdleTime() { + return Duration.ofMillis(getIdleTimeMillis()); + } + + /** + * Gets the amount of time in milliseconds that this object last spend in the * idle state (it may still be idle in which case subsequent calls will * return an increased value). * diff --git a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java index a0d05c9..1ab282a 100644 --- a/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java +++ b/src/main/java/org/apache/commons/pool2/impl/BaseGenericObjectPool.java @@ -248,7 +248,7 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject { * * @param size number of values to maintain in the cache. */ - public StatsStore(final int size) { + StatsStore(final int size) { this.size = size; values = new AtomicLong[size]; for (int i = 0; i < size; i++) { @@ -256,13 +256,17 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject { } } + void add(Duration value) { + add(value.toMillis()); + } + /** * Adds a value to the cache. If the cache is full, one of the * existing values is replaced by the new value. * * @param value new value to add to the cache. */ - public synchronized void add(final long value) { + synchronized void add(final long value) { values[index].set(value); index++; if (index == size) { @@ -1544,30 +1548,33 @@ public abstract class BaseGenericObjectPool<T> extends BaseObject { /** * Updates statistics after an object is borrowed from the pool. + * * @param p object borrowed from the pool * @param waitTime time (in milliseconds) that the borrowing thread had to wait */ - final void updateStatsBorrow(final PooledObject<T> p, final long waitTime) { + final void updateStatsBorrow(final PooledObject<T> p, final Duration waitTime) { borrowedCount.incrementAndGet(); - idleTimes.add(p.getIdleTimeMillis()); + idleTimes.add(p.getIdleTime()); waitTimes.add(waitTime); + final long waitTimeMillis = waitTime.toMillis(); // lock-free optimistic-locking maximum - long currentMax; + long currentMaxMillis; do { - currentMax = maxBorrowWaitTimeMillis.get(); - if (currentMax >= waitTime) { + currentMaxMillis = maxBorrowWaitTimeMillis.get(); + if (currentMaxMillis >= waitTimeMillis) { break; } - } while (!maxBorrowWaitTimeMillis.compareAndSet(currentMax, waitTime)); + } while (!maxBorrowWaitTimeMillis.compareAndSet(currentMaxMillis, waitTimeMillis)); } /** * Updates statistics after an object is returned to the pool. + * * @param activeTime the amount of time (in milliseconds) that the returning * object was checked out */ - final void updateStatsReturn(final long activeTime) { + final void updateStatsReturn(final Duration activeTime) { returnedCount.incrementAndGet(); activeTimes.add(activeTime); } 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 e4098c4..50c7f2b 100644 --- a/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java +++ b/src/main/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java @@ -16,6 +16,7 @@ */ package org.apache.commons.pool2.impl; +import java.time.Duration; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; @@ -474,7 +475,7 @@ public class GenericKeyedObjectPool<K, T> extends BaseGenericObjectPool<T> deregister(key); } - updateStatsBorrow(p, System.currentTimeMillis() - waitTimeMillis); + updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - waitTimeMillis)); return p.getObject(); } @@ -1427,7 +1428,7 @@ public class GenericKeyedObjectPool<K, T> extends BaseGenericObjectPool<T> markReturningState(p); - final long activeTime = p.getActiveTimeMillis(); + final Duration activeTime = p.getActiveTime(); try { if (getTestOnReturn() && !factory.validateObject(key, p)) { 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 e602a5e..97d38a2 100644 --- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java +++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java @@ -16,16 +16,6 @@ */ package org.apache.commons.pool2.impl; -import org.apache.commons.pool2.DestroyMode; -import org.apache.commons.pool2.ObjectPool; -import org.apache.commons.pool2.PoolUtils; -import org.apache.commons.pool2.PooledObject; -import org.apache.commons.pool2.PooledObjectFactory; -import org.apache.commons.pool2.PooledObjectState; -import org.apache.commons.pool2.SwallowedExceptionListener; -import org.apache.commons.pool2.TrackedUse; -import org.apache.commons.pool2.UsageTracking; - import java.time.Duration; import java.util.ArrayList; import java.util.HashSet; @@ -34,9 +24,18 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import org.apache.commons.pool2.DestroyMode; +import org.apache.commons.pool2.ObjectPool; +import org.apache.commons.pool2.PoolUtils; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.PooledObjectFactory; +import org.apache.commons.pool2.PooledObjectState; +import org.apache.commons.pool2.SwallowedExceptionListener; +import org.apache.commons.pool2.TrackedUse; +import org.apache.commons.pool2.UsageTracking; + /** * A configurable {@link ObjectPool} implementation. * <p> @@ -272,7 +271,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> * available instances in request arrival order. * </p> * - * @param borrowMaxWaitMillis The time to wait in milliseconds for an object + * @param borrowMaxWait The time to wait for an object * to become available * * @return object instance from the pool @@ -281,14 +280,14 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> * * @throws Exception if an object instance cannot be returned due to an * error + * @since 2.10.0 */ - public T borrowObject(final long borrowMaxWaitMillis) throws Exception { + public T borrowObject(final Duration borrowMaxWait) throws Exception { assertOpen(); final AbandonedConfig ac = this.abandonedConfig; - if (ac != null && ac.getRemoveAbandonedOnBorrow() && - (getNumIdle() < 2) && - (getNumActive() > getMaxTotal() - 3) ) { + if (ac != null && ac.getRemoveAbandonedOnBorrow() && (getNumIdle() < 2) && + (getNumActive() > getMaxTotal() - 3)) { removeAbandoned(ac); } @@ -312,16 +311,14 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> } if (blockWhenExhausted) { if (p == null) { - if (borrowMaxWaitMillis < 0) { + if (borrowMaxWait.isNegative()) { p = idleObjects.takeFirst(); } else { - p = idleObjects.pollFirst(borrowMaxWaitMillis, - TimeUnit.MILLISECONDS); + p = idleObjects.pollFirst(borrowMaxWait); } } if (p == null) { - throw new NoSuchElementException( - "Timeout waiting for idle object"); + throw new NoSuchElementException("Timeout waiting for idle object"); } } else { if (p == null) { @@ -343,8 +340,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> } p = null; if (create) { - final NoSuchElementException nsee = new NoSuchElementException( - "Unable to activate object"); + final NoSuchElementException nsee = new NoSuchElementException("Unable to activate object"); nsee.initCause(e); throw nsee; } @@ -367,8 +363,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> } p = null; if (create) { - final NoSuchElementException nsee = new NoSuchElementException( - "Unable to validate object"); + final NoSuchElementException nsee = new NoSuchElementException("Unable to validate object"); nsee.initCause(validationThrowable); throw nsee; } @@ -377,12 +372,65 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> } } - updateStatsBorrow(p, System.currentTimeMillis() - waitTimeMillis); + updateStatsBorrow(p, Duration.ofMillis(System.currentTimeMillis() - waitTimeMillis)); return p.getObject(); } /** + * Borrows an object from the pool using the specific waiting time which only + * applies if {@link #getBlockWhenExhausted()} is true. + * <p> + * If there is one or more idle instance available in the pool, then an + * idle instance will be selected based on the value of {@link #getLifo()}, + * activated and returned. If activation fails, or {@link #getTestOnBorrow() + * testOnBorrow} is set to {@code true} and validation fails, the + * instance is destroyed and the next available instance is examined. This + * continues until either a valid instance is returned or there are no more + * idle instances available. + * </p> + * <p> + * If there are no idle instances available in the pool, behavior depends on + * the {@link #getMaxTotal() maxTotal}, (if applicable) + * {@link #getBlockWhenExhausted()} and the value passed in to the + * {@code borrowMaxWaitMillis} parameter. If the number of instances + * checked out from the pool is less than {@code maxTotal,} a new + * instance is created, activated and (if applicable) validated and returned + * to the caller. If validation fails, a {@code NoSuchElementException} + * is thrown. + * </p> + * <p> + * If the pool is exhausted (no available idle instances and no capacity to + * create new ones), this method will either block (if + * {@link #getBlockWhenExhausted()} is true) or throw a + * {@code NoSuchElementException} (if + * {@link #getBlockWhenExhausted()} is false). The length of time that this + * method will block when {@link #getBlockWhenExhausted()} is true is + * determined by the value passed in to the {@code borrowMaxWaitMillis} + * parameter. + * </p> + * <p> + * When the pool is exhausted, multiple calling threads may be + * simultaneously blocked waiting for instances to become available. A + * "fairness" algorithm has been implemented to ensure that threads receive + * available instances in request arrival order. + * </p> + * + * @param borrowMaxWaitMillis The time to wait in milliseconds for an object + * to become available + * + * @return object instance from the pool + * + * @throws NoSuchElementException if an instance cannot be returned + * + * @throws Exception if an object instance cannot be returned due to an + * error + */ + public T borrowObject(final long borrowMaxWaitMillis) throws Exception { + return borrowObject(Duration.ofMillis(borrowMaxWaitMillis)); + } + + /** * Clears any objects sitting idle in the pool by removing them from the * idle instance pool and then invoking the configured * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each @@ -1080,7 +1128,7 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T> markReturningState(p); - final long activeTime = p.getActiveTimeMillis(); + final Duration activeTime = p.getActiveTime(); if (getTestOnReturn() && !factory.validateObject(p)) { try { diff --git a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java index 30307d2..6beb283 100644 --- a/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java +++ b/src/test/java/org/apache/commons/pool2/impl/TestBaseGenericObjectPool.java @@ -50,7 +50,7 @@ public class TestBaseGenericObjectPool { @Test public void testActiveTimeStatistics() { for (int i = 0; i < 99; i++) { // must be < MEAN_TIMING_STATS_CACHE_SIZE - pool.updateStatsReturn(i); + pool.updateStatsReturn(Duration.ofMillis(i)); } assertEquals(49, pool.getMeanActiveTimeMillis(), Double.MIN_VALUE); } @@ -58,10 +58,10 @@ public class TestBaseGenericObjectPool { @Test public void testBorrowWaitStatistics() { final DefaultPooledObject<String> p = (DefaultPooledObject<String>) factory.makeObject(); - pool.updateStatsBorrow(p, 10); - pool.updateStatsBorrow(p, 20); - pool.updateStatsBorrow(p, 20); - pool.updateStatsBorrow(p, 30); + pool.updateStatsBorrow(p, Duration.ofMillis(10)); + pool.updateStatsBorrow(p, Duration.ofMillis(20)); + pool.updateStatsBorrow(p, Duration.ofMillis(20)); + pool.updateStatsBorrow(p, Duration.ofMillis(30)); assertEquals(20, pool.getMeanBorrowWaitTimeMillis(), Double.MIN_VALUE); assertEquals(30, pool.getMaxBorrowWaitTimeMillis(), 0); } @@ -69,13 +69,13 @@ public class TestBaseGenericObjectPool { public void testBorrowWaitStatisticsMax() { final DefaultPooledObject<String> p = (DefaultPooledObject<String>) factory.makeObject(); assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); - pool.updateStatsBorrow(p, 0); + pool.updateStatsBorrow(p, Duration.ZERO); assertEquals(0, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); - pool.updateStatsBorrow(p, 20); + pool.updateStatsBorrow(p, Duration.ofMillis(20)); assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); - pool.updateStatsBorrow(p, 20); + pool.updateStatsBorrow(p, Duration.ofMillis(20)); assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); - pool.updateStatsBorrow(p, 10); + pool.updateStatsBorrow(p, Duration.ofMillis(10)); assertEquals(20, pool.getMaxBorrowWaitTimeMillis(), Double.MIN_VALUE); } diff --git a/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java b/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java index cc3f63e..ad02d5c 100644 --- a/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java +++ b/src/test/java/org/apache/commons/pool2/impl/TestDefaultPooledObject.java @@ -35,21 +35,20 @@ public class TestDefaultPooledObject { * @throws Exception May occur in some failure modes */ @Test - public void testgetIdleTimeMillis() throws Exception { + public void testGetIdleTimeMillis() throws Exception { final DefaultPooledObject<Object> dpo = new DefaultPooledObject<>(new Object()); final AtomicBoolean negativeIdleTimeReturned = new AtomicBoolean(false); - final ExecutorService executor = Executors.newFixedThreadPool( - Runtime.getRuntime().availableProcessors()*3); + final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 3); final Runnable allocateAndDeallocateTask = () -> { - for (int i1=0;i1<10000;i1++) { - if (dpo.getIdleTimeMillis() < 0) { + for (int i1 = 0; i1 < 10000; i1++) { + if (dpo.getIdleTime().isNegative()) { negativeIdleTimeReturned.set(true); break; } } dpo.allocate(); - for (int i2=0;i2<10000;i2++) { - if (dpo.getIdleTimeMillis() < 0) { + for (int i2 = 0; i2 < 10000; i2++) { + if (dpo.getIdleTime().isNegative()) { negativeIdleTimeReturned.set(true); break; } @@ -57,8 +56,8 @@ public class TestDefaultPooledObject { dpo.deallocate(); }; final Runnable getIdleTimeTask = () -> { - for (int i=0;i<10000;i++) { - if (dpo.getIdleTimeMillis() < 0) { + for (int i = 0; i < 10000; i++) { + if (dpo.getIdleTime().isNegative()) { negativeIdleTimeReturned.set(true); break; } @@ -67,15 +66,15 @@ public class TestDefaultPooledObject { final double probabilityOfAllocationTask = 0.7; final List<Future<?>> futures = new ArrayList<>(); for (int i = 1; i <= 10000; i++) { - final Runnable randomTask = Math.random() < probabilityOfAllocationTask ? - allocateAndDeallocateTask : getIdleTimeTask; + final Runnable randomTask = Math.random() < probabilityOfAllocationTask ? allocateAndDeallocateTask + : getIdleTimeTask; futures.add(executor.submit(randomTask)); } - for (final Future<?> future: futures) { + for (final Future<?> future : futures) { future.get(); } assertFalse(negativeIdleTimeReturned.get(), - "DefaultPooledObject.getIdleTimeMillis() returned a negative value"); + "DefaultPooledObject.getIdleTimeMillis() returned a negative value"); } } \ No newline at end of file