Skip to content

Commit 1a9e8b7

Browse files
committed
[POOL-418] The maximum wait time for GenericObjectPool.borrowObject(*)
may exceed expectations due to a spurious thread wakeup - Revisit this issue with 2 changes - The remaining duration was incorrectly calculated and the method did not end up waiting long enough - Recompute the remaining duration an additional time when we block when exhausted
1 parent 9f5b3ae commit 1a9e8b7

File tree

2 files changed

+17
-16
lines changed

2 files changed

+17
-16
lines changed

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ The <action> type attribute can be add,update,fix,remove.
4848
<release version="2.12.2" date="YYYY-MM-DD" description="This is a feature and maintenance release. Java 8 or later is required.">
4949
<!-- FIX -->
5050
<action type="fix" dev="ggregory" due-to="Gary Gregory">Remove -nouses directive from maven-bundle-plugin. OSGi package imports now state 'uses' definitions for package imports, this doesn't affect JPMS (from org.apache.commons:commons-parent:80).</action>
51+
<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>
5152
<!-- ADD -->
5253
<!-- UPDATE -->
5354
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 79 to 81.</action>

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ public T borrowObject() throws Exception {
246246
* <p>
247247
* If there are no idle instances available in the pool, behavior depends on
248248
* the {@link #getMaxTotal() maxTotal}, (if applicable)
249-
* {@link #getBlockWhenExhausted()} and the value passed in to the
250-
* {@code borrowMaxWaitMillis} parameter. If the number of instances
249+
* {@link #getBlockWhenExhausted()} and the value passed in the
250+
* {@code maxWaitDuration} parameter. If the number of instances
251251
* checked out from the pool is less than {@code maxTotal,} a new
252252
* instance is created, activated and (if applicable) validated and returned
253253
* to the caller. If validation fails, a {@code NoSuchElementException}
@@ -261,7 +261,7 @@ public T borrowObject() throws Exception {
261261
* {@code NoSuchElementException} (if
262262
* {@link #getBlockWhenExhausted()} is false). The length of time that this
263263
* method will block when {@link #getBlockWhenExhausted()} is true is
264-
* determined by the value passed in to the {@code borrowMaxWaitMillis}
264+
* determined by the value passed in to the {@code maxWaitDuration}
265265
* parameter.
266266
* </p>
267267
* <p>
@@ -271,17 +271,17 @@ public T borrowObject() throws Exception {
271271
* available instances in request arrival order.
272272
* </p>
273273
*
274-
* @param borrowMaxWaitDuration The time to wait for an object to become available, not null.
274+
* @param maxWaitDuration The time to wait for an object to become available, not null.
275275
* @return object instance from the pool
276276
* @throws NoSuchElementException if an instance cannot be returned
277277
* @throws Exception if an object instance cannot be returned due to an error
278278
* @since 2.10.0
279279
*/
280-
public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception {
280+
public T borrowObject(final Duration maxWaitDuration) throws Exception {
281281
assertOpen();
282282
final Instant startInstant = Instant.now();
283-
final boolean negativeDuration = borrowMaxWaitDuration.isNegative();
284-
Duration remainingWaitDuration = borrowMaxWaitDuration;
283+
final boolean negativeDuration = maxWaitDuration.isNegative();
284+
Duration remainingWaitDuration = maxWaitDuration;
285285
final AbandonedConfig ac = this.abandonedConfig;
286286
if (ac != null && ac.getRemoveAbandonedOnBorrow() && getNumIdle() < 2 && getNumActive() > getMaxTotal() - 3) {
287287
removeAbandoned(ac);
@@ -292,7 +292,7 @@ public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception {
292292
final boolean blockWhenExhausted = getBlockWhenExhausted();
293293
boolean create;
294294
while (p == null) {
295-
remainingWaitDuration = remainingWaitDuration.minus(durationSince(startInstant));
295+
remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant));
296296
create = false;
297297
p = idleObjects.pollFirst();
298298
if (p == null) {
@@ -303,11 +303,11 @@ public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception {
303303
}
304304
if (blockWhenExhausted) {
305305
if (PooledObject.isNull(p)) {
306+
remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant));
306307
p = negativeDuration ? idleObjects.takeFirst() : idleObjects.pollFirst(remainingWaitDuration);
307308
}
308309
if (PooledObject.isNull(p)) {
309-
throw new NoSuchElementException(appendStats(
310-
"Timeout waiting for idle object, borrowMaxWaitDuration=" + remainingWaitDuration));
310+
throw new NoSuchElementException(appendStats("Timeout waiting for idle object, maxWaitDuration=" + remainingWaitDuration));
311311
}
312312
} else if (PooledObject.isNull(p)) {
313313
throw new NoSuchElementException(appendStats("Pool exhausted"));
@@ -379,7 +379,7 @@ public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception {
379379
* If there are no idle instances available in the pool, behavior depends on
380380
* the {@link #getMaxTotal() maxTotal}, (if applicable)
381381
* {@link #getBlockWhenExhausted()} and the value passed in to the
382-
* {@code borrowMaxWaitMillis} parameter. If the number of instances
382+
* {@code maxWaitMillis} parameter. If the number of instances
383383
* checked out from the pool is less than {@code maxTotal,} a new
384384
* instance is created, activated and (if applicable) validated and returned
385385
* to the caller. If validation fails, a {@code NoSuchElementException}
@@ -393,7 +393,7 @@ public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception {
393393
* {@code NoSuchElementException} (if
394394
* {@link #getBlockWhenExhausted()} is false). The length of time that this
395395
* method will block when {@link #getBlockWhenExhausted()} is true is
396-
* determined by the value passed in to the {@code borrowMaxWaitMillis}
396+
* determined by the value passed in to the {@code maxWaitMillis}
397397
* parameter.
398398
* </p>
399399
* <p>
@@ -403,15 +403,15 @@ public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception {
403403
* available instances in request arrival order.
404404
* </p>
405405
*
406-
* @param borrowMaxWaitMillis The time to wait in milliseconds for an object
406+
* @param maxWaitMillis The time to wait in milliseconds for an object
407407
* to become available
408408
* @return object instance from the pool
409409
* @throws NoSuchElementException if an instance cannot be returned
410410
* @throws Exception if an object instance cannot be returned due to an
411411
* error
412412
*/
413-
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
414-
return borrowObject(Duration.ofMillis(borrowMaxWaitMillis));
413+
public T borrowObject(final long maxWaitMillis) throws Exception {
414+
return borrowObject(Duration.ofMillis(maxWaitMillis));
415415
}
416416

417417
/**
@@ -512,7 +512,7 @@ private PooledObject<T> create(final Duration maxWaitDuration) throws Exception
512512
Boolean create = null;
513513
while (create == null) {
514514
// remainingWaitDuration handles spurious wakeup from wait().
515-
remainingWaitDuration = remainingWaitDuration.minus(durationSince(startInstant));
515+
remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant));
516516
synchronized (makeObjectCountLock) {
517517
final long newCreateCount = createCount.incrementAndGet();
518518
if (newCreateCount > localMaxTotal) {

0 commit comments

Comments
 (0)