Skip to content

Commit 9a5664d

Browse files
committed
The maximum wait time for GenericObjectPool.borrowObject(*) may exceed
expectations due to a spurious thread wakeup
1 parent e973e08 commit 9a5664d

File tree

2 files changed

+12
-9
lines changed

2 files changed

+12
-9
lines changed

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ The <action> type attribute can be add,update,fix,remove.
4949
<!-- FIX -->
5050
<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>
5151
<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>
52+
<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>
5253
<!-- ADD -->
5354
<!-- UPDATE -->
5455
<action type="update" dev="ggregory">Bump org.apache.commons:commons-parent from 62 to 73.</action>

src/main/java/org/apache/commons/pool2/impl/GenericObjectPool.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
8787
"org.apache.commons.pool2:type=GenericObjectPool,name=";
8888

8989
private static void wait(final Object obj, final Duration duration) throws InterruptedException {
90-
obj.wait(duration.toMillis(), duration.getNano() % 1_000_000);
90+
if (!duration.isNegative()) {
91+
obj.wait(duration.toMillis(), duration.getNano() % 1_000_000);
92+
}
9193
}
9294

9395
private volatile String factoryType;
@@ -506,22 +508,23 @@ public void close() {
506508
* @throws Exception if the object factory's {@code makeObject} fails
507509
*/
508510
private PooledObject<T> create(final Duration maxWaitDuration) throws Exception {
511+
final Instant startInstant = Instant.now();
512+
Duration remainingWaitDuration = maxWaitDuration.isNegative() ? Duration.ZERO : maxWaitDuration;
509513
int localMaxTotal = getMaxTotal();
510514
// This simplifies the code later in this method
511515
if (localMaxTotal < 0) {
512516
localMaxTotal = Integer.MAX_VALUE;
513517
}
514-
515518
final Instant localStartInstant = Instant.now();
516-
final Duration localMaxWaitDuration = maxWaitDuration.isNegative() ? Duration.ZERO : maxWaitDuration;
517-
518519
// Flag that indicates if create should:
519520
// - TRUE: call the factory to create an object
520521
// - FALSE: return null
521522
// - null: loop and re-test the condition that determines whether to
522523
// call the factory
523524
Boolean create = null;
524525
while (create == null) {
526+
// remainingWaitDuration handles spurious wakeup from wait().
527+
remainingWaitDuration = remainingWaitDuration.minus(durationSince(startInstant));
525528
synchronized (makeObjectCountLock) {
526529
final long newCreateCount = createCount.incrementAndGet();
527530
if (newCreateCount > localMaxTotal) {
@@ -538,18 +541,17 @@ private PooledObject<T> create(final Duration maxWaitDuration) throws Exception
538541
// bring the pool to capacity. Those calls might also
539542
// fail so wait until they complete and then re-test if
540543
// the pool is at capacity or not.
541-
wait(makeObjectCountLock, localMaxWaitDuration);
544+
wait(makeObjectCountLock, remainingWaitDuration);
542545
}
543546
} else {
544547
// The pool is not at capacity. Create a new object.
545548
makeObjectCount++;
546549
create = Boolean.TRUE;
547550
}
548551
}
549-
550-
// Do not block more if localMaxWaitDuration > 0.
551-
if (create == null && localMaxWaitDuration.compareTo(Duration.ZERO) > 0 &&
552-
durationSince(localStartInstant).compareTo(localMaxWaitDuration) >= 0) {
552+
// Do not block more if remainingWaitDuration > 0.
553+
if (create == null && remainingWaitDuration.compareTo(Duration.ZERO) > 0 &&
554+
durationSince(localStartInstant).compareTo(remainingWaitDuration) >= 0) {
553555
create = Boolean.FALSE;
554556
}
555557
}

0 commit comments

Comments
 (0)