This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch POOL_2_X
in repository https://gitbox.apache.org/repos/asf/commons-pool.git


The following commit(s) were added to refs/heads/POOL_2_X by this push:
     new 9a5664d6 The maximum wait time for GenericObjectPool.borrowObject(*) 
may exceed expectations due to a spurious thread wakeup
9a5664d6 is described below

commit 9a5664d64914f692d5dd6b71839a8368730a4cd6
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Nov 24 18:12:19 2024 -0500

    The maximum wait time for GenericObjectPool.borrowObject(*) may exceed
    expectations due to a spurious thread wakeup
---
 src/changes/changes.xml                              |  1 +
 .../apache/commons/pool2/impl/GenericObjectPool.java | 20 +++++++++++---------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6c7ce5d0..0c5d6bc1 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -49,6 +49,7 @@ The <action> type attribute can be add,update,fix,remove.
     <!-- FIX -->
     <action type="fix" dev="ggregory" due-to="Gary Gregory">Use 
java.time.Instant precision in 
org.apache.commons.pool2.impl.ThrowableCallStack.Snapshot throwable 
message.</action> 
     <action type="fix" dev="ggregory" due-to="Gary 
Gregory">GenericObjectPool.borrowObject(Duration) doesn't obey its 
borrowMaxWait Duration argument when the argument is different from 
GenericObjectPool.getMaxWaitDuration().</action>
+    <action type="fix" issue="POOL-418" dev="ggregory" due-to="Gary 
Gregory">The maximum wait time for GenericObjectPool.borrowObject(*) may exceed 
expectations due to a spurious thread wakeup.</action>
     <!-- ADD -->
     <!-- UPDATE -->
     <action type="update" dev="ggregory">Bump 
org.apache.commons:commons-parent from 62 to 73.</action>
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 f4a7c9d2..ba27b250 100644
--- a/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java
@@ -87,7 +87,9 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         "org.apache.commons.pool2:type=GenericObjectPool,name=";
 
     private static void wait(final Object obj, final Duration duration) throws 
InterruptedException {
-        obj.wait(duration.toMillis(), duration.getNano() % 1_000_000);
+        if (!duration.isNegative()) {
+            obj.wait(duration.toMillis(), duration.getNano() % 1_000_000);
+        }
     }
 
     private volatile String factoryType;
@@ -506,15 +508,14 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
      * @throws Exception if the object factory's {@code makeObject} fails
      */
     private PooledObject<T> create(final Duration maxWaitDuration) throws 
Exception {
+        final Instant startInstant = Instant.now();
+        Duration remainingWaitDuration = maxWaitDuration.isNegative() ? 
Duration.ZERO : maxWaitDuration;
         int localMaxTotal = getMaxTotal();
         // This simplifies the code later in this method
         if (localMaxTotal < 0) {
             localMaxTotal = Integer.MAX_VALUE;
         }
-
         final Instant localStartInstant = Instant.now();
-        final Duration localMaxWaitDuration = maxWaitDuration.isNegative() ? 
Duration.ZERO : maxWaitDuration;
-
         // Flag that indicates if create should:
         // - TRUE:  call the factory to create an object
         // - FALSE: return null
@@ -522,6 +523,8 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
         //          call the factory
         Boolean create = null;
         while (create == null) {
+            // remainingWaitDuration handles spurious wakeup from wait().
+            remainingWaitDuration = 
remainingWaitDuration.minus(durationSince(startInstant));
             synchronized (makeObjectCountLock) {
                 final long newCreateCount = createCount.incrementAndGet();
                 if (newCreateCount > localMaxTotal) {
@@ -538,7 +541,7 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                         // 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.
-                        wait(makeObjectCountLock, localMaxWaitDuration);
+                        wait(makeObjectCountLock, remainingWaitDuration);
                     }
                 } else {
                     // The pool is not at capacity. Create a new object.
@@ -546,10 +549,9 @@ public class GenericObjectPool<T> extends 
BaseGenericObjectPool<T>
                     create = Boolean.TRUE;
                 }
             }
-
-            // Do not block more if localMaxWaitDuration > 0.
-            if (create == null && 
localMaxWaitDuration.compareTo(Duration.ZERO) > 0 &&
-                    
durationSince(localStartInstant).compareTo(localMaxWaitDuration) >= 0) {
+            // Do not block more if remainingWaitDuration > 0.
+            if (create == null && 
remainingWaitDuration.compareTo(Duration.ZERO) > 0 &&
+                    
durationSince(localStartInstant).compareTo(remainingWaitDuration) >= 0) {
                 create = Boolean.FALSE;
             }
         }

Reply via email to