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
The following commit(s) were added to refs/heads/master by this push:
new 466c7e51 The maximum wait time for GenericObjectPool.borrowObject(*)
may exceed expectations due to a spurious thread wakeup
466c7e51 is described below
commit 466c7e51fb621cbf5deb67082af09b07d1120d5f
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Nov 24 18:44:07 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/pool3/impl/GenericObjectPool.java | 20 +++++++++++---------
.../commons/pool3/impl/TestGenericObjectPool.java | 4 ++--
3 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1930f822..5933e84d 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -68,6 +68,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/pool3/impl/GenericObjectPool.java
b/src/main/java/org/apache/commons/pool3/impl/GenericObjectPool.java
index 04d7f1b3..7d957177 100644
--- a/src/main/java/org/apache/commons/pool3/impl/GenericObjectPool.java
+++ b/src/main/java/org/apache/commons/pool3/impl/GenericObjectPool.java
@@ -88,7 +88,9 @@ public class GenericObjectPool<T, E extends Exception>
extends BaseGenericObject
"org.apache.commons.pool3: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;
@@ -509,15 +511,14 @@ public class GenericObjectPool<T, E extends Exception>
extends BaseGenericObject
* @throws E if the object factory's {@code makeObject} fails
*/
private PooledObject<T> create(final Duration maxWaitDuration) throws E {
+ 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
@@ -525,6 +526,8 @@ public class GenericObjectPool<T, E extends Exception>
extends BaseGenericObject
// 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) {
@@ -542,7 +545,7 @@ public class GenericObjectPool<T, E extends Exception>
extends BaseGenericObject
// fail so wait until they complete and then re-test if
// the pool is at capacity or not.
try {
- wait(makeObjectCountLock, localMaxWaitDuration);
+ wait(makeObjectCountLock, remainingWaitDuration);
} catch (final InterruptedException e) {
// Don't surface exception type of internal
locking mechanism.
throw cast(e);
@@ -554,10 +557,9 @@ public class GenericObjectPool<T, E extends Exception>
extends BaseGenericObject
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;
}
}
diff --git
a/src/test/java/org/apache/commons/pool3/impl/TestGenericObjectPool.java
b/src/test/java/org/apache/commons/pool3/impl/TestGenericObjectPool.java
index 6354d5b1..184a6db4 100644
--- a/src/test/java/org/apache/commons/pool3/impl/TestGenericObjectPool.java
+++ b/src/test/java/org/apache/commons/pool3/impl/TestGenericObjectPool.java
@@ -1941,8 +1941,8 @@ public class TestGenericObjectPool extends
TestBaseObjectPool {
assertFalse(thread1.isAlive());
assertFalse(thread2.isAlive());
- assertTrue(thread1.thrown instanceof UnsupportedCharsetException);
- assertTrue(thread2.thrown instanceof UnsupportedCharsetException);
+ assertTrue(thread1.thrown instanceof UnsupportedCharsetException,
() -> Objects.toString(thread1.thrown));
+ assertTrue(thread2.thrown instanceof UnsupportedCharsetException,
() -> Objects.toString(thread2.thrown));
}
}