2626 */
2727package org .apache .hc .core5 .pool ;
2828
29+ import java .time .Clock ;
2930import java .util .Deque ;
3031import java .util .HashSet ;
3132import java .util .Iterator ;
@@ -78,6 +79,8 @@ public class LaxConnPool<T, C extends ModalCloseable> implements ManagedConnPool
7879 private final ConcurrentMap <T , PerRoutePool <T , C >> routeToPool ;
7980 private final AtomicBoolean isShutDown ;
8081
82+ private final Clock clock ;
83+
8184 private volatile int defaultMaxPerRoute ;
8285
8386 /**
@@ -89,6 +92,16 @@ public LaxConnPool(
8992 final PoolReusePolicy policy ,
9093 final DisposalCallback <C > disposalCallback ,
9194 final ConnPoolListener <T > connPoolListener ) {
95+ this (defaultMaxPerRoute , timeToLive , policy , disposalCallback , connPoolListener , Clock .systemUTC ());
96+ }
97+
98+ LaxConnPool (
99+ final int defaultMaxPerRoute ,
100+ final TimeValue timeToLive ,
101+ final PoolReusePolicy policy ,
102+ final DisposalCallback <C > disposalCallback ,
103+ final ConnPoolListener <T > connPoolListener ,
104+ final Clock clock ) {
92105 super ();
93106 Args .positive (defaultMaxPerRoute , "Max per route value" );
94107 this .timeToLive = TimeValue .defaultsToNegativeOneMillisecond (timeToLive );
@@ -98,6 +111,7 @@ public LaxConnPool(
98111 this .routeToPool = new ConcurrentHashMap <>();
99112 this .isShutDown = new AtomicBoolean ();
100113 this .defaultMaxPerRoute = defaultMaxPerRoute ;
114+ this .clock = Args .notNull (clock , "clock" );
101115 }
102116
103117 /**
@@ -145,7 +159,8 @@ private PerRoutePool<T, C> getPool(final T route) {
145159 policy ,
146160 this ,
147161 disposalCallback ,
148- connPoolListener );
162+ connPoolListener ,
163+ clock );
149164 routePool = routeToPool .putIfAbsent (route , newRoutePool );
150165 if (routePool == null ) {
151166 routePool = newRoutePool ;
@@ -266,7 +281,7 @@ public void enumLeased(final Callback<PoolEntry<T, C>> callback) {
266281
267282 @ Override
268283 public void closeIdle (final TimeValue idleTime ) {
269- final long deadline = System . currentTimeMillis () - (TimeValue .isPositive (idleTime ) ? idleTime .toMilliseconds () : 0 );
284+ final long deadline = clock . millis () - (TimeValue .isPositive (idleTime ) ? idleTime .toMilliseconds () : 0 );
270285 enumAvailable (entry -> {
271286 if (entry .getUpdated () <= deadline ) {
272287 entry .discardConnection (CloseMode .GRACEFUL );
@@ -276,7 +291,7 @@ public void closeIdle(final TimeValue idleTime) {
276291
277292 @ Override
278293 public void closeExpired () {
279- final long now = System . currentTimeMillis ();
294+ final long now = clock . millis ();
280295 enumAvailable (entry -> {
281296 if (entry .getExpiryDeadline ().isBefore (now )) {
282297 entry .discardConnection (CloseMode .GRACEFUL );
@@ -306,11 +321,11 @@ static class LeaseRequest<T, C extends ModalCloseable> implements Cancellable {
306321
307322 LeaseRequest (
308323 final Object state ,
309- final Timeout requestTimeout ,
324+ final Deadline deadline ,
310325 final BasicFuture <PoolEntry <T , C >> future ) {
311326 super ();
312327 this .state = state ;
313- this .deadline = Deadline . calculate ( requestTimeout ) ;
328+ this .deadline = deadline ;
314329 this .future = future ;
315330 }
316331
@@ -362,6 +377,8 @@ private enum RequestServiceStrategy { FIRST_SUCCESSFUL, ALL }
362377 private final AtomicInteger allocated ;
363378 private final AtomicLong releaseSeqNum ;
364379
380+ private final Clock clock ;
381+
365382 private volatile int max ;
366383
367384 PerRoutePool (
@@ -371,7 +388,8 @@ private enum RequestServiceStrategy { FIRST_SUCCESSFUL, ALL }
371388 final PoolReusePolicy policy ,
372389 final ConnPoolStats <T > connPoolStats ,
373390 final DisposalCallback <C > disposalCallback ,
374- final ConnPoolListener <T > connPoolListener ) {
391+ final ConnPoolListener <T > connPoolListener ,
392+ final Clock clock ) {
375393 super ();
376394 this .route = route ;
377395 this .timeToLive = timeToLive ;
@@ -386,6 +404,7 @@ private enum RequestServiceStrategy { FIRST_SUCCESSFUL, ALL }
386404 this .allocated = new AtomicInteger (0 );
387405 this .releaseSeqNum = new AtomicLong (0 );
388406 this .max = max ;
407+ this .clock = Args .notNull (clock , "clock" );
389408 }
390409
391410 public void shutdown (final CloseMode closeMode ) {
@@ -412,7 +431,7 @@ private PoolEntry<T, C> createPoolEntry() {
412431 prev = allocated .get ();
413432 next = (prev < poolMax ) ? prev + 1 : prev ;
414433 } while (!allocated .compareAndSet (prev , next ));
415- return prev < next ? new PoolEntry <>(route , timeToLive , disposalCallback ) : null ;
434+ return prev < next ? new PoolEntry <>(route , timeToLive , disposalCallback , clock ) : null ;
416435 }
417436
418437 private void deallocatePoolEntry () {
@@ -437,12 +456,13 @@ private void removeLeased(final PoolEntry<T, C> entry) {
437456 }
438457
439458 private PoolEntry <T , C > getAvailableEntry (final Object state ) {
459+ final long now = clock .millis ();
440460 for (final Iterator <AtomicMarkableReference <PoolEntry <T , C >>> it = available .iterator (); it .hasNext (); ) {
441461 final AtomicMarkableReference <PoolEntry <T , C >> ref = it .next ();
442462 final PoolEntry <T , C > entry = ref .getReference ();
443463 if (ref .compareAndSet (entry , entry , false , true )) {
444464 it .remove ();
445- if (entry .getExpiryDeadline ().isExpired ( ) || !Objects .equals (entry .getState (), state )) {
465+ if (entry .getExpiryDeadline ().isBefore ( now ) || !Objects .equals (entry .getState (), state )) {
446466 entry .discardConnection (CloseMode .GRACEFUL );
447467 deallocatePoolEntry ();
448468 } else {
@@ -486,7 +506,7 @@ public PoolEntry<T, C> get(
486506 addLeased (entry );
487507 future .completed (entry );
488508 } else {
489- pending .add (new LeaseRequest <>(state , requestTimeout , future ));
509+ pending .add (new LeaseRequest <>(state , Deadline . calculate ( clock . millis (), requestTimeout ) , future ));
490510 if (releaseState != releaseSeqNum .get ()) {
491511 servicePendingRequest ();
492512 }
@@ -496,7 +516,8 @@ public PoolEntry<T, C> get(
496516
497517 public void release (final PoolEntry <T , C > releasedEntry , final boolean reusable ) {
498518 removeLeased (releasedEntry );
499- if (!reusable || releasedEntry .getExpiryDeadline ().isExpired ()) {
519+ final long now = clock .millis ();
520+ if (!reusable || releasedEntry .getExpiryDeadline ().isBefore (now )) {
500521 releasedEntry .discardConnection (CloseMode .GRACEFUL );
501522 }
502523 if (releasedEntry .hasConnection ()) {
@@ -531,8 +552,9 @@ private void servicePendingRequests(final RequestServiceStrategy serviceStrategy
531552 }
532553 final Object state = leaseRequest .getState ();
533554 final Deadline deadline = leaseRequest .getDeadline ();
555+ final long now = clock .millis ();
534556
535- if (deadline .isExpired ( )) {
557+ if (deadline .isBefore ( now )) {
536558 leaseRequest .failed (DeadlineTimeoutException .from (deadline ));
537559 } else {
538560 final long releaseState = releaseSeqNum .get ();
@@ -560,6 +582,7 @@ private void servicePendingRequests(final RequestServiceStrategy serviceStrategy
560582 }
561583
562584 public void validatePendingRequests () {
585+ final long now = clock .millis ();
563586 final Iterator <LeaseRequest <T , C >> it = pending .iterator ();
564587 while (it .hasNext ()) {
565588 final LeaseRequest <T , C > request = it .next ();
@@ -568,7 +591,7 @@ public void validatePendingRequests() {
568591 it .remove ();
569592 } else {
570593 final Deadline deadline = request .getDeadline ();
571- if (deadline .isExpired ( )) {
594+ if (deadline .isBefore ( now )) {
572595 request .failed (DeadlineTimeoutException .from (deadline ));
573596 }
574597 if (request .isDone ()) {
0 commit comments